Commit 14152815 authored by Him188's avatar Him188

Add more details for Profile

parent ba4f16a2
......@@ -93,6 +93,7 @@ kotlin {
}
sourceSets["jvmTest"].apply {
kotlin.outputDir = file("build/classes/kotlin/jvm/test")
kotlin.setSrcDirs(listOf("src/$name/kotlin"))
}
......
......@@ -193,19 +193,44 @@ enum class MemberPermission {
*/
// FIXME: 2019/11/8 should be `data class Profile`
@Suppress("PropertyName")
class Profile(qq: UInt, nickname: String, zipCode: String?, phone: String?, gender: Gender, var birthday: Date?) {
internal var _qq: UInt = qq
internal var _nickname: String = nickname
internal var _zipCode: String? = zipCode
internal var _phone: String? = phone
internal var _gender: Gender = gender
val qq: UInt = _qq
val nickname: String = _nickname
val zipCode: String? = _zipCode
val phone: String? = _phone
val gender: Gender = _gender
class Profile(
internal var _qq: UInt,
internal var _nickname: String,
internal var _zipCode: String?,
internal var _phone: String?,
internal var _gender: Gender,
internal var _birthday: Date?,
internal var _personalStatus: String?,
internal var _school: String?,
internal var _homepage: String?,
internal var _email: String?,
internal var _company: String?
) {
val qq: UInt get() = _qq
val nickname: String get() = _nickname
val zipCode: String? get() = _zipCode
val phone: String? get() = _phone
val gender: Gender get() = _gender
/**
* 个性签名
*/
val personalStatus: String? get() = _personalStatus
val school: String? get() = _school
val company: String? get() = _company
/**
* 主页
*/
val homepage: String? get() = _homepage
val email: String? get() = _email
val birthday: Date? get() = _birthday
override fun toString(): String = "Profile(" +
"qq=$qq, nickname=$nickname, zipCode=$zipCode, phone=$phone, " +
"gender=$gender, birthday=$birthday, personalStatus=$personalStatus, school=$school, " +
"homepage=$homepage, email=$email, company=$company" +
")"
}
fun Profile.copyFrom(another: Profile) {
......@@ -214,6 +239,12 @@ fun Profile.copyFrom(another: Profile) {
this._zipCode = another.zipCode
this._phone = another.phone
this._gender = another.gender
this._birthday = another.birthday
this._personalStatus = another.personalStatus
this._school = another.school
this._homepage = another.homepage
this._email = another.email
this._company = another.company
}
/**
......
......@@ -21,4 +21,6 @@ class UnknownPacket(val id: PacketId, val body: ByteReadPacket) : Packet {
/**
* 仅用于替换类型应为 [Unit] 的情况
*/
object NoPacket : Packet
\ No newline at end of file
object NoPacket : Packet {
override fun toString(): String = "NoPacket"
}
\ No newline at end of file
......@@ -75,8 +75,8 @@ enum class KnownPacketId(override inline val value: UShort, override inline val
inline GROUP_IMAGE_ID(0x0388u, GroupImageIdRequestPacket),
inline FRIEND_IMAGE_ID(0x0352u, FriendImageIdRequestPacket),
inline REQUEST_PROFILE_AVATAR(0x0031u, RequestProfilePicturePacket),
inline REQUEST_PROFILE_DETAILS(0x003Cu, RequestProfilePicturePacket),
inline REQUEST_PROFILE_AVATAR(0x0031u, RequestProfileAvatarPacket),
inline REQUEST_PROFILE_DETAILS(0x003Cu, RequestProfileDetailsPacket),
// @Suppress("DEPRECATION")
// inline SUBMIT_IMAGE_FILE_NAME(0x01BDu, SubmitImageFilenamePacket),
......
......@@ -104,8 +104,11 @@ object CanAddFriendPacket : SessionPacketFactory<CanAddFriendResponse>() {
}
val qq: QQ = readUInt().qq()
return when (val state = readUByte().toUInt()) {
//09 4E A4 B1 00 03
0x00u -> CanAddFriendResponse.ReadyToAdd(qq)
0x01u -> CanAddFriendResponse.RequireVerification(qq)
0x99u -> CanAddFriendResponse.AlreadyAdded(qq)
......
......@@ -61,18 +61,23 @@ object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsR
discardExact(6)
val map = readTLVMap(tagSize = 2, expectingEOF = true)
val profile = Profile(
qq = qq,
nickname = (map[0x4E22u] ?: error("Cannot determine nickname")).stringOfWitch(),
zipCode = map[0x4E25u]?.stringOfWitch(),
phone = map[0x4E27u]?.stringOfWitch(),
gender = when (map[0x4E29u]?.let { it[0] }?.toUInt()) {
null -> error("Cannot determine gender, entry 0x4E29u not found")
_qq = qq,
_nickname = map[0x4E22u]?.stringOfWitch() ?: "",//error("Cannot determine nickname")
_zipCode = map[0x4E25u]?.stringOfWitch(),
_phone = map[0x4E27u]?.stringOfWitch(),
_gender = when (map[0x4E29u]?.let { it[0] }?.toUInt()) {
null -> Gender.SECRET //error("Cannot determine gender, entry 0x4E29u not found")
0x02u -> Gender.FEMALE
0x01u -> Gender.MALE
else -> Gender.SECRET // 猜的
//else -> error("Cannot determine gender, bad value of 0x4E29u: ${map[0x4729u]!![0].toUHexString()}")
},
birthday = map[0x4E3Fu]?.let { Date(it.toUInt().toInt()) }
_birthday = map[0x4E3Fu]?.let { Date(it.toUInt().toInt()) },
_personalStatus = map[0x4E33u]?.stringOfWitch(),
_homepage = map[0x4E2Du]?.stringOfWitch(),
_company = map[0x5DC8u]?.stringOfWitch(),
_school = map[0x4E35u]?.stringOfWitch(),
_email = map[0x4E2Bu]?.stringOfWitch()
)
map.clear()
......@@ -141,21 +146,4 @@ data class RequestProfileDetailsResponse(
00 01 00 76 E4 B8 DD 00 00 00 00 00 29
4E 22 00 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 4E 25 00 06 34 33 33 31 30 30 4E 26 00 09 E4 B8 8D E7 9F A5 E9 81 93 4E 27 00 0A 31 33 38 2A 2A 2A 2A 2A 2A 2A 4E 29 00 01 01 4E 2A 00 00 4E 2B 00 00 4E 2D 00 23 68 74 74 70 3A 2F 2F 77 77 77 2E 34 33 39 39 2E 63 6F 6D 2F 66 6C 61 73 68 2F 33 32 39 37 39 2E 68 74 6D 4E 2E 00 02 31 00 4E 2F 00 04 36 30 33 00 4E 30 00 00 4E 31 00 01 00 4E 33 00 00 4E 35 00 00 4E 36 00 01 0A 4E 37 00 01 06 4E 38 00 01 00 4E 3F 00 04 07 DD 0B 13 4E 40 00 0C 00 41 42 57 00 00 00 00 00 00 00 00 4E 41 00 02 08 04 4E 42 00 02 00 00 4E 43 00 02 0C 04 4E 45 00 01 05 4E 49 00 04 00 00 00 00 4E 4B 00 04 00 00 00 00 4E 4F 00 01 06 4E 54 00 00 4E 5B 00 04 00 00 00 00 52 0B 00 04 13 80 02 00 52 0F 00 14 01 00 00 00 00 00 00 00 52 00 40 48 89 50 80 02 00 00 03 00 5D C2 00 0C 00 41 42 57 00 00 00 00 00 00 00 00 5D C8 00 00 65 97 00 01 07 69 9D 00 04 00 00 00 00 69 A9 00 00 9D A5 00 02 00 01 A4 91 00 02 00 00 A4 93 00 02 00 01 A4 94 00 02 00 00 A4 9C 00 02 00 00 A4 B5 00 02 00 00
*/
}
fun main() {
val mapFemale =
"4E 22 00 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 4E 25 00 06 34 33 33 31 30 30 4E 26 00 09 E4 B8 8D E7 9F A5 E9 81 93 4E 27 00 0A 31 33 38 2A 2A 2A 2A 2A 2A 2A 4E 29 00 01 02 4E 2A 00 00 4E 2B 00 00 4E 2D 00 23 68 74 74 70 3A 2F 2F 77 77 77 2E 34 33 39 39 2E 63 6F 6D 2F 66 6C 61 73 68 2F 33 32 39 37 39 2E 68 74 6D 4E 2E 00 02 31 00 4E 2F 00 04 36 30 33 00 4E 30 00 00 4E 31 00 01 00 4E 33 00 00 4E 35 00 00 4E 36 00 01 0A 4E 37 00 01 06 4E 38 00 01 00 4E 3F 00 04 07 DD 0B 13 4E 40 00 0C 00 41 42 57 00 00 00 00 00 00 00 00 4E 41 00 02 08 04 4E 42 00 02 00 00 4E 43 00 02 0C 04 4E 45 00 01 05 4E 49 00 04 00 00 00 00 4E 4B 00 04 00 00 00 00 4E 4F 00 01 06 4E 54 00 00 4E 5B 00 04 00 00 00 00 52 0B 00 04 13 80 02 00 52 0F 00 14 01 00 00 00 00 00 00 00 52 00 40 48 89 50 80 02 00 00 03 00 5D C2 00 0C 00 41 42 57 00 00 00 00 00 00 00 00 5D C8 00 00 65 97 00 01 07 69 9D 00 04 00 00 00 00 69 A9 00 00 9D A5 00 02 00 01 A4 91 00 02 00 00 A4 93 00 02 00 01 A4 94 00 02 00 00 A4 9C 00 02 00 00 A4 B5 00 02 00 00".hexToBytes()
.read {
readTLVMap(tagSize = 2, expectingEOF = true)
}
val mapMale =
"4E 22 00 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 4E 25 00 06 34 33 33 31 30 30 4E 26 00 09 E4 B8 8D E7 9F A5 E9 81 93 4E 27 00 0A 31 33 38 2A 2A 2A 2A 2A 2A 2A 4E 29 00 01 01 4E 2A 00 00 4E 2B 00 00 4E 2D 00 23 68 74 74 70 3A 2F 2F 77 77 77 2E 34 33 39 39 2E 63 6F 6D 2F 66 6C 61 73 68 2F 33 32 39 37 39 2E 68 74 6D 4E 2E 00 02 31 00 4E 2F 00 04 36 30 33 00 4E 30 00 00 4E 31 00 01 00 4E 33 00 00 4E 35 00 00 4E 36 00 01 0A 4E 37 00 01 06 4E 38 00 01 00 4E 3F 00 04 07 DD 0B 13 4E 40 00 0C 00 41 42 57 00 00 00 00 00 00 00 00 4E 41 00 02 08 04 4E 42 00 02 00 00 4E 43 00 02 0C 04 4E 45 00 01 05 4E 49 00 04 00 00 00 00 4E 4B 00 04 00 00 00 00 4E 4F 00 01 06 4E 54 00 00 4E 5B 00 04 00 00 00 00 52 0B 00 04 13 80 02 00 52 0F 00 14 01 00 00 00 00 00 00 00 52 00 40 48 89 50 80 02 00 00 03 00 5D C2 00 0C 00 41 42 57 00 00 00 00 00 00 00 00 5D C8 00 00 65 97 00 01 07 69 9D 00 04 00 00 00 00 69 A9 00 00 9D A5 00 02 00 01 A4 91 00 02 00 00 A4 93 00 02 00 01 A4 94 00 02 00 00 A4 9C 00 02 00 00 A4 B5 00 02 00 00".hexToBytes()
.read {
readTLVMap(tagSize = 2, expectingEOF = true)
}
mapFemale.filter { (key, value) -> !mapMale.containsKey(key) || !mapMale[key]!!.contentEquals(value) }.forEach {
println("id=" + it.key.toUShort().toUHexString() + ", valueFemale=" + it.value.toUHexString() + ",valueMale=" + mapMale[it.key]!!.toUHexString())
}
}
\ No newline at end of file
......@@ -65,7 +65,9 @@ object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Re
}
@NoLog
object Response : Packet
object Response : Packet {
override fun toString(): String = "SendFriendMessagePacket.Response"
}
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): Response = Response
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_UNSIGNED_LITERALS")
import PacketDebugger.dataSent
import PacketDebugger.localIp
import PacketDebugger.qq
import PacketDebugger.sessionKey
import kotlinx.coroutines.*
......@@ -15,8 +14,6 @@ import net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocketAdapter
import net.mamoe.mirai.network.protocol.tim.handler.PacketHandler
import net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.event.EventPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.UnknownEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.CaptchaKey
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.protocol.tim.packet.login.ShareKey
......@@ -48,8 +45,11 @@ fun main() {
JpcapCaptor.openDevice(JpcapCaptor.getDeviceList()[0], 65535, true, 1000).loopPacket(Int.MAX_VALUE) {
println(it)
}*/
val localIp = Pcaps.findAllDevs()[0].addresses[0].address.hostAddress
println("LocalIp = $localIp")
Pcaps.findAllDevs().forEach {
listenDevice(it)
listenDevice(localIp, it)
}
}
......@@ -58,7 +58,7 @@ fun main() {
*/
val DISPATCHER = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
private fun listenDevice(device: PcapNetworkInterface) {
private fun listenDevice(localIp: String, device: PcapNetworkInterface) {
val sender = device.openLive(65536, PromiscuousMode.PROMISCUOUS, 10)
thread {
sender.setFilter("src $localIp && udp port 8000", BpfCompileMode.OPTIMIZE)
......@@ -104,7 +104,7 @@ private fun listenDevice(device: PcapNetworkInterface) {
/**
* 抓包分析器.
* 设置好 [sessionKey], [localIp] 和 [qq] 后运行即可开始抓包和自动解密
* 设置好 [sessionKey], 和 [qq] 后运行即可开始抓包和自动解密
*
* @author Him188moe
*/
......@@ -123,9 +123,8 @@ object PacketDebugger {
* 7. 运行完 `mov eax,dword ptr ss:[ebp+10]`
* 8. 查看内存, `eax` 到 `eax+10` 的 16 字节就是 `sessionKey`
*/
val sessionKey: SessionKey = SessionKey("9B AA 9C 93 78 47 7B 6F C4 57 F2 13 76 AC C7 72".hexToBytes())
val sessionKey: SessionKey = SessionKey("7F 32 DB 6D 24 73 13 06 DF 11 25 CB B0 07 FF F3".hexToBytes())
const val qq: UInt = 1040400290u
val localIp: String = "10.162.12.231".also { println("Local IP: $it") }
val IgnoredPacketIdList: List<PacketId> = listOf(
KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE,
......@@ -160,10 +159,12 @@ object PacketDebugger {
with(id.factory) {
provideDecrypter(id.factory)
.decrypt(this@read.readRemainingBytes().let { ByteReadPacket(it, 0, it.size - 1) })
.let {
it.debugPrint(" 解密body", it.remaining)
}
.decode(id, sequenceId, DebugNetworkHandler)
}
}
println(" 解析body=$packet")
handlePacket(id, sequenceId, packet, id.factory)
} catch (e: DecryptionFailedException) {
......@@ -173,6 +174,15 @@ object PacketDebugger {
}
}
internal fun ByteReadPacket.debugPrint(name: String = "", length: Long = this.remaining): ByteReadPacket {
val bytes = this.readBytes(length.toInt())
println("$name=" + bytes.toUHexString())
return bytes.toReadPacket()
}
/**
* 提供解密密匙. 无需修改
*/
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
internal fun <D : Decrypter> provideDecrypter(factory: PacketFactory<*, D>): D =
when (factory.decrypterType) {
......@@ -187,6 +197,9 @@ object PacketDebugger {
else -> error("No decrypter is found")
} as? D ?: error("Internal error: could not cast decrypter which is found for factory to class Decrypter")
/**
* 处理一个包
*/
@Suppress("UNUSED_PARAMETER")
fun <TPacket : Packet> handlePacket(
id: PacketId,
......@@ -194,28 +207,8 @@ object PacketDebugger {
packet: TPacket,
factory: PacketFactory<TPacket, *>
) {
println(" 解析body=$packet")
return
when (packet) {
is UnknownEventPacket -> {
println("--------------")
println("未知事件ID=$id")
println("未知事件: $packet")
}
is UnknownPacket -> {
println("--------------")
println("未知包ID=$id")
println("未知包: $packet")
}
is EventPacket -> {
println("事件")
println(packet)
}
else -> {
}
}
}
fun dataSent(rawPacket: ByteArray) = rawPacket.cutTail(1).read {
......@@ -230,7 +223,7 @@ object PacketDebugger {
discardExact(3)//head
val id = matchPacketId(readUShort())
val sequence = readUShort()
val sequence = readUShort().toUHexString()
if (IgnoredPacketIdList.contains(id)) {
return
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment