From a75843b6789db56be612898796a1f252ac627ffa Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 16 Jan 2020 22:05:44 +0800 Subject: [PATCH] Daily QQA update: TLVMap enhancement, A1 parsing, OutgoingPacket, Tickets, D2 key for decrypting --- .../qqandroid/network/QQAndroidClient.kt | 4 +- .../mamoe/mirai/qqandroid/network/Ticket.kt | 30 +++++++++++++ .../protocol/packet/OutgoingPacketAndroid.kt | 45 ++++++++++++++++++- .../protocol/packet/login/LoginPacket.kt | 41 ++++++++++++----- .../net.mamoe.mirai/utils/io/InputUtils.kt | 10 ++--- 5 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/Ticket.kt diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt index b295c20ca..788b760cb 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt @@ -181,8 +181,8 @@ class LoginExtraData( class WLoginSigInfo( val uin: Long, - val encryptA1: ByteArray, // sigInfo[0] - val noPicSig: ByteArray, // sigInfo[1] + val encryptA1: ByteArray?, // sigInfo[0] + val noPicSig: ByteArray?, // sigInfo[1] val G: ByteArray, // sigInfo[2] val dpwd: ByteArray, val randSeed: ByteArray, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/Ticket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/Ticket.kt new file mode 100644 index 000000000..282f4ed57 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/Ticket.kt @@ -0,0 +1,30 @@ +package net.mamoe.mirai.qqandroid.network + +class Ticket( + val id: Int, + val data: ByteArray, + val key: ByteArray?, + val creationTime: Long, + val expireTime: Long +) { + companion object { + const val USER_A5 = 0x2 + const val AQ_SIG = 0x200000 + const val USER_SIG_64 = 0x2000 + const val SUPER_KEY = 0x100000 + const val OPEN_KEY = 0x4000 + const val ACCESS_TOKEN = 0x8000 + const val USER_ST_SIG = 0x80 + const val USER_A8 = 0x10 + const val LS_KEY = 0x200 + const val S_KEY = 0x1000 + const val V_KEY = 0x20000 + const val TGT = 0x40 + const val D2 = 0x40000 + const val SID = 0x80000 + const val USER_ST_WEB_SIG = 0x20 + const val PAY_TOKEN = 0x800000 + const val PF = 0x1000000 + const val DA2 = 0x2000000 + } +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt index d4176d54c..b1c467364 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt @@ -10,7 +10,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.cryptor.DecrypterByteArray import net.mamoe.mirai.utils.cryptor.encryptAndWrite -import net.mamoe.mirai.utils.io.* +import net.mamoe.mirai.utils.io.encryptAndWrite +import net.mamoe.mirai.utils.io.writeHex +import net.mamoe.mirai.utils.io.writeIntLVPacket +import net.mamoe.mirai.utils.io.writeQQ /** * 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket]. @@ -31,6 +34,45 @@ internal class OutgoingPacket constructor( private val KEY_16_ZEROS = ByteArray(16) private val EMPTY_BYTE_ARRAY = ByteArray(0) +/** + * 最外层的包. 结构适用于登录之后的过程. + * + * 在 QQ 中这个被以 JNI 实现: + * com.tencent.qphone.base.util.CodecWarpper#encodeRequest(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int, int, java.lang.String, byte, byte, byte, byte[], byte[], boolean) + * + * **Packet structure** + * int remaining.length + 4 + * int 0x0B + * byte 0x01 + * int sequenceId + * byte 0 + * int uinAccount.length + 4 + * byte[] uinAccount + * + * byte[] body encrypted by 16 zero + */ +/* + * 00 00 02 34 // remaining.length + 4 + * 00 00 00 0B + * 01 + * 00 01 4E 73 // sequence + * 00 + * 00 00 00 0E + * 31 39 39 34 37 30 31 30 32 31 + * 18 5D 8F 17 7D 67 71 61 FE DB 30 A4 4D 16 DD 0E 8D 84 0A F2 44 BE FB BB 11 BB B4 AC 79 50 50 9F 4C 99 CC 77 0B AA B6 E0 06 0C F7 91 79 99 57 31 3D EF 38 92 2C C8 81 33 79 83 FF C6 2F BA 18 2A 33 F8 D9 4E CD 62 07 D8 08 B7 1A 1E C7 EB AC AB B4 1E C9 9D A9 15 9C 29 29 2A 99 F6 BB D0 43 65 D6 5E 9C 93 A8 8D 17 08 5B 6A 29 92 58 6A 75 C9 B5 45 B3 0E A5 D3 52 8F 9A A4 88 36 A0 14 3A 21 F2 46 C3 91 66 A3 73 67 6A 3E F7 9D 8E 44 52 87 7B 8A C7 1B E2 D3 98 62 E8 25 30 2A 43 5C 5A B2 C6 45 F5 39 EC 85 81 BF 7D 22 4C E8 01 87 92 48 38 06 6B A0 83 70 0B 51 ED CF 7A FF E2 F2 06 3E A7 95 4E E5 29 23 32 1C FE 79 C6 08 C5 7A 39 B9 AF CD 4F 80 3E 5D 74 4D 0B E1 10 33 8D F0 54 8E 0E 22 96 B4 06 7F 29 01 1E CA 30 35 FD 8A 2E 51 04 20 79 7B 08 DC DF F6 64 21 6B C5 95 34 B3 40 D2 E8 CE BB DC 69 89 75 62 A6 0B 4A 49 9D 90 BA 68 2B BD 8A 50 2D 68 6B 56 40 0C 39 F2 08 20 1B EB A4 A5 20 1D 1F 7E FA 4B B8 2E 58 79 2A 16 54 26 6C C8 44 6C 4F 64 2D 5C 0C 47 2E 90 13 A9 D7 33 4A 51 17 6E 3F 3E 48 AE 39 D8 45 05 2C 0C 3C 9F 92 39 DB 62 B3 BB 64 EE 7E 91 C5 84 92 10 96 D9 F1 13 02 94 00 EA DA 87 7C 85 7B 68 BA 8D A1 AB F5 CD 9C EB 4C CD A0 38 78 43 80 DD E5 1D 28 25 1F F0 25 EF 0D 95 91 0F 21 5D 41 06 00 03 48 77 E0 98 09 3E 04 5A B0 93 63 3B AE 8E 49 0C C2 12 BA DD C3 5A ED FF 68 98 22 C4 5E F6 1E 85 57 15 E8 7E 26 22 E3 70 C2 57 F4 CE 2F CB C4 DC 39 4A 9C FE DE 27 18 D3 36 66 88 92 D7 69 D0 04 8E 93 9B AD E9 2E 5A 2C 91 CD 28 DF BE 62 CF 2C 72 8E FD A9 1F 0E 8E 00 9E 54 28 50 25 0C E7 DC 98 85 C9 B3 59 A8 97 F5 2E 7F 44 4C 43 3C C4 65 E5 AB DB 5B 3C 50 2D 53 B3 EA 74 3C 39 F4 0A 52 31 34 30 F5 E6 82 CD 36 D9 + */ +internal inline fun PacketFactory<*, *>.buildOutgingPacket( + client: QQAndroidClient, + subAppId: Long, + extraData: ByteArray = EMPTY_BYTE_ARRAY, + name: String? = null, + id: PacketId = this.id, + ssoExtraData: ByteReadPacket = BRP_STUB, + body: BytePacketBuilder.(sequenceId: Int) -> Unit +){ + +} + /** * 最外层的包. 结构适用于登录. * @@ -158,6 +200,7 @@ private inline fun BytePacketBuilder.writeLoginSsoPacket( writeIntLVPacket(lengthOffset = { it + 4 }, builder = body) } + /** * Outermost packet, not for login * diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index e93f4ea7f..be80b05f2 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -10,7 +10,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.utils.GuidSource import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.qqandroid.utils.guidFlag -import net.mamoe.mirai.qqandroid.utils.inline import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.cryptor.DecrypterByteArray import net.mamoe.mirai.utils.cryptor.DecrypterType @@ -321,18 +320,27 @@ internal object LoginPacket : PacketFactory + a1 = arg1 + noPicSig = arg2 + } + } client.wLoginSigInfo = WLoginSigInfo( uin = client.uin, - encryptA1 = inline { - val t10c = tlvMap119[0x10c] - val t106 = tlvMap119[0x106] - if (t106 != null && t10c != null) { - t106 + t10c - } else ByteArray(0) - }, - noPicSig = tlvMap119.getOrEmpty(0x16a), + encryptA1 = a1, + noPicSig = noPicSig, G = byteArrayOf(), // defaults {}, from asyncContext._G dpwd = byteArrayOf(), // defaults {}, from asyncContext._G randSeed = tlvMap119.getOrEmpty(0x403), // or from asyncContext._t403.get_body_data() @@ -393,6 +401,19 @@ internal object LoginPacket : PacketFactory Unit) { + val map = t531.toReadPacket().readTLVMap() + + val t106 = map[0x106] + val t16a = map[0x16a] + val t113 = map[0x113] + val t10c = map[0x10c] + + if (t106 != null && t16a != null && t113 != null && t10c != null) { + handler(t106 + t10c, t16a) + } + } + /** * @throws error */ diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt index 09f34d3d0..89e40378b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt @@ -162,15 +162,15 @@ fun Input.readFlatTUVarIntMap(expectingEOF: Boolean = false, tagSize: Int = 1): return map } -fun Map.printTLVMap(name: String = "", keyLength: Int = 1) = +fun Map.printTLVMap(name: String = "", keyLength: Int = 2) = debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys { when (keyLength) { - 1 -> it.key.toInt().toUByte().toUHexString() - 2 -> it.key.toInt().toUShort().toUHexString() - 4 -> it.key.toInt().toUInt().toUHexString() + 1 -> it.key.toUByte().toUHexString() + 2 -> it.key.toUShort().toUHexString() + 4 -> it.key.toUInt().toUHexString() else -> illegalArgument("Expecting 1, 2 or 4 for keyLength") } - }) + }.entries.joinToString(prefix = "{", postfix = "}", separator = "\n")) internal inline fun unsupported(message: String? = null): Nothing = error(message ?: "Unsupported")