From 932a3ef1f29680da4487e1b1b5db2e8652737d75 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Fri, 28 Feb 2020 19:16:22 +0800 Subject: [PATCH] Review: misc improvements --- .../mamoe/mirai/qqandroid/message/messages.kt | 2 +- .../qqandroid/network/QQAndroidClient.kt | 15 +- .../network/protocol/packet/PacketFactory.kt | 18 +- .../packet/chat/receive/OnlinePush.kt | 7 +- .../network/protocol/packet/login/WtLogin.kt | 37 +- .../src/commonTest/kotlin/test/printing.kt | 55 ++ .../androidPacketTests/clientToServer.kt | 533 ------------------ .../androidPacketTests/serverToClient.kt | 297 ---------- .../kotlin/androidPacketTests/utils.kt | 66 --- .../JceDecoderTest.kt | 2 +- .../src/jvmTest/kotlin/test/protoBuf.kt | 102 ++++ .../mirai/utils/BotConfigurationAndroid.kt | 46 +- .../net/mamoe/mirai/utils/addSuppressed.kt | 8 +- .../mirai/utils/cryptor/contentToString.kt | 46 -- .../io/PlatformDatagramChannelAndroid.kt | 2 - .../net/mamoe/mirai/utils/platformAndroid.kt | 1 + .../kotlin/net.mamoe.mirai/BotImpl.kt | 18 +- .../event/internal/InternalEventListeners.kt | 3 +- .../event/subscribeMessages.kt | 6 - .../net.mamoe.mirai/utils/BotConfiguration.kt | 11 +- .../kotlin/net.mamoe.mirai/utils/channels.kt | 4 + .../net.mamoe.mirai/utils/contentToString.kt | 167 ++++++ .../net.mamoe.mirai/utils/cryptor/TEA.kt | 145 ++--- .../net.mamoe.mirai/utils/cryptor/protoBuf.kt | 292 ---------- .../net.mamoe.mirai/utils/io/ByteArrayPool.kt | 8 + .../utils/io/PlatformDatagramChannel.kt | 6 - .../kotlin/net.mamoe.mirai/utils/io/Varint.kt | 150 ----- .../net.mamoe.mirai/utils/io/chunked.kt | 142 +++++ .../net.mamoe.mirai/utils/io/debugging.kt | 102 ---- .../kotlin/net.mamoe.mirai/utils/io/input.kt | 46 +- .../kotlin/net.mamoe.mirai/utils/io/output.kt | 6 +- .../mamoe/mirai/utils/BotConfigurationJvm.kt | 65 +-- .../net/mamoe/mirai/utils/PlatformUtilsJvm.kt | 1 + .../mirai/utils/cryptor/contentToString.kt | 54 -- .../mamoe/mirai/utils/io/PlatformSocketJvm.kt | 2 - .../testCaptchaPacket/TestCaptchaPacket.kt | 10 +- 36 files changed, 673 insertions(+), 1802 deletions(-) create mode 100644 mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt delete mode 100644 mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt delete mode 100644 mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt delete mode 100644 mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt create mode 100644 mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt delete mode 100644 mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugging.kt delete mode 100644 mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt index 875502082..5749525ff 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.qqandroid.message import kotlinx.io.core.buildPacket +import kotlinx.io.core.discardExact import kotlinx.io.core.readBytes import kotlinx.io.core.readUInt import net.mamoe.mirai.contact.Member @@ -19,7 +20,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.MiraiDebugAPI import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.toByteArray 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 74acb554b..e3f6c3f5a 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 @@ -7,12 +7,13 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_API_USAGE") + package net.mamoe.mirai.qqandroid.network import kotlinx.atomicfu.AtomicInt import kotlinx.atomicfu.atomic -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.toByteArray +import kotlinx.io.core.* import net.mamoe.mirai.BotAccount import net.mamoe.mirai.RawAccountIdUse import net.mamoe.mirai.data.OnlineStatus @@ -20,12 +21,10 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger import net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv -import net.mamoe.mirai.utils.DeviceInfo import net.mamoe.mirai.qqandroid.utils.NetworkType -import net.mamoe.mirai.utils.SystemDeviceInfo import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.cryptor.ECDH -import net.mamoe.mirai.utils.cryptor.decryptBy +import net.mamoe.mirai.utils.cryptor.TEA import net.mamoe.mirai.utils.io.* /* @@ -72,7 +71,7 @@ internal open class QQAndroidClient( internal inline fun <R> tryDecryptOrNull(data: ByteArray, size: Int = data.size, mapper: (ByteArray) -> R): R? { keys.forEach { (key, value) -> kotlin.runCatching { - return mapper(data.decryptBy(value, size).also { PacketLogger.verbose { "成功使用 $key 解密" } }) + return mapper(TEA.decrypt(data, value, size).also { PacketLogger.verbose { "成功使用 $key 解密" } }) } } return null @@ -314,6 +313,10 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) : internal typealias PSKeyMap = MutableMap<String, PSKey> internal typealias Pt4TokenMap = MutableMap<String, Pt4Token> +internal inline fun Input.readUShortLVString(): String = kotlinx.io.core.String(this.readUShortLVByteArray()) + +internal inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt()) + internal fun parsePSKeyMapAndPt4TokenMap(data: ByteArray, creationTime: Long, expireTime: Long, outPSKeyMap: PSKeyMap, outPt4TokenMap: Pt4TokenMap) = data.read { repeat(readShort().toInt()) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index 2c62b1477..a0410b37b 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -25,9 +25,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin +import net.mamoe.mirai.qqandroid.network.readUShortLVByteArray import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.cryptor.TEA import net.mamoe.mirai.utils.cryptor.adjustToPublicKey -import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.io.* import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -194,8 +195,8 @@ internal object KnownPacketFactories { kotlin.runCatching { when (flag2) { - 2 -> data.decryptBy(DECRYPTER_16_ZERO, size).also { PacketLogger.verbose { "成功使用 16 zero 解密" } } - 1 -> data.decryptBy(bot.client.wLoginSigInfo.d2Key, size).also { PacketLogger.verbose { "成功使用 d2Key 解密" } } + 2 -> TEA.decrypt(data, DECRYPTER_16_ZERO, size).also { PacketLogger.verbose { "成功使用 16 zero 解密" } } + 1 -> TEA.decrypt(data, bot.client.wLoginSigInfo.d2Key, size).also { PacketLogger.verbose { "成功使用 d2Key 解密" } } 0 -> data else -> error("") } @@ -335,6 +336,7 @@ internal object KnownPacketFactories { return IncomingPacket(packetFactory, ssoSequenceId, packet, commandName) } + @UseExperimental(MiraiInternalAPI::class) private suspend fun <T : Packet?> ByteReadPacket.parseOicqResponse( bot: QQAndroidBot, packetFactory: OutgoingPacketFactory<T>, @@ -352,10 +354,10 @@ internal object KnownPacketFactories { this.discardExact(1) // const = 0 val packet = when (encryptionMethod) { 4 -> { - var data = this.decryptBy(bot.client.ecdh.keyPair.initialShareKey, (this.remaining - 1).toInt()) + var data = TEA.decrypt(this, bot.client.ecdh.keyPair.initialShareKey, (this.remaining - 1).toInt()) val peerShareKey = bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey()) - data = data.decryptBy(peerShareKey) + data = TEA.decrypt(data, peerShareKey) packetFactory.decode(bot, data) } @@ -366,13 +368,13 @@ internal object KnownPacketFactories { this.readFully(byteArrayBuffer, 0, size) runCatching { - byteArrayBuffer.decryptBy(bot.client.ecdh.keyPair.initialShareKey, size) + TEA.decrypt(byteArrayBuffer, bot.client.ecdh.keyPair.initialShareKey, size) }.getOrElse { - byteArrayBuffer.decryptBy(bot.client.randomKey, size) + TEA.decrypt(byteArrayBuffer, bot.client.randomKey, size) }.toReadPacket() } } else { - this.decryptBy(bot.client.randomKey, 0, (this.remaining - 1).toInt()) + TEA.decrypt(this, bot.client.randomKey, 0, (this.remaining - 1).toInt()) } packetFactory.decode(bot, data) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt index 8f1ab906c..33fe56c32 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt @@ -11,10 +11,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.readBytes -import kotlinx.io.core.readUByte -import kotlinx.io.core.readUInt +import kotlinx.io.core.* import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.NoPacket @@ -38,7 +35,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.debug -import net.mamoe.mirai.utils.io.discardExact + import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.readString import net.mamoe.mirai.utils.io.toUHexString diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt index 29ea3b873..f9408baa5 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt @@ -20,14 +20,9 @@ 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.utils.MiraiDebugAPI -import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.cryptor.contentToString -import net.mamoe.mirai.utils.cryptor.decryptBy -import net.mamoe.mirai.utils.currentTimeSeconds +import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.cryptor.TEA import net.mamoe.mirai.utils.io.* -import net.mamoe.mirai.utils.io.discardExact -import net.mamoe.mirai.utils.md5 internal class WtLogin { /** @@ -317,7 +312,7 @@ internal class WtLogin { // println("type=$type") discardExact(2) - val tlvMap: TlvMap = this.readTLVMap() + val tlvMap: TlvMap = this._readTLVMap() // tlvMap.printTLVMap() return when (type.toInt()) { 0 -> onLoginSuccess(tlvMap, bot) @@ -325,7 +320,7 @@ internal class WtLogin { 2 -> onSolveLoginCaptcha(tlvMap, bot) 160 /*-96*/ -> onUnsafeDeviceLogin(tlvMap) 204 /*-52*/ -> onSMSVerifyNeeded(tlvMap, bot) - else -> tlvMap[0x149]?.let { analysisTlv149(it) } ?: error("unknown login result type: $type, TLVMap = ${tlvMap.contentToString()}") + else -> tlvMap[0x149]?.let { analysisTlv149(it) } ?: error("unknown login result type: $type, TLVMap = ${tlvMap._miraiContentToString()}") } } @@ -393,7 +388,7 @@ internal class WtLogin { // } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString()) } - error("UNKNOWN CAPTCHA, tlvMap=" + tlvMap.contentToString()) + error("UNKNOWN CAPTCHA, tlvMap=" + tlvMap._miraiContentToString()) } @UseExperimental(MiraiDebugAPI::class) @@ -405,17 +400,17 @@ internal class WtLogin { // tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } tlvMap[0x161]?.let { client.analysisTlv161(it) } tlvMap[0x119]?.let { t119Data -> - t119Data.decryptBy(client.tgtgtKey).read { + TEA.decrypt(t119Data, client.tgtgtKey).read { discardExact(2) // always discarded. 00 1C // 00 1C // 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00 - val tlvMap119 = this.readTLVMap() + val tlvMap119 = this._readTLVMap() // ??? tlvMap119[0x1c]?.read { val bytes = readBytes() - DebugLogger.warning(bytes.toUHexString()) - DebugLogger.warning(bytes.encodeToString()) + bot.network.logger.debug("onLoginSuccess, tlvMap119[0x1c]: " + bytes.toUHexString()) + bot.network.logger.debug("onLoginSuccess, tlvMap119[0x1c]: " + bytes.encodeToString()) } tlvMap119[0x130]?.let { client.analysisTlv130(it) } @@ -490,7 +485,7 @@ internal class WtLogin { face = readUShort().toInt() age = readUByte().toInt() gender = readUByte().toInt() - nick = readUByteLVString() + nick = readString(readByte().toInt() and 0xff) } } @@ -605,7 +600,7 @@ internal class WtLogin { } private inline fun analysisTlv0x531(t531: ByteArray, handler: (a1: ByteArray, noPicSig: ByteArray) -> Unit) { - val map = t531.toReadPacket().readTLVMap() + val map = t531.toReadPacket()._readTLVMap() val t106 = map[0x106] val t16a = map[0x16a] @@ -621,7 +616,7 @@ internal class WtLogin { * @throws error */ private fun QQAndroidClient.parseWFastLoginInfoDataOutA1(t169: ByteArray): ByteReadPacket { - val map = t169.toReadPacket().readTLVMap() + val map = t169.toReadPacket()._readTLVMap() val t106 = map[0x106] val t10c = map[0x10c] @@ -656,7 +651,7 @@ internal class WtLogin { //discardExact(2) loginExtraData = LoginExtraData( // args are to correct order uin = readUInt().toLong(), - ip = readUByteLVByteArray(), + ip = readBytes(readByte().toInt() and 0xff), time = readInt(), // correct version = readInt() ) @@ -699,7 +694,7 @@ internal class WtLogin { } private fun QQAndroidClient.analysisTlv161(t161: ByteArray) { - val tlv = t161.toReadPacket().apply { discardExact(2) }.readTLVMap() + val tlv = t161.toReadPacket().apply { discardExact(2) }._readTLVMap() tlv[0x173]?.let { analysisTlv173(it) } tlv[0x17f]?.let { analysisTlv17f(it) } @@ -750,4 +745,6 @@ internal class WtLogin { } } } -} \ No newline at end of file +} + +private fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt b/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt new file mode 100644 index 000000000..043d67d47 --- /dev/null +++ b/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +@file:Suppress("NOTHING_TO_INLINE") + +package test + +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.Input +import kotlinx.io.core.readAvailable +import kotlinx.io.core.use +import kotlinx.io.pool.useInstance +import net.mamoe.mirai.utils.DefaultLogger +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.MiraiLoggerWithSwitch +import net.mamoe.mirai.utils.io.ByteArrayPool +import net.mamoe.mirai.utils.io.toReadPacket +import net.mamoe.mirai.utils.io.toUHexString +import net.mamoe.mirai.utils.withSwitch +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + + +val DebugLogger: MiraiLoggerWithSwitch = DefaultLogger("Packet Debug").withSwitch(true) + +inline fun ByteArray.debugPrintThis(name: String): ByteArray { + DebugLogger.debug(name + "=" + this.toUHexString()) + return this +} + +@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class) +inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R { + + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + callsInPlace(onFail, InvocationKind.UNKNOWN) + } + ByteArrayPool.useInstance { + val count = this.readAvailable(it) + try { + return it.toReadPacket(0, count).use(block) + } catch (e: Throwable) { + onFail(it.take(count).toByteArray()).readAvailable(it) + DebugLogger.debug("Error in ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count)) + throw e + } + } +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt deleted file mode 100644 index fcc294793..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") - -package androidPacketTests - -import kotlinx.io.core.* -import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger -import net.mamoe.mirai.utils.cryptor.ECDH -import net.mamoe.mirai.utils.cryptor.contentToString -import net.mamoe.mirai.utils.cryptor.decryptBy -import net.mamoe.mirai.utils.cryptor.initialPublicKey -import net.mamoe.mirai.utils.io.* -import net.mamoe.mirai.utils.io.discardExact -import net.mamoe.mirai.utils.md5 -import kotlin.text.toByteArray - -// sessionTicket = 55 F7 24 8B 04 4E AA A8 98 E6 77 D2 D6 54 A9 B4 43 91 94 A3 0D DA CF 8F E8 94 E0 F4 A2 6B B4 8B 2B 4F 78 8D 21 EE D4 95 A6 F7 A4 3D B5 87 9B 3D -// sessionTicketKey = B6 9D E4 EC 65 38 64 FD C8 3A D8 33 54 35 0C 73 -// randomKey = A4 9A 6A EE 17 5B 7E 3D C0 71 DA 04 1C E1 E4 88 - -// login send 20da22db750806141ef448110800450005aa50ef400080060000c0a8030a71600dd0fe501f908b8c28a908ce7556501001fb487f00000000058c0000000a0200000004000000000e313939343730313032312b87bf2f7c22eb2396f80b887dc6410b85b6f1fc05874d1331d8722ec60d01a47b9ad330945d474934331ec4de12913e927bae5284862329eef2675a122dc83c00407ca3d4e734d772aaae33c4f17b6833a79e12a2ee549a17b08c7e4034874584f3e35e69c55e995e079ed1d0d877be9b1c9a03e62d0f3d714a1506d4a0292a853c6d28b582f5c715f5b6a6d051446b376cd9b55a311b42ba66f20080dc54a429fd240cdc9089534ffc3bcd02ab15f61b86467268e2e743a81f5709c07af6a7b03a90fb1d742988c13fa0b4b946079f9b9b2034fd8a330bc43fe8b2396ec3cac57d6753db28eb3af9e1904e8d4efbe38a354fd1aa71626a8f124498b24d9983d037e5a9f5ad44833df03335e4268cc9489366e793a84f4ed892a7d78345abce6b2888fe081b0317343a7e3bf6dafbe2c1caa0eb956637cf475a16ff1105b10c798a4ee68ea817d6edb0790aba884ced9f638b68cbfeb527d4178769efd533ba032e332f841fc3ede8b50da138470c1118fdb5070eda09215252e7fbca110d6fd4269aca30ed17e642f93caa5b5ef4b1a4abcbb91416e0388f2e625c1a362936360e5935a03a78433a44a56abc936670d44c350a88d0c2e82e092009e6262c1cbd523e6573aa9de5e304b5fc823877fcc4d4c06a08acc62b1b538c9692a70234985347eae2b00956408e2ab1a08a494118f1508364172a0d09eadfcf876855a41894767dd407f75875de1f21f9cdafc6cf8322f62115b721273659995c82987a6e972af56fa0cdd2cde2fe5de5771cafa1db1f70d5c1d70caa9361506533c0495a6f016c0e24fe196ff8a6f288de96255e6e811d6528b26a6c348b99f61b80992033406a05d005c984802ad055360d4e406090b96189c2733436ec3572fe24d6c516214f1a88e46798e2a5244428e61335d6f3f76a8d459715c172016686630d6e20ea6249ea27e4d8d0ae688c79dfc802257614b22f34232eb62d361df629957a05daf743fc8f0e14802035b7534a63628bd10bf128b4ae2ae067a2f6f6f5b62c772ff2afc09a5735e7fd3550886584d99694d001bda4ff945cf0a160b6c21ece0e7fda622ad74d1d15bc6f5173f44d7cbf25d320834e794e7a581350c2330a429a350499335cd1997db163d83de42c3adea77209e4620850900e5dd54a75c393dc89d686ea576fa264d382ead875c30de4462cb32c5f16917d0e2bbad2767a3c4de1a0318a073974f80277b1b9f25af00d0a966511f60de1d4eaa0c576cc27cbc1d910a6dd6c4bfa3ca66bb83cb0f3d791138df8c8afd5d8e8a2635c3d59cbb35d340fb881ad5d57bccbe1042f26ae4dff6b5347938cf5d2cda08f2beef38d666db5333b9168c677acf537b9a37d380ac8c8b4a8da11f5f118316b29f0a465f3616edaa58a0a5360d4b7f503db7685d3b3b702db3b5fb4e1bd8ecd674a1a2d8c85bf0c8c233f3255347991737c2f080f36f3012bfcd9814a205cb5b1ac0d6a99c6f43078d74d4babed46470cde8cf1839f75306d41278020aa94001b9eef595b57a568c03b4f59a5a59d3ac37609d896db7f88aed3a1f3bdc35a7035bcf8249758f41af966e33eb99a33972b5a8f3c1b8e34a27cfb59840a96b944eea462389b5e0c1762d039968bb707874bbbdafa539fbfcfcce58bcfbb913f2cef7a03ace5cdc14c43f195e1494c15cd6af5cc5cb0a13f6e272f65ee3625894c5f950473516c79168944695e295ca3dfd04d1b91ba70eb1c612a570b7bf1ca06c8d83da20c2e442acf5b70b7b3597a802bcf8b024a765f429b964809fcb48f43d727e0606b56fa4545361f743418a042c086f6dbce97ceb5719467dcbbb759e77eebd6c97b9f4ccc6312bbd972215a58ff0a83ff1717430273e11b04751f9beaeca285546ad1d24edc9ff19a9718e4e6eb6ea80e7e16a0518a96059659dc39c50bcbd4aa64f888d3085c7c5899b3a2c2342b598d -// trans emp 1 send 20da22db750806141ef4481108004500038450f2400080060000c0a8030a71600dd0fe501f908b8c2e3508ce7dde50180200465900000000035c0000000a0200000004000000000e31393934373031303231fbb1b5c286bc9eb6a2ae43ce77353dcb0bebf522873dfc23064ea499d060fd11751986d486a6744341c3ff8ee89c30566491a5a449363549f9b917f20e9b19eb04c58d7347e51ec00be55a5e4c2433f4fd981f617726a7f74f66f6b253080103d4754ccd9474a6451123818c94b84a9613875fce9aee86c9f3879df9d0918663ea888389ddb66007827a5bf38c97a7ea6f2ef60468519679c3405444df4a334108f03ca88fdebeb3e3ed39c0b8de6d440469428ceea3fac54ccb4c620d394ec98f94534419f34ec3c2200f6d066cee9d6b3dbc6e46dc313e3863681529f1647bf9d5726747954e3ffa7515105a98bb5a9b17b92a6c56cbcff298d46b653d2f72cbc24165cc0118910aba8c56b1cb6b35b2f7df51f30965bc74cdf422611779e6d82bda537e4790a0acb3b25004fd49cfcae70cc5f52e4c267e1aeb63acf1db34a0f591282024382d99453dee4a75aa6d9e0b69fe42efd1aeb914a4324066aa65037a1c8ca151e562c0bd502f2f5eb80deff7d817ef5cb5a4a03d13f08ce1bfe4485ced084f81376b2fb83f82200725c2a9e5be2f0ebea6b9a68d8ce072c68a62be4eaab170ef94036269267f53f75ed3f6369c80c50ceb9e481c8858e077e16a8d7a80df1406e792a561f635e6a4d5e6662e2422ec88617e350b8686b17ab3c17b6a3b59f9af1519c4c73642e14b9a533073455170daa51fbed6352fa3c957038256079a4395eecc2b6712d0ecdf9a62be9191c2b7cd22dd81c78865ba57626614415f78d8b7812f107acc9110bcff90a376a52e2dd652743770df9eaa9f199bc9e66997fbe121a005c606e9e3855473452379bc4e68f30f3be75f0346c452db79076bd7a47ecc3ad1b8fb2bb796fbf6c3899f1fbc61ee1560d5e9fed4ec157e6e377098e3d7ad43997f342393472e5084b6e4c44be0f2e527f01cb3ea849621ed0108d6109992b08db4d51b13918ca59622f7c0e8389520d79ec96e7818cebd47dd2a700069c32972133cf87083c58571a74c94b2a56c3bbc6f0a94eb95218122e19763deda732536a599138a4bd0b68a59526bba99476c3a5bf065f11b5abe9fd5c74d7bc2b407a362373cb5cf243ae198185b5dc9154d36409153c79097578c8d7c1ae3629dc46c5c9c0302c61c12d05051f82381029e6affe7c65d9b66ec -// GrayUinPro.Check (UniPacket) send 20da22db750806141ef4481108004500018c50f4400080060000c0a8030a71600dd0fe501f908b8c32f508ce7dde5018020044610000000001640000000a0200000004000000000e31393934373031303231f822fc392e93d773a975a2d467d2c40df1021fa5748fd80e8e86af4f4aa9c7745671b903fcb3dea0f314b7e9543b22f02410bd5288fcf358666cb9db4d452cefde2cc9e11b27c7e2ef386a7e8b523af49340e1a9ed10c3a37e6417028f5c019272c7b8e0e1a5af0b27d005c1333777376d960bb41f419842352c2a00e4ede8c642c4f4fd1339d8e81950e9490637cacf42c3ddb5dcb0e987836e77aeb65cf50d6a0867d061b08639f72eafe7b7c5f44240a1e1a9905526bdc6037373bfa20a3fe6d38db3696381831ef1725dfafc5e65b9c1fe77a85080f1a5dfe0c4961d21cd5b70623551b5371f0b4a6d9792d0332b5611cb54e56aa4b99704b34b27a661b7775cc0d16b981c7a7b57283b803b818869d21c91b84ade0ffda282f83bf6619084ef4a17b6301d096211c7bb00768e0d481b11f4907a130f092b4e2fbefdd9570718294c52232eae -// ConfigurationService.ReqGetConfig send 20da22db750806141ef4481108004500017450f7400080060000c0a8030a71600dd0fe501f908b8c345908ce7ff6501801fe444900000000014c0000000a0100000044e0e22a59327abb9ce80cf63be86694210344fab2f2b065d7785a32cacfa4075cd509f49cb37a075ad0685cbd472344bfdef1ede611c0ad81129be9e2d7e476d2000000000e3139393437303130323110192b12a4688c94df8f36df4db7a4f485475e5166166ac2d63858b4d6c0a5748b88373db0353efe12acb0df584dae933d1b6e81f3c19dfeea5e3457db3e82523f71d153dc1e6e14e3f144b4c1fda31aac8321d2d23cad0d4365985e2bcc39e20411a9520dedc8cba4ba1580215e6a27889390a8253aa783315e9ca4dd9f637cd137b4eec24ef8384851b989dfe562e69b9ee519da64abbe9ef25507f42804bd96357a219e62e594f025303dde5901a6b662f704ca3d21e40e2593910315fd3fbb571f513f047ac4b4abf6dea7324459cc6040c6776810bcfd2dd531fc139624394ae11c0143f261ecbbb331e83e1af3 - -// CertifiedAccountSvc.certified_account_read.GetMainPage send 20da22db750806141ef4481108004500025c50fb400080060000c0a8030a71600dd0fe501f908b8c37d908ce913e5018020045310000000002340000000b0100014e74000000000e31393934373031303231c64688d01401061eff81d0ddcdd5a41f2cf87fa1fb2057ef139d96c1e5b27f530e1b7378df9ea59103616c09895434f33523add35ab97703588ea24da0f616cc56a745f76c9071a9408b3d71ff8dbfd7dcfb9b69897c6bf9d42aef2a237f92e0ecf036f9ae20de61d6859d6681d195ccfcaadf6d3eca4211eec7ddb6ee8764ad9061353bf1e1f943a9546a88e4842e2ade0ee6229df968c1d0a119af4bd804af16863152203526dc6057216a676a80c0eb1f678dc2d9d0ab6dd5f6420d67a9889b7559e81a2c1cff31477a9f2dc6ae79c34b6da8564e589e6cb6ed4d201a9e47d85829ec21a5bd34066f8b2bf620dec4ed59dd2268e781cb8d53301e59dccb325a8f21c4e01cd56cf7f06566287afc58a16bd53cb6724f31629a3126a38cb8a29fb58f91fed11d90faacc2607dc853940c9eae2abcb26a6f54d868bbba36bad09a895baaf1915f79ad635a6af00144eac218d0a1664f83a261db7f3500ae85a26deaccc43ee98af0fb2be07b6cdda12931c51fe880e0bfb2f219993e71b4d36f5b22a05648c4ce23d9e767f695e25e6a2a303917d50934f80c1c9dfdfcee585d240269f7145ca4392231b808f8d4836ac56e02d5ece083432ccaf71228ea76a228484f4ec80688a08e452edbb02d033b752c1311bac1132a9bc14e33078b112113d29e305fafa2ac3aaad765420e1e28ff93fbbdf8c94c6d10046f3398d29f06917fcbdcd5c68735ef0e95558312d24df95909f6a7fa4f07 -// x2 - -// OidbSvc.0xcf8 (getShoppingCardInfo)send 20da22db750806141ef4481108004500009450fd400080060000c0a8030a71600dd0fe501f908b8c3b2908ce927e501801fe436900000000006c0000000b0100014e78000000000e31393934373031303231fde67e3ef90dfab4c8706743671f3abf660fc3f77e4efa3fabdeb96e9984107b3f4bf7677b5d34c72e88a6e212af9107e3c52c5f97b7e41c2c3cecfe9301293ab4be504d4d2eca28a816ec514deca805 -// OidbSvc.0x59f (requestIsFirstLogin) send 20da22db750806141ef4481108004500008c50fe400080060000c0a8030a71600dd0fe501f908b8c3b9508ce9366501801fe43610000000000640000000b0100014e7a000000000e313939343730313032319798479335a617d6037b4351bf340716d7095f01a83a19090ced9170a5d2e45578915b186770906f21b623304ee25b7ccb52126e3ab84275d2063f4801b3ea88f4b8fd064be98e75 -// friendlist.GetTroopListReqV2 send 20da22db750806141ef4481108004500011450ff400080060000c0a8030a71600dd0fe501f908b8c3bf908ce963e501801fb43e90000000000ec0000000b0100014e7b000000000e31393934373031303231e92012cde1f02af5088df9ee11e33f90ffc5416dce23cff1193985eb747ea1f453957b000b9f642708e394f590d5c8e7030064ef6f56210330319d4dd4fe21f0463095d74c6d5e21d94fcfb318c58e18d5a83ae548c03827b28c384e9a598f2ec9758ccb313fb825d44b727c10f8929682c5b6d34a8721f06e7ebb6768d39c6e1283b29bbdc15c6f35943fcc26195d1db57ae8de55d96637c7ae4caaa432bcf6768f17a39aa7f49882a86af04876146a2e44c3dcebf3ab106944cf7de19c85111add7b97c8d6bb53f773f11c09525614 -// OidbSvc.0x791_0 (getRedPointInfo) send 20da22db750806141ef448110800450000bc5101400080060000c0a8030a71600dd0fe501f908b8c3ce508ce96a6501801fa43910000000000940000000b0100014e7d000000000e313939343730313032311ca9030fef4676440544c30ee8455143b3d29bf4b96fcb3923e437c30fd64795a325c777e38cd08f173f5dd6814541ca160a2de1ebdb46dcd73386cbf35ee2faae6cdac5c91aa81cf0beca175d7fdee366bacea06859de8b3e163e95d6765256fc523b7e435dc8f73d1a3f5a5c6c793706c88e103a55efa5 -// OidbSvc.0x480_9 (getDetailCardInfo) send 20da22db750806141ef4481108004500008c5102400080060000c0a8030a71600dd0fe501f908b8c3d7908ce97165018020043610000000000640000000b0100014e7e000000000e3139393437303130323183063dc1abd81bf314960d1a00d2a457ff44cb694c4d17d048b888600262a1a957713055cd6e2a68a55d239e1d70d26f9a39bfc61418a8cfbdd96dac2ae1be32e34f94a0271339d8 -// OidbSvc.0x5eb_15 (getHiddenSwitch) send 20da22db750806141ef4481108004500009c5105400080060000c0a8030a71600dd0fe501f908b8c3ddd08ce9e7e501801fe43710000000000740000000b0100014e7f000000000e31393934373031303231e80e3bc84057f0f3cdbfcde52cf82c5c640f69afd3680638b3bbd86bab49ddb8e29e297815f1f41ddfecfe241f76b57f999b02988fe5bb6f92da0693e27e023baea0504b7ca768c541630e636c31a01bdce3df4c27f88906 - -// CliLogSvc.UploadReq send 20da22db750806141ef448110800450001cc5106400080060000c0a8030a71600dd0fe501f908b8c3e5108ce9eee501801fd44a10000000001a40000000b0100014e80000000000e31393934373031303231393e687d1ca6ee2f38f879c89a930bfbd7a9d0d5d6a84e3e76574b272980e8222c35564db3952d09f452bd954248922527ac9aee51a742b3508d50d3794ce81120245bff0a4e3ac74b026fb5444e9e5a5fad8a5c9865e855ede492fa445cc27e48a27f05d81f39e7f9818890717bffeab38df651f21e884cf5a7292c0f94aa3a849cbfdc84bc7e560e371fbbc4d62c1bd134b41f66915272cee5e4094ef47a5641cdb7e19a070a6babd955a02e722b2a5fbad6adc161f58f295d9a6406dd8bd9ac14bfc520e454eb2f0422ae20beaf6789d25ca367cb22fae28488670f5871e4fa7cd8953eb854a4b53cdd8ef0406f5efbd46870afb095efb7b375f956d4f085b6e03c7f7eada6b35d3cdc6e17c2e0169177f6bc24074f1a5fe90ea54cd8af627a00cb78518e6b7f3daab0811fc0872408581f385d25efeecfa4634998ffa81d43868521467262ff08f7fc338fac60499c3452f530bce46272599401fb4cf363ff74e8813c8e12a51b6bfb992e24d3dcf1dfde34af16e6e892f0a3cf146d2d3e444b62432e5e2932 -// CliLogSvc.UploadReq send 20da22db750806141ef448110800450001c45107400080060000c0a8030a71600dd0fe501f908b8c3ff508ce9eee501801fd449900000000019c0000000b0100014e81000000000e3139393437303130323128f6ceffe231f5a913fe9b4c59d24ea84d7dd8a67ef3c802f5eb2705ffeec4e98ddb95ec61e2adb2a52bcf4f744613a956a7af05be228b1dc95d40a0c4e187e79b495946e95bf2ffcabfc550c4f384269f87fea6d550a744a493cc0e11fea05cd7816fcf625b48ee6ed270e13fbacaeaef462a43dd17be5fa587e41e28fa40c083caa7e632cdcdef307404d75c30dce888de061ad715192916ec0b740c5614589b8e06683526042f8b74e896ba1049b71b8deff9354b1b2e83991d7426e2844cc592f511d9d51697361f6ef54370c4043b1bec4d7aa227804dc2b539ef95313b77561faa4d23f7392a2d18f5d796f7768af59d3faa65892169636d9c0e26a7b55be18bcb78719bd0a8ab5fa446f55e515ec5f2d0bfe9e0977ac98fe9af99f0426bc7d65066248d605eefed9b3c42f9d95d578e82515afce2fe239b857619bf64faadee7b10268bd3a079c23a413fffa209fdb9c7d23adab92e4c42fb446add76c24556d2f176f9020183814dfb2fa68861576bfcc41dfdd254d3a63dc8528b95 -// OidbSvc.0x787_11 (getTroopMemberListBy0x787) send 20da22db750806141ef448110800450000a45108400080060000c0a8030a71600dd0fe501f908b8c419108ce9eee501801fd437900000000007c0000000b0100014e84000000000e31393934373031303231de7a3d7936e5679df9be661fdb98fa181243fed121a7c37e0a0a035537b27705ed59f5707bf86495d9b4781418f5c8dc36f561d8b659e2936d4549b55ec6c807650b4e5af7c79fe881ee9cb3df4b4b307748103b3b52eef06aeaf81adb25767c -// OidbSvc.0xaf6_0 (getTroopMemberListForHeadBatch) send 20da22db750806141ef44811080045000094510a400080060000c0a8030a71600dd0fe501f908b8c420d08ce9f86501801fd436900000000006c0000000b0100014e85000000000e313939343730313032311425f3e63a4e0a0454c45b48639abbe10c101b19f0a66f37e774289e87cc3c54def76486925c7291f5d37392120d7e0d30a9caba77f73808b201f49ff2a04a55c5ec502b10a6f861dbffb6eed7324b92 -// OidbSvc.0xdc9 (getHostTroopHonorList) send 20da22db750806141ef448110800450000d4510b400080060000c0a8030a71600dd0fe501f908b8c427908cea04e501801fc43a90000000000ac0000000b0100014e86000000000e31393934373031303231abc2f4d9170210b0504e058282363e45527ce2ec6080f48a99d24ca40171ab146ae451517cc60d98585c746d2d00d434d3b44a9e3c9bcbe0a5ee23d406d8ae9c88b9d51beae9d05a3c22fa67f5a03bea60c17017848d32852b7e6abbe290974f74183d7af2b20d6c43c133b52ec2506f9e752676a40518cf73b922505e1e5497fb58146e526627c8fdd2c8ec4424ff96 -// IncreaseURLSvr.QQHeadUrlReq, IncreaseURLSvr.QQHeadUrlReq send 20da22db750806141ef44811080045000150510c400080060000c0a8030a71600dd0fe501f908b8c432508cea116501801fb44250000000000b40000000b0100014e87000000000e31393934373031303231a3e5c904a29879bfe01d903db3522e51d9d388a6f976387267c78f937ebfb4b800cf847d9af9522bcf8b4e710d905935a904be7e144a8f85db87e784b42bdcc1f19b3dbf307b37d62ba3ee42e6145249161da0a9c608289f139e4f545b9efe40f868a9d7fc06381e59499b04da34e36aac978bb42ad6b93efa55fd63ddc18db1216c68afafc50c954caace2c159cc2ae5cdda25d4bcb5f2b000000740000000b0100014e88000000000e31393934373031303231c571c13b189ca8a129aeecfc734352ff401917c63da218afd73b3f95eaf5121a54331f4ae9534b5c5c5add7307c612fe935cbdfedb775ff1b386adba02b35984aac175015452125c1e93cb333e43c314d53cbb0201ebf0a5 -// OidbSvc.0xc42 send 20da22db750806141ef448110800450004ec510d400080060000c0a8030a71600dd0fe501f908b8c444d08cea3ee5018020047c10000000004c40000000b0100014e89000000000e31393934373031303231e59b7a31945585845fec9d893236808c4868e458601b681c89bdefb2b52d4c2b4f3f3d6e6abe0e0e0fc8df28dccc8733c80a4bb9869cfd7203664227c9c485d1e22d02f7fb24944d70542610a73cdb657fb915b1ded3c1a5a52eece81fddbe4df82302b948c88b51bb4fa1bcc9af6302304ffd89ae1fa47f6b732c84c1deb3d4b1bbd6818359f066be51e868d90bd500bb5b3d65ae0cc11a40c3aa03e3aa1e1144dea014d65d1d1ac82cbf6df2415c4b0629bd07323d61f5735bc768d0bbcc7be7fd66ebd9b65232b5491a7c2b33c1079f0af971c0ff178868361cd0fb40b05e3d421145e43a92696ef394f3fbc9e1ac0c037791aa57ad1c6542195c681eb951e7e59532fc9c1eed68c96e79e720d3057f1d6ae4cf4ef253af35fdb2feadec3bac82091d8146a7022b5f2578e3f126a474e9f1cec9d987b055c9217c003931a5cf03530a0062641e6f4549cedf50ea16c75feb7035a849a3229a3663c135cd8905f17176e878fe29942a55107895041595e5b497ca8796d9489d0682e8823589965240ade202ea0efa9f84f1ebc0e25d33e3e81b3a43d7bdc75005fe8b43d09fe914c6af785ba4ad4cb40cb41f5bc622af6dc1cad1460871b8c7fdec63e08a80536ddf340e907b1eddd8fa90d2323d3e2920c7cd80fd379d633e9f8b8c526f47f9e101d6a1edeccaf15d61c893cc55e9c192cd23b7a417c3175461096146a70a08717a8e654086d8db042b674e72dc54a057a80dd7dd291b85af7d4f7ee0b8dabb8a0e58cafb411f1e501b855a2e6ea6b5a089cb8d066d5d9181d0e16d5389123f96a06d630269efe3727ccc2ca84a4b3dbd1f5d786c3a655e2b3e9311bae60acf3855e55b5e96981295193564ab0172ab8fd6fb4a6c05bff477e7800dcfa6d1e6d2face51219e69987a1a03419ce7d6a43b6216f5f3f02d87b8c9e4faf227f4509427f3b26316f3e996a4a9dae90afc693b3eb359ecd2226087305d77bfe3f6fcbb949061f7a4d1076634144fa1b3f2491ca34d75ee4d3c7a4f4ba17233b7e56bcc8c04044ee5c0d0eed5c53814a6efc0347251e47eaf5981bfc5ccc7b3c2c9e6b2d8b5c1b81f2d79d5134420f5909d0526c5d761b94c0afade2dda634f2d44b911b977630dec1c3f7aa0ed1aea422aebb5c6aef49a9873ce5df751bea10926983c43843fa5adc56077f835e84bc07fd434867e4af0d605c64c729baa77e6a22d5b58d4b9378f518b10f57a61a486a815afb0e3591f85b5c5cdb37f5fba5c89b2638b21c27cc369acd6c995932ead6ceb49fdf6122e2c09b385eb43ffbf2e81acb31057beba0700fb388374ce9fb33794b3991e9b3a18123e732b8d14e5aef01391424200dab855c12b47ade4a2c60b760cd9e3c5400d452fa040ead996a21aad6ce6fac722121f35910b39a3cc8e20543fb2539fe3fe538162f086e1de508f0f9b0cc2414b8af6800ece710f944c95031d2536470ca87083a41641392edbf976413844cd7c7d64a6ed61a2f7ce8e09a6948099d62da1f81b28fcb50424f88b1f0a9b271a7172247ff0313e0dff87ef3b2b4d965852d200e0476fb56aba5a71e5e7a2ad500e62e7f15dc165f0a6b315bffde194822420b846fd18ed2f510eb2610d5d23ccaffbded2c495224c0a215859bf03f0cb1ffc7f3373aff418326c2a98c7a44e8bffe93b -// ConfigPushSvc.GetIpDirect send 20da22db750806141ef44811080045000094510e400080060000c0a8030a71600dd0fe501f908b8c491108cea4b6501801ff436900000000006c0000000b0100014e8a000000000e31393934373031303231831dcd7b8023b2e8434f5897e7a34a13b67bcf4def875c650022a4a6b71417d9151938fe206612ae192f55d174e41aa848a02e51441700440260e2fdaf7882cfc60b2a10946dc2e76c172ddefc309389 -// * friendlist.getFriendGroupList send 20da22db750806141ef448110800450001245111400080060000c0a8030a71600dd0fe501f908b8c497d08ceae5e501801fd43f90000000000fc0000000b0100014ea4000000000e31393934373031303231899eb2e0bc999ad0799e23cd538fcb600850fda949fd86368e3897c1e1b736ce01509901fc190da6c2c0eb00edd851ebad2efac593997488b51ec58d289d3a050ff1a732a616e3b4fffd40b7aca1da2d04dc0c9e1205bcc63c9473d9ed5b4386cf41d4a786f7382f94266b09db3e3993a27bff4685ffb7d9eda7f66fcf592c8c8d593aa8c00e3fae4274371e5c4b4c3c23bdbc4f6a255d1f5b66f4bf2d56798099afbb34507f516d16ae24b344607bd23c899e241048c18b227a6094cbdcb12d2a8a33b443f7a4f26bc9b3f04fd8001daa46302bdeb22bf32bdc72d06c4a7959 -// * StatSvc.GetOnlineStatus send 20da22db750806141ef4481108004500008c5112400080060000c0a8030a71600dd0fe501f908b8c4a7908ceae5e501801fd43610000000000640000000b0100014ea5000000000e31393934373031303231954e34510d4f281e8676c7345c1e8d453c99aeca52c7649053a6230953ac2bca49f6e250394081be6153eceaa531aec179450ec3d671d72251db0c36cff7e1ba3b6de0e6ade53de2 -// * account.RequestQueryQQMobileContactsV3 send 20da22db750806141ef448110800450001445115400080060000c0a8030a71600dd0fe501f908b8c4add08ceba36501801ff441900000000011c0000000b0100014ea9000000000e31393934373031303231a78ed17a376177e537dec3ed98b7e47b51e9be299048820b1bf3ae88d8a18324e6773ef89922a7e7cd46f24d18a6f8aa53472bfeb09000bb7c44ef5f646d8a8f9e0d04accb35183b0216bea7dee1ef8a51207f11a0681fbcfcd09364a21b16bd6974fef7d66c7eca43aea623c499f1c81fb995d11d5490e64f7929861b670e2f24dc975684ea3098f0b96aff986b4e0b6b09a45bf7f2e301fa58ba1b462e755685681c112c3d2ac369d785d56c25c12b1c66bf39396030b374400879c922a7c43d6d36cc40aa8482f079ef48355b9a2562669f5bce3d3ace4f014ce0454b158693f2727833d62d29bdc7884b165b93c57e60d443351e9fff7e093494f0c61918 -// OidbSvc.0x58a (getDiscuss) send 20da22db750806141ef448110800450000945116400080060000c0a8030a71600dd0fe501f908b8c4bf908cebb5e501801fe436900000000006c0000000b0100014eaa000000000e3139393437303130323178d1c8e8c5d15a4dca043edc70fa514b19b92706d26a84f2bdb1b3e478d5ef53748b8d6082dea9a74f1c9f0ab519406c1ef36fcc0502cf201f658ecac1cd1682a34bb1ff42ac64da5f35e3af7fabcc8c -// OidbSvc.0x7c4_0, OidbSvc.0xbe8 send 20da22db750806141ef448110800450001085117400080060000c0a8030a71600dd0fe501f908b8c4c6508cebbe6501801fe43dd00000000006c0000000b0100014eab000000000e31393934373031303231a047827363fff4e4596299c74f6d847be9e8b5c2e4327bdaddffb8dd40e8df0eb76c54a6c7b7a56eddbae7136bb2b076386eb3b51091c46e756d1f9241678b656498da02276578cfb1ebfb6445513f51000000740000000b0100014eac000000000e31393934373031303231bd70b59f03df1144ae0ab45768257763ae622d5517bc20092fffaf4a5a3d55118ef6cd02907467c0eaa4a45309661277166f75e8ec4b75c0ec5fbf313e03b4a667ce26dfb0d148039a4d9ef8be7ad8cdf5e5a810edb60dd4 -// OidbSvc.0x58b_0, CertifiedAccountSvc.certified_account_read.GetFollowList send 20da22db750806141ef448110800450002f05118400080060000c0a8030a71600dd0fe501f908b8c4d4508cebcd6501801fd45c500000000009c0000000b0100014ead000000000e313939343730313032311c087c0ad4a44631d3e9eb9a6ebb4456a003db2e7eaa5e27ffd4c3ee592b8de7b89b9f6a56fc8260e57eef4f3b6616a5255ab830548dd50e9a99cf87011584f3811591c35fc95cadfc364bbeb0ed337063a518aca85fd17c30ab57408389f5c071ed2626ec47b1086478164d0ae0bd0f21222eb7d4b7a5601bc160f99a8f70330000022c0000000b0100014eae000000000e313939343730313032312dd572fad1b71384d3fdb16a1c57f3cbadac7e41ae6de9d13cc2827262e4c5a4bb95829233b72b5f9076b4652c1ffd1ebba874003bcf7e3be86cdc1034ac656efde24be90a49484209c954b78dec531b3d194cc92a297efef340d8273de90a63335bf543109e96a5aa958283161e7e78f50b74b49adb176472ac82fdad36adb68b38421deba0d1c1dcf24f8342bb8d39c1181545a675abb92a3e28431e60a02aaca6c2b4f5ea79c3f4431865444932c9af983f07dd39dd6d81e47f0f59e386e5320051472fe9235f3b98b8850a9335074173730103565ddf81fb57fedb200f328d9ba16d2e52ecf1cc590382087529678501b1ab6b5866afb25b70f0d03b0773f84691e6b94e6d03c3c47f1baa4e17552fafce1bcc90e336ae0d7fca76babcb035378ce191aee9315885197a39301f319857c33cd25a017f6a0480e16030b623547216bafe886f41014f65ae2e6cf3b4c58a4ce96b8f0967c4b0e0be6a9e669c25a06e6b412f115522522af613c6b8fee922d4f3f189d3c2696521afd63fdfc4103bd827a3eb4aba949a6e07424510af29c74e3de4faa1706bf128ac7f9f66306852da2390bb6cb8ed34c885b49d577e22b86222e95142b351dec31690804b98d98baa66b405cb6d649069d9e21254f2f81a6963962955c86e6a74be6c159eb9dce9eff7eb7e0c34f864d826ececbc1e577b4498b403140983fd716ca2b04aa5c23b8c18fe28367bc5ba76081c4aafd3 -// CertifiedAccountSvc.certified_account_read.GetFollowList send 20da22db750806141ef44811080045000274511c400080060000c0a8030a71600dd0fe501f908b8c500d08ceca66501801ff454900000000024c0000000b0100014eaf000000000e313939343730313032318b6198dc33ae901346ecbe82c9821a5e2e7e68d070b2baa3dc8525246a625cb7461bf329a93acc9d20b0221108cd838505d2c0e2c9b944cf7c57eb17b74e74c99c873febfc6e89395b139c033c68d66b731b0ad4d9d7444471b7638628924c3ebb4c2ed326a87b1761d5d5a72043c5e3e538d5797843333867177b5f52445e0b5fa5805cba497e5f0f7da203da3504d74977180e1b86dcdb0c3b7298c4b10d6e53a145c277033e20445a82d0fcc78a1ce597294bf767e43ca42608e7531577d185cc14f91db8f3b71b614f5299ab75c448dd7462a0ce66b1a0992db05ebbab6667ca79ff39d2e9e5f58f73eb2b47cf1896e5560ebe389289b2222687ea8af254b4518ccc6d3468dd03ebce6247e7aac4278ba99210b4c91c49c6f29fd3ecb2653eae174e7bddaf40ef743f8da7605d09d36bca5ef84ee9b75d126db616fbe2b67cdbbb26809d56e97b2e4f6175da52694f0ee640ea1990dc79e5a0c2b266ddc16d0b7bf19818ad7727add2fd2ab379ba1b41763d953d0e21dd26e2f469e8273059f5130355a0976d5a01ca438f3b698b96a2c5394b6ebe381b3818b032ee5226a1a246935a741d39fb84feb917f9680d6c7b2e950e74def6523c318cbbe592904167f2ff8dbaf10616fb2b39a5af68fa19443e874ad2a6bc0639240903fccf8d2eecaacb9404c15ec387faf459f438c229e4a571234403078d270499a2634b13baf5051979ebe31ec8fedb55854e73d58534e51a924c637ee797389de4a27da494dbe7b50c69e1c4857537d3f369752e -// AuthSvr.ThemeAuth send 20da22db750806141ef448110800450000a4511d400080060000c0a8030a71600dd0fe501f908b8c525908ceca66501801ff437900000000007c0000000b0100014eb0000000000e31393934373031303231080fdebf8f489c29fc1aa0611a0ee8dbcf8ada3129852e779569f93003e9af00101b5b5bf3c4a87ec47420f735c83d1029c82729ba3191fac7059dac5122342dc4560637b8ff15bfc84d8e84e8dfbc5dc8de067ae6b902c50c3e5a1915f0753a -// CertifiedAccountSvc.certified_account_read.GetFollowList send 20da22db750806141ef4481108004500027c5120400080060000c0a8030a71600dd0fe501f908b8c52d508ced606501801ff45510000000002540000000b0100014eb1000000000e31393934373031303231379d9ef3c72b17257dadcc13699bb8b027ccdd304c5ec4df57671b991739b43493c60017360ba5f88dd8a8afada19c134c82cf9ae4b5216f2abd4bc30563b981cf22865a55edf3856e46c6a3d7081eed19eb3c4f0a45bfe3120dca2d49e105827873ca33d5c2abcfb89428a5bff03fdb6337d86bff7421ffbc35887803ea9820abb58a0f7ccf607b41e203a8c3902933246a1e3b2f751ff97f8e2cc4661185f8f62f5183306222b8926c00249a80a285d9b43bee57935a825b2b07c1b4850953f04e866006538c8e7202444c25e5d87e6b040de52a7ccfd5cbba56e748b6a1d26d6e14bd4968039ae1c0f3745e7f2d41e1f8b3f46547c0dfcf045cef83bfedc9fc5b59e0adc8ec33d752835f814197e3fd2d5d5d99b20961b33b519e5457c14c84a45d4a5a8cf28ef428b33003a79753c7406181a1ac8de81fdfcac09c6a6265787fc1d27db83d52f5f3676da6589ffc2434465a9363f458b12b56c962eef53d354ed450f0671d5be9df26c6611725840cb5c5bce33ff0bb0ac45cbd1ea735122cb0aa7d489720c31afa83eeef8e2b692e9b83afcfb38700391788335dbc811b43bc018062ff9205273bc8271d2afa958f7f3b7c4077beddc6acb77e1864ff2c614f3af60265e6876a5ecbb95ad78e5f8cf74162a7cf87887db855808181a3cd8d2c363ded3af6105ef8164b408ae7925e5b9311091ceaf8b116be765d29c6d1317c1b4d7febe2e3862ebab3c28eb2ea05587ed09cab27ecaedb38f0e651f2662f605251f90d9b029bd4b617188ab49afe306d1094df0ae0 -// OidbSvc.0x7a2_0 send 20da22db750806141ef448110800450000845123400080060000c0a8030a71600dd0fe501f908b8c552908cedcde50180200435900000000005c0000000b0100014eb6000000000e31393934373031303231d751dc69de793526fd65f55e83c1b8d43435ece7da38acf0b8503d8259484c583dbe89b603a4abc1f1f665d1a92876d711431b5833908d216efd7398286b8adf -// ConfigPushSvc.PushResp, ** StatSvc.register send 20da22db750806141ef448110800450002f85124400080060000c0a8030a71600dd0fe501f908b8c558508cee2605018020045cd0000000000ac0000000b01a4da6eb0000000000e3139393437303130323122465f76192ce454d64e90a36fe651eb96d052893396c002a815851dc04994d16f7a596b06ca02b4743077fb387442f00409806ece8bf2f533a129073db60c8d5a22b9d4224387629e8ce29959a683f505b741aa1a83068239f6df0f97a4e5499c1035092e7466b005307110460211aecd1f11c10cea0cc861007e08882accb0bbaec68cd9c9a83fd19bb5eebda016ff000002240000000a0100000044e0e22a59327abb9ce80cf63be86694210344fab2f2b065d7785a32cacfa4075cd509f49cb37a075ad0685cbd472344bfdef1ede611c0ad81129be9e2d7e476d2000000000e31393934373031303231f4cf6ac405adbbfab64c0905f9f1f4b3236e4b17c34bc8aaefd77b184f012c036978eea06bb7f9e6d443a9d71f6eaf8b08b3c49858269de0405d7acf5076fd3000357cdbced53142200fcd8f87dc2a047ecfdd0d374aa0bb1b5c97b35e59a7cc77f146c930d04be6b70a75be3d71704d1b95794cbc36ef0ffe96533d61521c7b4b676f6b067859c0760ae5689f146b3c1a19668b694a50aa1d561f2feb76b49d507dc530d49007ba08f84297b23290177539d0b2a3ceb0a2ef8a64b65494e574ed78857fc1c93911c7639dce6699d5fde605f432d4ab7dfd6cf8cc5451950b29d79f5e755f90faadf55dfcb3993d9b4fb9479f44e47c1a0702205da298327b0deb180e2976d07be7b6ac0302b128d792200a092e084ca6908fe0c13e142d50f42783009029b3d2da23bd1050a2cc56023f943ce3b164002fa31fdf9624a255455cc77b774223726f36cccb2603240ca528534ee1533eb66872b007ea20bb5e76f87efa35d7dcb4be7e5f410ed7276c09e20f04db63ea4b79fd7d2201ef0ec14b5f9795bd2bd028e58b3ab8d3461a188f113a8b50406e9c3db9f8b2115924faadc9f2707c6715dc2dda73119228079467fbe145cf951cf1948e755aa428f4e478a6ab9708ad39ded4 - - -internal var wtSessionTicketKey = "B6 9D E4 EC 65 38 64 FD C8 3A D8 33 54 35 0C 73".hexToBytes() -internal var tgtgtKey = "D7 71 03 E3 4C E5 8F 6B 05 D8 C7 8C 96 FB FB 23".hexToBytes() -internal var deviceToken = "CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40".hexToBytes() -internal var D2Key = "44 28 6B 35 7A 54 2D 45 45 5D 56 32 44 33 47 49".hexToBytes() -internal var userStKey = "35 29 42 54 78 62 47 68 5E 77 68 54 6B 76 57 5F".hexToBytes() -internal var tgtKey = "44 24 3F 43 3F 21 37 2B 29 44 6E 47 70 3A 4E 3D".hexToBytes() - -internal val t108 = "BD 12 96 6C 83 53 EF DD 06 16 52 16 B8 1B 25 69".hexToBytes() -internal val t10c = "23 7D 2C 7A 3F 4A 41 35 7D 3B 45 51 6D 3D 2A 56".hexToBytes() -internal val t163 = "2C 7A 7B 23 4E 24 3F 24 24 47 62 6B 69 2E 47 50".hexToBytes() - - -var ecdhPrivateKeyS = "97a52992cb7a2110413629af94a3c249c68a3b731510caa8" - -internal val shareKeyCalculatedByConstPubKey - by lazy { - ECDH.calculateShareKey( - loadPrivateKey(ecdhPrivateKeyS), - initialPublicKey - ) - } - -var passwordMd5: ByteArray = byteArrayOf() -var uin: Long = 0L - -fun main() { - val data = """ -20da22db750806141ef448110800450000285129400080060000c0a8030a71600dd0fe501f908b8c5bf508cef1de5010020042fd0000 - - - - """.trimIndent() - .trim().split("\n").map { - val bytes = it.trim().autoHexToBytes() - if (bytes[0].toInt() == 0) { - bytes - } else bytes.dropTCPHead() - }.flatMap { it.toList() }.toByteArray() - data.read { decodeMultiClientToServerPackets() } -} - -/** - * 顶层方法. TCP 切掉头后直接来这里 - */ -fun ByteReadPacket.decodeMultiClientToServerPackets() { - DebugLogger.enable() - PacketLogger.enable() - println("=======================处理客户端到服务器=======================") - var count = 0 - while (remaining != 0L) { - readBytes((readUInt() - 4u).toInt()).toReadPacket().runCatching { analysisOneFullPacket() }.exceptionOrNull()?.printStackTrace() - count++ - if (remaining != 0L) { - println() - println() - println() - println() - println() - } else DebugLogger.info("=======================共有 $count 个包=======================") - } - println() -} - -fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) = - DebugLogger.debug("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys { - when (keyLength) { - 1 -> it.key.toUByte().contentToString() - 2 -> it.key.toUShort().contentToString() - 4 -> it.key.toUInt().contentToString() - else -> error("Expecting 1, 2 or 4 for keyLength") - } - }.entries.joinToString(prefix = "{", postfix = "}", separator = "\n")) - -fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed", { buildPacket { writeInt(it.size + 4); writeFully(it) } }) { - val flag1 = readInt() - print("flag1=" + flag1.contentToString() + ", ") - val flag2 = readByte().toInt() - print("flag2=$flag2" + ", ") - if (flag1 == 0x0B) { - if (flag2 == 1) { - print("sequenceId = " + readInt().toUHexString() + ", ") - } else { - print("extra data=" + readBytes(readInt() - 4).toUHexString() + ", ") - } - } else { - //if (flag2 == 1) { - val loginExtraData = readBytes(readInt() - 4) - loginExtraData.debugPrintThis("loginExtraData") - // } else { - // this.debugPrint() - // error("未知 flag2") - // } - } - - print("flag3=" + readByte().toUHexString() + ", ") - readString(readInt() - 4) - - print("// 解密 body") - val encrypted = readBytes() - - val decrypted = encrypted.tryDecryptOrNull() - if (decrypted == null) { - println(", cannot decrypt: ${encrypted.toUHexString()}") - error("cannot decrypt: ${encrypted.toUHexString()}") - } else { - decrypted.toReadPacket().debugPrintThis("outer body decrypted").apply { - when (flag1) { - 0x0A -> decodeSso() - 0x0B -> decodeUni() - else -> error("unknown flag1: $flag1") - } - - when (flag2) { - - 2 -> { - - this.debugPrintThis("Oicq Request").apply { - /* - byte 2 // head flag - short 27 + 2 + remaining.length - ushort client.protocolVersion // const 8001 - ushort 0x0001 // const0 - uint client.uin - byte 3 // const1 - ubyte encryptMethod.value // [EncryptMethod] - byte 0 // const2 - int 2 // const3 - int client.appClientVersion - int 0 // const4 - */ - discardExact(3) - readShort().toInt().takeIf { it != 8001 }?.let { - println("这个包不是 oicqRequest") - return@debugIfFail this - //println(" got new protocolVersion=$it") - } - val commandId = readUShort().toInt() - println(" commandId=0x${commandId.toShort().toUHexString()}") - readUShort().toInt().takeIf { it != 1 }?.let { - println(" got new const0=$it") - } - println(" uin=${readUInt()}") - readByte().toInt().takeIf { it != 3 }?.let { - println(" got new const1=$it") - } - val encryptionMethod = readUByte().toInt() - readByte().toInt().takeIf { it != 0 }?.let { - println(" got new const2=$it") - } - readInt().takeIf { it != 2 }?.let { - println(" got new const3=$it") - } - readInt().takeIf { it != 0 }?.let { - println(" got new appClientVersion=$it") - } - readInt().takeIf { it != 0 }?.let { - println(" got new const4=$it") - } - - - discardExact(1) - discardExact(1) - val randomKey = readBytes(16) - println("randomKey= ${randomKey.toUHexString()}") - readUShort().toInt().takeIf { it != 258 }?.let { - println(" got new const in ECDH head(originally=258)=$it") - } - val publicKey = readBytes(readShort().toInt()) - println("ecdh publicKey=" + publicKey.toUHexString()) - - - val encrypt = when (encryptionMethod) { - 135, 7 -> { - ECDH.calculateShareKey( - loadPrivateKey(ecdhPrivateKeyS), - //"04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7".chunkedHexToBytes().adjustToPublicKey() // QQ: 04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7 - ECDH.constructPublicKey("30 46 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 1F 03 32 00".hexToBytes() + publicKey) - ) - } - - 69 -> { - error("encryptionMethod 69") - } - else -> error("unknown encryptionMethod=$encryptionMethod") - } - - val encryptedBody = readBytes((remaining - 1).toInt()) - - @Suppress("NAME_SHADOWING") - val decrypted = kotlin.runCatching { - encryptedBody.decryptBy(encrypt).also { println("first by calculatedShareKey or sessionKey(method=7)") } - }.getOrElse { - encryptedBody.decryptBy(shareKeyCalculatedByConstPubKey).also { println("first by shareKeyCalculatedByConstPubKey") } - }.let { firstDecrypted -> - runCatching { - firstDecrypted.decryptBy(encrypt).also { println("second by calculatedShareKey") } - }.getOrElse { - kotlin.runCatching { - firstDecrypted.decryptBy(shareKeyCalculatedByConstPubKey) - }.getOrDefault(firstDecrypted) - } - } - - PacketLogger.info("Real body=" + decrypted.toUHexString()) - decrypted.toReadPacket().apply { - if (commandId == 0x0810) { - DebugLogger.info("发送 login!! 正在获取 tgtgtKey") - try { - discardExact(4) - val tlvMap = readTLVMap() - tlvMap.printTLVMap() - tlvMap[0x106] - ?.also { DebugLogger.info("找到了 0x106") } - ?.decryptBy(md5(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray())) - ?.read { - discardExact(2 + 4 * 4 + 8 + 4 + 4 + 1 + 16) - tgtgtKey = readBytes(16) - DebugLogger.info("获取 tgtgtKey=${tgtgtKey.toUHexString()}") - } ?: DebugLogger.info("找不到 0x106") - } catch (e: Exception) { - e.printStackTrace() - } - } - - } - } - } - else -> { - this.debugPrintThis("uni packet") - } - } - } - } - -} - -fun ByteReadPacket.decodeUni() { - - // 00 00 00 C7 A4 DA 6F A2 20 02 ED BD 20 02 ED BD 01 00 00 00 00 00 00 00 00 00 01 00 00 00 00 4C B8 12 0D E1 DA 19 AF D3 EB 36 76 BD 42 08 F6 DC A5 35 69 C0 8F F2 75 28 B4 CE 09 C9 B7 86 E3 5A 14 D1 0D CA 5D D4 CB 16 77 8B 32 8D 81 3B 3F D9 52 13 77 03 D3 F7 0E CD 7B 21 95 D2 59 CE 0C 31 D6 F1 38 2A FA 82 AD 60 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70 00 00 00 08 02 B0 5B 8B 00 00 00 13 38 35 38 34 31 34 33 36 39 32 31 31 39 39 33 00 00 00 04 00 22 7C 34 35 34 30 30 31 32 32 38 34 33 37 35 39 30 7C 41 38 2E 32 2E 30 2E 32 37 66 36 65 61 39 36 00 00 00 04 00 00 00 5B 10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 02 22 14 DA 6F A3 0B 8C 98 0C A8 0C - - // 00 00 00 2A 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70 00 00 00 08 02 B0 5B 8B 00 00 00 04 - // 00 00 00 5B 10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 01 22 14 DA 6E B1 0B 8C 98 0C A8 0C - println("// 尝试解 Uni") - println("// head") - //return - readBytes(readInt() - 4).debugPrintThis("head").toReadPacket().apply { - val commandName = readString(readInt() - 4).also { PacketLogger.warning("commandName=$it") } - println(commandName) - println(" unknown4Bytes=" + readBytes(readInt() - 4).toUHexString()) - // 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70 - // 00 00 00 08 02 B0 5B 8B - // 00 00 00 04 - println(" extraData=" + readBytes(readInt() - 4).toUHexString()) - } - readBytes(readInt() - 4).debugPrintThis("Real body").read { - // real body - //10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 01 22 14 DA 6E B1 0B 8C 98 0C A8 0C - - } -} - -fun ByteReadPacket.decodeSso() { - // 00 00 02 24 - // 00 00 00 0A 01 00 00 00 44 E0 E2 2A 59 32 7A BB 9C E8 0C F6 3B E8 66 94 21 03 44 FA B2 F2 B0 65 D7 78 5A 32 CA CF A4 07 5C D5 09 F4 9C B3 7A 07 5A D0 68 5C BD 47 23 44 BF DE F1 ED E6 11 C0 AD 81 12 9B E9 E2 D7 E4 76 D2 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 F4 CF 6A C4 05 AD BB FA B6 4C 09 05 F9 F1 F4 B3 23 6E 4B 17 C3 4B C8 AA EF D7 7B 18 4F 01 2C 03 69 78 EE A0 6B B7 F9 E6 D4 43 A9 D7 1F 6E AF 8B 08 B3 C4 98 58 26 9D E0 40 5D 7A CF 50 76 FD 30 00 35 7C DB CE D5 31 42 20 0F CD 8F 87 DC 2A 04 7E CF DD 0D 37 4A A0 BB 1B 5C 97 B3 5E 59 A7 CC 77 F1 46 C9 30 D0 4B E6 B7 0A 75 BE 3D 71 70 4D 1B 95 79 4C BC 36 EF 0F FE 96 53 3D 61 52 1C 7B 4B 67 6F 6B 06 78 59 C0 76 0A E5 68 9F 14 6B 3C 1A 19 66 8B 69 4A 50 AA 1D 56 1F 2F EB 76 B4 9D 50 7D C5 30 D4 90 07 BA 08 F8 42 97 B2 32 90 17 75 39 D0 B2 A3 CE B0 A2 EF 8A 64 B6 54 94 E5 74 ED 78 85 7F C1 C9 39 11 C7 63 9D CE 66 99 D5 FD E6 05 F4 32 D4 AB 7D FD 6C F8 CC 54 51 95 0B 29 D7 9F 5E 75 5F 90 FA AD F5 5D FC B3 99 3D 9B 4F B9 47 9F 44 E4 7C 1A 07 02 20 5D A2 98 32 7B 0D EB 18 0E 29 76 D0 7B E7 B6 AC 03 02 B1 28 D7 92 20 0A 09 2E 08 4C A6 90 8F E0 C1 3E 14 2D 50 F4 27 83 00 90 29 B3 D2 DA 23 BD 10 50 A2 CC 56 02 3F 94 3C E3 B1 64 00 2F A3 1F DF 96 24 A2 55 45 5C C7 7B 77 42 23 72 6F 36 CC CB 26 03 24 0C A5 28 53 4E E1 53 3E B6 68 72 B0 07 EA 20 BB 5E 76 F8 7E FA 35 D7 DC B4 BE 7E 5F 41 0E D7 27 6C 09 E2 0F 04 DB 63 EA 4B 79 FD 7D 22 01 EF 0E C1 4B 5F 97 95 BD 2B D0 28 E5 8B 3A B8 D3 46 1A 18 8F 11 3A 8B 50 40 6E 9C 3D B9 F8 B2 11 59 24 FA AD C9 F2 70 7C 67 15 DC 2D DA 73 11 92 28 07 94 67 FB E1 45 CF 95 1C F1 94 8E 75 5A A4 28 F4 E4 78 A6 AB 97 08 AD 39 DE D4 - - // 00 00 00 C1 - // 00 01 4E B9 //sequence - // 20 02 ED BD - // 20 02 ED BD - // 01 00 00 00 00 00 00 00 00 00 01 00 - // - // 00 00 00 4C // extra - // B8 12 0D E1 DA 19 AF D3 EB 36 76 BD 42 08 F6 DC A5 35 69 C0 8F F2 75 28 B4 CE 09 C9 B7 86 E3 5A 14 D1 0D CA 5D D4 CB 16 77 8B 32 8D 81 3B 3F D9 52 13 77 03 D3 F7 0E CD 7B 21 95 D2 59 CE 0C 31 D6 F1 38 2A FA 82 AD 60 - // - // 00 00 00 14 //cmd - // 53 74 61 74 53 76 63 2E 72 65 67 69 73 74 65 72 - // - // 00 00 00 08 - // 02 B0 5B 8B - // - // 00 00 00 13 - // 38 35 38 34 31 34 33 36 39 32 31 31 39 39 33 - // - // 00 00 00 04 - // - // 00 22 7C 34 35 34 30 30 31 32 32 38 34 33 37 35 39 30 7C 41 38 2E 32 2E 30 2E 32 37 66 36 65 61 39 36 - // - // 00 00 00 04 - - // 00 00 00 FD 10 03 2C 3C 4C 56 0B 50 75 73 68 53 65 72 76 69 63 65 66 0E 53 76 63 52 65 71 52 65 67 69 73 74 65 72 7D 00 01 00 CD 08 00 01 06 0E 53 76 63 52 65 71 52 65 67 69 73 74 65 72 1D 00 01 00 B5 0A 02 76 E4 B8 DD 10 07 2C 36 00 40 0B 5C 6C 7C 8C 9C AC B0 19 C0 01 D6 00 EC FD 10 00 00 10 BB 95 0A A0 8F AB 2E 55 38 39 FF 6D 90 40 B3 48 F1 11 08 04 FC 12 F6 13 0D 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 F6 14 0D 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 F6 15 05 37 2E 31 2E 31 F0 16 01 F1 17 06 0F FC 18 FC 1A F3 1B 00 00 00 00 D0 0D 60 71 F6 1C 00 FC 1D F6 1E 0A 5B 75 5D 4F 6E 65 50 6C 75 73 F6 1F 14 3F 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 5F 32 33 5F 31 37 F6 20 00 FD 21 00 00 0D 0A 04 08 2E 10 00 0A 05 08 9B 02 10 00 FC 22 FC 24 0B 8C 98 0C A8 0C - - - println("// 尝试解 SSO") - println("// head") - discardExact(4) - (" sequenceId=" + readUInt()) - println(" subAppId=" + readUInt()) - println(" subAppId2=" + readUInt()) - println(" unknownHex=" + readBytes(12).toUHexString()) - println(" extraData=" + readBytes(readInt() - 4).toUHexString()) - val commandName = readBytes(readInt() - 4).encodeToString() - PacketLogger.warning(" commandName=$commandName") - (" unknown4Bytes=" + readBytes(readInt() - 4).toUHexString()) - (" imei=" + readBytes(readInt() - 4).toUHexString()) - (" 0 bytes=" + readBytes(readInt() - 4).toUHexString()) - (" ksid=" + readBytes(readShort() - 2).toUHexString()) - (" 0 bytes=" + readBytes(readInt() - 4).toUHexString()) - - println() - discardExact(4) - println("// body(maybe OicqRequest)") -} - -val keys: Map<String, ByteArray> - get() = mapOf( - "16 zero" to ByteArray(16), - "wtSessionTicketKey" to wtSessionTicketKey, - "D2 key" to D2Key, - "tgtgtKey" to tgtgtKey, - "tgtKey" to tgtKey, - "userStKey" to userStKey, - "deviceToken" to deviceToken, - "shareKeyCalculatedByConstPubKey" to shareKeyCalculatedByConstPubKey, - "t108" to t108, - "t10c" to t10c, - "t163" to t163 - ) - -fun ByteArray.tryDecrypt(): ByteArray { - return this.tryDecryptOrNull() ?: error("Cannot decrypt. Encrypted data=" + this.toUHexString()) -} - -fun ByteArray.tryDecryptOrNull(): ByteArray? { - keys.forEach { (key, value) -> - kotlin.runCatching { - return decryptBy(value).also { println("outer by $key") } - } - } - return null -} - -fun main1() { - val toUHexString = - "20da22db750806141ef4481108004500012c525d400080060000c0a8030a71600dd0fe501f908b8d83b908d5d6de501803fe44010000000001040000000b01000150ce000000000e31393934373031303231d2d5378a3c47b184e294b2afbf14704d7317bb38be8273dfa287e00a7aba8a8171771de1717fb7c1661d8c3d414f51096ab7b77b8828a65aab7e40259bc8359cc6e23a5f941d700fd7894d416b7a29a270773df81d3265d7d8d16d13429c0c72db48954b66efb9e6e4c13b2c36b0d73fe285c82a8c650f0b1cf1a7c7e11f0c32f50814aa5a43cd8ea88214249763f053794e338d5f1cf81c893b3944cca7635ffcbf8742892da5f4bcb2694954ddaee63fa2a298dc3bd4a22710f2064293c5304ad4faf5baa5b24b56455994ca4c4b1755c723aff08be5dc3a1bb6a72e10bb9ae77054baf54b7091" - .chunkedHexToBytes().drop(16 * 3 + 6).toByteArray().toUHexString() - - println(toUHexString) - println() - println() - - /* - 00 00 01 23 remaining + 4 - - 00 00 00 0A - 02 00 00 00 - 04 00 00 00 - 00 05 - 00 05 - 30 40 3C 5C D4 C3 65 C7 7F A4 40 A3 4F 88 7F D8 56 1C F1 12 EE 3E FC 7E 51 94 F6 D9 2E 01 2D CB BB 1D 7E 3A 01 0E AB 97 FB 55 20 2C 05 82 6D 70 87 33 F0 97 6F B3 04 DC 90 EC C1 D3 C6 C3 66 D8 26 1A B2 08 0B 89 0F 25 AB 8B 91 5C B8 C9 FF A1 DE 43 0F D2 F4 E5 F6 C0 1D DE 65 0B 72 1D 24 D8 7E C0 A6 31 64 71 1C C2 7D 39 93 6B 86 9F 62 2B 76 58 6C 49 5D 60 0B A6 E7 90 AB CB A8 72 E5 3F 6F 25 B2 AD A6 C8 C6 B7 B5 2D 90 19 71 A0 46 57 F4 BD 96 7D E2 EF 86 DA BE B8 F9 EB DA BB D0 B6 F0 73 1C 27 14 DB 3A 66 BF F9 68 CA 4A 7B 4A D2 DF 66 C8 B4 C5 56 93 72 22 D0 38 FD CA 61 74 31 6A C5 3D 0B 3F E2 92 6A 84 16 B3 E5 86 AD D3 87 7C 32 3E 86 DA B4 E7 69 A0 AF A3 C7 97 DF 90 DC 9A 5A 46 5F DA 32 2A 15 21 C6 A0 8C 8D DA AE B2 4D 49 0E 07 05 5F 12 03 1D 0F 5B 53 6A 8E F0 29 78 41 BD 19 AC BB 92 44 D7 2F 7A FB A9 46 39 AF 69 - */ - - // first (cli log) - // 00 00 01 23 - // 00 00 00 0A - // 02 00 00 00 - // 04 00 - // 00 00 00 05 - // 30 - // - // 40 3C 5C D4 C3 65 C7 7F A4 40 A3 4F 88 7F D8 56 1C F1 12 EE 3E FC 7E 51 94 F6 D9 2E 01 2D CB BB 1D 7E 3A 01 0E AB 97 FB 55 20 2C 05 82 6D 70 87 33 F0 97 6F B3 04 DC 90 EC C1 D3 C6 C3 66 D8 26 1A B2 08 0B 89 0F 25 AB 8B 91 5C B8 C9 FF A1 DE 43 0F D2 F4 E5 F6 C0 1D DE 65 0B 72 1D 24 D8 7E C0 A6 31 64 71 1C C2 7D 39 93 6B 86 9F 62 2B 76 58 6C 49 5D 60 0B A6 E7 90 AB CB A8 72 E5 3F 6F 25 B2 AD A6 C8 C6 B7 B5 2D 90 19 71 A0 46 57 F4 BD 96 7D E2 EF 86 DA BE B8 F9 EB DA BB D0 B6 F0 73 1C 27 14 DB 3A 66 BF F9 68 CA 4A 7B 4A D2 DF 66 C8 B4 C5 56 93 72 22 D0 38 FD CA 61 74 31 6A C5 3D 0B 3F E2 92 6A 84 16 B3 E5 86 AD D3 87 7C 32 3E 86 DA B4 E7 69 A0 AF A3 C7 97 DF 90 DC 9A 5A 46 5F DA 32 2A 15 21 C6 A0 8C 8D DA AE B2 4D 49 0E 07 05 5F 12 03 1D 0F 5B 53 6A 8E F0 29 78 41 BD 19 AC BB 92 44 D7 2F 7A FB A9 46 39 AF 69 - // - - - // second, cli log - - - // third, longest - - - // full trans_emp packet: - - /* - 00 00 03 5C // =860 - 00 00 00 0A - 02 - 00 00 00 04 extra data length - 00 - - 00 00 00 0E - 31 39 39 34 37 30 31 30 32 31 // uin - - // encrypted by 16 zero - FB B1 B5 C2 86 BC 9E B6 A2 AE 43 CE 77 35 3D CB 0B EB F5 22 87 3D FC 23 06 4E A4 99 D0 60 FD 11 75 19 86 D4 86 A6 74 43 41 C3 FF 8E E8 9C 30 56 64 91 A5 A4 49 36 35 49 F9 B9 17 F2 0E 9B 19 EB 04 C5 8D 73 47 E5 1E C0 0B E5 5A 5E 4C 24 33 F4 FD 98 1F 61 77 26 A7 F7 4F 66 F6 B2 53 08 01 03 D4 75 4C CD 94 74 A6 45 11 23 81 8C 94 B8 4A 96 13 87 5F CE 9A EE 86 C9 F3 87 9D F9 D0 91 86 63 EA 88 83 89 DD B6 60 07 82 7A 5B F3 8C 97 A7 EA 6F 2E F6 04 68 51 96 79 C3 40 54 44 DF 4A 33 41 08 F0 3C A8 8F DE BE B3 E3 ED 39 C0 B8 DE 6D 44 04 69 42 8C EE A3 FA C5 4C CB 4C 62 0D 39 4E C9 8F 94 53 44 19 F3 4E C3 C2 20 0F 6D 06 6C EE 9D 6B 3D BC 6E 46 DC 31 3E 38 63 68 15 29 F1 64 7B F9 D5 72 67 47 95 4E 3F FA 75 15 10 5A 98 BB 5A 9B 17 B9 2A 6C 56 CB CF F2 98 D4 6B 65 3D 2F 72 CB C2 41 65 CC 01 18 91 0A BA 8C 56 B1 CB 6B 35 B2 F7 DF 51 F3 09 65 BC 74 CD F4 22 61 17 79 E6 D8 2B DA 53 7E 47 90 A0 AC B3 B2 50 04 FD 49 CF CA E7 0C C5 F5 2E 4C 26 7E 1A EB 63 AC F1 DB 34 A0 F5 91 28 20 24 38 2D 99 45 3D EE 4A 75 AA 6D 9E 0B 69 FE 42 EF D1 AE B9 14 A4 32 40 66 AA 65 03 7A 1C 8C A1 51 E5 62 C0 BD 50 2F 2F 5E B8 0D EF F7 D8 17 EF 5C B5 A4 A0 3D 13 F0 8C E1 BF E4 48 5C ED 08 4F 81 37 6B 2F B8 3F 82 20 07 25 C2 A9 E5 BE 2F 0E BE A6 B9 A6 8D 8C E0 72 C6 8A 62 BE 4E AA B1 70 EF 94 03 62 69 26 7F 53 F7 5E D3 F6 36 9C 80 C5 0C EB 9E 48 1C 88 58 E0 77 E1 6A 8D 7A 80 DF 14 06 E7 92 A5 61 F6 35 E6 A4 D5 E6 66 2E 24 22 EC 88 61 7E 35 0B 86 86 B1 7A B3 C1 7B 6A 3B 59 F9 AF 15 19 C4 C7 36 42 E1 4B 9A 53 30 73 45 51 70 DA A5 1F BE D6 35 2F A3 C9 57 03 82 56 07 9A 43 95 EE CC 2B 67 12 D0 EC DF 9A 62 BE 91 91 C2 B7 CD 22 DD 81 C7 88 65 BA 57 62 66 14 41 5F 78 D8 B7 81 2F 10 7A CC 91 10 BC FF 90 A3 76 A5 2E 2D D6 52 74 37 70 DF 9E AA 9F 19 9B C9 E6 69 97 FB E1 21 A0 05 C6 06 E9 E3 85 54 73 45 23 79 BC 4E 68 F3 0F 3B E7 5F 03 46 C4 52 DB 79 07 6B D7 A4 7E CC 3A D1 B8 FB 2B B7 96 FB F6 C3 89 9F 1F BC 61 EE 15 60 D5 E9 FE D4 EC 15 7E 6E 37 70 98 E3 D7 AD 43 99 7F 34 23 93 47 2E 50 84 B6 E4 C4 4B E0 F2 E5 27 F0 1C B3 EA 84 96 21 ED 01 08 D6 10 99 92 B0 8D B4 D5 1B 13 91 8C A5 96 22 F7 C0 E8 38 95 20 D7 9E C9 6E 78 18 CE BD 47 DD 2A 70 00 69 C3 29 72 13 3C F8 70 83 C5 85 71 A7 4C 94 B2 A5 6C 3B BC 6F 0A 94 EB 95 21 81 22 E1 97 63 DE DA 73 25 36 A5 99 13 8A 4B D0 B6 8A 59 52 6B BA 99 47 6C 3A 5B F0 65 F1 1B 5A BE 9F D5 C7 4D 7B C2 B4 07 A3 62 37 3C B5 CF 24 3A E1 98 18 5B 5D C9 15 4D 36 40 91 53 C7 90 97 57 8C 8D 7C 1A E3 62 9D C4 6C 5C 9C 03 02 C6 1C 12 D0 50 51 F8 23 81 02 9E 6A FF E7 C6 5D 9B 66 EC - */ - // decrypted: - - // 00 00 00 C2 - // 00 01 4E 66 // =85606 - // - // 20 02 ED BD // =537062845 - // 20 02 ED BD - // - // 01 00 00 00 00 00 00 00 00 00 01 00 - // - // 00 00 00 4C //72+4, unknown - // B8 12 0D E1 - // DA 19 AF D3 - // EB 36 76 BD - // 42 08 F6 DC A5 35 69 C0 8F F2 75 28 B4 CE 09 C9 B7 86 E3 5A 14 D1 0D CA 5D D4 CB 16 77 8B 32 8D 81 3B 3F D9 52 13 77 03 D3 F7 0E CD 7B 21 95 D2 59 CE 0C 31 D6 F1 38 2A FA 82 AD 60 - // - // 00 00 00 15 // 17+4, command - // 77 74 6C 6F 67 69 6E 2E 74 72 61 6E 73 5F 65 6D 70 // wtlogin.trans_emp - // - // 00 00 00 08 // 4+4 - // 02 B0 5B 8B // unknown, =45112203 - // - // 00 00 00 13 // 15+4, imei: - // 38 35 38 34 31 34 33 36 39 32 31 31 39 39 33 - // - // 00 00 00 04 - // - // 00 22 // 32+2, ksid: - // 7C 34 35 34 30 30 31 32 32 38 34 33 37 35 39 30 7C 41 38 2E 32 2E 30 2E 32 37 66 36 65 61 39 36 - // - // 00 00 00 04 - // - // 00 00 02 70 // remaining size=620+4 - // 02 - // 02 6C // 27+2+body.size = 620 = 27+2+591 - // 1F 41 // 8001 - // 08 12 // commandId 2066 - // 00 01 // const? - // 76 E4 B8 DD // accountId=1994701021 - // 03 - // 07 // EncryptMethod - // 00 - // 00 00 00 02 - // 00 00 00 00 - // 00 00 00 00 // const - // - // // OutgoingPacket.body: ECDH encrypted - // 01 - // 01 - // // private key: len=16 - // A4 9A 6A EE 17 5B 7E 3D C0 71 DA 04 1C E1 E4 88 - // 01 02 // =258 - // // pub key: len=49 - // [00 31] 04 CB 36 66 98 56 1E 93 6E 80 C1 57 E0 74 CA B1 3B 0B B6 8D DE B2 82 45 48 A1 B1 8D D4 FB 61 22 AF E1 2F E4 8C 52 66 D8 D7 26 9D 76 51 A8 EB 6F E7 - // E8 6F F6 C1 D9 8B 9C C1 B2 99 A9 53 68 80 E0 4A 34 F9 C2 F7 6D A5 4E E6 25 F1 31 A7 16 46 6D 2A E5 14 2B 64 8D EA 29 15 19 48 69 34 C4 90 D1 50 9A A6 3F 58 69 94 B1 E2 1E E7 C5 D9 53 5B 6B 71 9F 20 9D 2D 02 B3 0D DF B0 F1 0E 03 1E 2C E8 5F F6 28 D6 97 FB A9 45 C6 E1 FF 79 84 C7 7E 42 79 81 BB 48 48 AD D5 9F 46 7C 45 EA B5 C1 10 F0 41 EF 94 A2 D5 80 67 EB CC 11 05 9E DE 06 A1 9E 5E 71 40 68 4A C8 32 B8 C8 48 73 6D AD 41 51 07 4F 43 E3 C6 7D 8C 7E 49 5D CF A3 D8 3F 29 22 AD 08 AE C4 15 29 90 22 DC 01 5D 81 BA B8 B0 06 ED B1 93 EE CC CC FC 65 97 1F 1F 22 36 AD 85 B1 3D 1B 02 7E C3 0C 1E 6E 4A 30 CB 4F 09 9D 67 C4 D7 DB 06 89 36 A0 7A 03 D0 46 5C 0E C1 B9 24 E4 30 6E BE 0C 60 25 10 57 1E D7 45 CD 0A B3 23 18 1C 47 0C 62 79 29 8F 55 3B F0 0D A2 FB DE 05 B7 71 AD B8 B2 D7 AD 4B 15 E0 ED EB 26 25 CC DE 39 66 8B 1A AE 96 0B E5 4E AB C7 A3 0C 09 82 D6 CD F9 3E 9D 6E C6 73 C5 20 20 F6 8E DF 80 95 13 68 9C 3B C7 EF 71 C9 FC 96 2C 07 48 0A 9A 06 8F 96 7E 90 1F 31 3A 05 86 86 E5 64 5D 5A 08 2C 6C EE 72 7C C2 DF 9B 3C F7 52 5C 17 0E 9B C9 AE 36 8E 54 C9 B5 5E E9 D6 F8 C4 54 81 AC 78 DE 1D 4A C3 31 C6 2E 3F 6D C7 9C FF 5F 7F 88 2C B4 63 CC AC FC 57 1B 84 5D 66 7E C0 14 29 1D 70 74 A8 EE 03 55 07 C2 D7 F5 CE 15 8F 9B DF DA AD C8 F9 33 90 CD 57 98 A1 62 94 E1 3E DD 0F F6 F4 6D 17 35 F1 CE 1D EE 72 72 8B 7A AB B0 7F 68 8F 40 68 96 20 11 BF 05 91 C5 C1 71 AB BF BC A1 9D CC 0B 7D 5F 24 23 3D AB 46 02 43 D6 15 5E 64 BD FD 35 19 C0 47 15 69 C1 EA 0E 9B 5E 8A CD C2 20 CF A7 39 2E 8B D9 89 FC 94 63 18 E9 DD 3F 5B 1B C0 27 4A 85 7A 84 3B C2 50 BC 6A 86 48 0A - // // decrypted by calculated share key: - // 00 00 01 04 - // 00 00 00 0B 01 00 01 50 CE 00 - // 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 D2 D5 37 8A 3C 47 B1 84 E2 94 B2 AF BF 14 70 4D 73 17 BB 38 BE 82 73 DF A2 87 E0 0A 7A BA 8A 81 71 77 1D E1 71 7F B7 C1 66 1D 8C 3D 41 4F 51 09 6A B7 B7 7B 88 28 A6 5A AB 7E 40 25 9B C8 35 9C C6 E2 3A 5F 94 1D 70 0F D7 89 4D 41 6B 7A 29 A2 70 77 3D F8 1D 32 65 D7 D8 D1 6D 13 42 9C 0C 72 DB 48 95 4B 66 EF B9 E6 E4 C1 3B 2C 36 B0 D7 3F E2 85 C8 2A 8C 65 0F 0B 1C F1 A7 C7 E1 1F 0C 32 F5 08 14 AA 5A 43 CD 8E A8 82 14 24 97 63 F0 53 79 4E 33 8D 5F 1C F8 1C 89 3B 39 44 CC A7 63 5F FC BF 87 42 89 2D A5 F4 BC B2 69 49 54 DD AE E6 3F A2 A2 98 DC 3B D4 A2 27 10 F2 06 42 93 C5 30 4A D4 FA F5 BA A5 B2 4B 56 45 59 94 CA 4C 4B 17 55 C7 23 AF F0 8B E5 DC 3A 1B B6 A7 2E 10 BB 9A E7 70 54 BA F5 4B 70 91 - // 03 - - - val data = - """ -E8 6F F6 C1 D9 8B 9C C1 B2 99 A9 53 68 80 E0 4A 34 F9 C2 F7 6D A5 4E E6 25 F1 31 A7 16 46 6D 2A E5 14 2B 64 8D EA 29 15 19 48 69 34 C4 90 D1 50 9A A6 3F 58 69 94 B1 E2 1E E7 C5 D9 53 5B 6B 71 9F 20 9D 2D 02 B3 0D DF B0 F1 0E 03 1E 2C E8 5F F6 28 D6 97 FB A9 45 C6 E1 FF 79 84 C7 7E 42 79 81 BB 48 48 AD D5 9F 46 7C 45 EA B5 C1 10 F0 41 EF 94 A2 D5 80 67 EB CC 11 05 9E DE 06 A1 9E 5E 71 40 68 4A C8 32 B8 C8 48 73 6D AD 41 51 07 4F 43 E3 C6 7D 8C 7E 49 5D CF A3 D8 3F 29 22 AD 08 AE C4 15 29 90 22 DC 01 5D 81 BA B8 B0 06 ED B1 93 EE CC CC FC 65 97 1F 1F 22 36 AD 85 B1 3D 1B 02 7E C3 0C 1E 6E 4A 30 CB 4F 09 9D 67 C4 D7 DB 06 89 36 A0 7A 03 D0 46 5C 0E C1 B9 24 E4 30 6E BE 0C 60 25 10 57 1E D7 45 CD 0A B3 23 18 1C 47 0C 62 79 29 8F 55 3B F0 0D A2 FB DE 05 B7 71 AD B8 B2 D7 AD 4B 15 E0 ED EB 26 25 CC DE 39 66 8B 1A AE 96 0B E5 4E AB C7 A3 0C 09 82 D6 CD F9 3E 9D 6E C6 73 C5 20 20 F6 8E DF 80 95 13 68 9C 3B C7 EF 71 C9 FC 96 2C 07 48 0A 9A 06 8F 96 7E 90 1F 31 3A 05 86 86 E5 64 5D 5A 08 2C 6C EE 72 7C C2 DF 9B 3C F7 52 5C 17 0E 9B C9 AE 36 8E 54 C9 B5 5E E9 D6 F8 C4 54 81 AC 78 DE 1D 4A C3 31 C6 2E 3F 6D C7 9C FF 5F 7F 88 2C B4 63 CC AC FC 57 1B 84 5D 66 7E C0 14 29 1D 70 74 A8 EE 03 55 07 C2 D7 F5 CE 15 8F 9B DF DA AD C8 F9 33 90 CD 57 98 A1 62 94 E1 3E DD 0F F6 F4 6D 17 35 F1 CE 1D EE 72 72 8B 7A AB B0 7F 68 8F 40 68 96 20 11 BF 05 91 C5 C1 71 AB BF BC A1 9D CC 0B 7D 5F 24 23 3D AB 46 02 43 D6 15 5E 64 BD FD 35 19 C0 47 15 69 C1 EA 0E 9B 5E 8A CD C2 20 CF A7 39 2E 8B D9 89 FC 94 63 18 E9 DD 3F 5B 1B C0 27 4A 85 7A 84 3B C2 50 BC 6A 86 48 0A -""".trimIndent().hexToBytes() - try { - println(data.decryptBy("F1 E3 A0 9F AD 63 80 68 43 3C 11 98 53 13 D4 BF".hexToBytes())) - println(data.decryptBy(ByteArray(16)).toUHexString()) - println("With key 16 zero") - } catch (e: Exception) { - println(data.decryptBy("%4;7t>;28<fc.5*6".toByteArray()).toUHexString()) - println("With key %4;7t>;28<fc.5*6") - } -} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt deleted file mode 100644 index 57392eb79..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:Suppress("EXPERIMENTAL_API_USAGE") - -package androidPacketTests - -import kotlinx.io.core.* -import kotlinx.io.pool.useInstance -import net.mamoe.mirai.qqandroid.network.protocol.packet.* -import net.mamoe.mirai.utils.cryptor.ECDH -import net.mamoe.mirai.utils.cryptor.adjustToPublicKey -import net.mamoe.mirai.utils.cryptor.decryptBy -import net.mamoe.mirai.utils.io.* -import net.mamoe.mirai.utils.unzip - -/* - */ - -fun main() { - val data = """ - 06141ef4481120da22db75080800450805aad52640003506283d71600dd0c0a8030a1f90fe5008cee55e8b8c5585501000f989000000000009080000000b01000000000e313939343730313032312dde8c38f7075e6bb46a3d04dfd5d720d12afb2eb067ba4f4595edbefd367615ce43b590f822f561d979fa1d05d94a02da20da629c562f7bd431e8969d5c36ea635429b9e5e0bff6ce8dd15ec796ce2edcd73637ff1f7eb4e1b1e7668abe6f29a9e94a0bba03220c95e89b927307c037a7c9bc2b5255647189b14c50ceb05c5c68d05ac732e23253b365d98295fe6c472db908e1ff173b4d43cda220f8bab1271fbb923b3df0585d582617d27369a1cfa0166b3fb680a0f608fb117f3355bdcdbef7d65d6de5f69b59be79b3836fc6cff3c0892cbb87a4cb1569955a866924509c933d79d423caea20b933b0738df02de26a9d057924191b34a2ec4c153c047886a0160ad536874bf67316c9776348b5a6f0bc4f542d3cab81dfa1198c19e4eeadc8484c4fbe47a3a290a57d02762937ff289a7c3859219d34d19cf00595674e063c1bf6d3f02199a1bbc2dd816c3760bd359b10160050a8bcadae6adcba3901cabb41f0c12d0f740a49e4508468cc60ab016527488ee921ec029d45db3b552bd73cb78942385ea7c1f3eb9b329ac3ad899547e7919a76c43953123e01221e656a2de4a307f068978e87b19db48ca05ac82860043869c8f1675e514dbb828ec98ee0591684a74143807d6a422927af4f448251b7d592cb7a4c8b34a8f13d9f9cf54e37e4c344f9aa54ac44be66b5450f237731fd24c4f42633078cd10907928987a25bdcb4cbe44ae507c700579680e8fb64ccd61dad8e6a9ceeac91ee9419f466bad22d6ac288c1219b00d4fb9009e170d7b5fcac2a672c11aced6a5eb6dd0b3dde1f29446ab6ddef6763661ed69bc8f0ab3520cfc332cdbe53355395bba697f1b6262507f62b1090e0733e351a76647e779d746f46685d36de7ba3d6a37485bba566060c55929f26e4773675ba4680d940e74c94b8cf64054ab298f100ebc7c19950443534a1fa342b53a315275b0004fc07ac95896620adc2f8befbdde38ef75c912cecd5e3a128d2a3f63773fdd0a4507debaddd527b31a19f63ff383b8ce6a00a0f5e2d7fbedcbd3eb9f2746f561b69e8cf0e9a9939e902a6158eea32e2add8f2975889174cbeb382596a3113e2741d47490382245ad4c405a9079779d989116df6fff14b89d1725907f809c4c7a9ff145852330bcde5f3ba26af3953a23b23dfdcaaab5905eaf5c063134ab26e0573dac18bbff2937479c2d658cff8ded19359fe1414c426dcd99bca93d93d1adfe768110837a7d8deb99359b5049babbfc37534609fff1674c549e1fc3f778579c91a8a10fdd2c75dfef12898353631643ce635ce91bd439ef5a0604afd722a3bd79d7bb65c6b17cc71473c9a65ba1562571aa9cd90fc6d245f2d66cb53530236000af5114bf827938bb76bece6549931f47e87d98ddbab02dabda7bd253fd28e7f7508fd48d0291a399eb1b0cb4a0d286838cdcf782de178a1657ad9373e1ef6d776bf6991bfeab8c1b99f17b7457e397f2afed8c4db005e45564b0b6bb8ba4231e014eaa94e113b981e7e7f854e8bdd849653634b51d10d703fed840a73831e228e737fd707a6649af0c3acb1eca585ff2390d8b714f75f2b5a2fed4b946166e26145a845a417a4ddbb93fa87526a18336c63437dc9561e9ff0b9e82440893553e5cb127b73405828adbc4bcc16e2628bece509adf4b5b4c213e30e47ce2b833cf1ae63b66f98e8b0ab82f4699f81101e8fc45c02c392f73d56a05e393c4ef26cc9cb18bda6f06b3c3c5e17256cdd413b1c7e53a36be6e5f8327ef2d74479b0f758f6867099a7954a25170f0598b88d0178e4264597d539f49db904f5af0c5fbaa0d1d3f6e267d271fd88d651d63a5d076b6c9cb83baeeeb9ee07119d5cc0d3301b8580c6f13f01193fe744accaf23d4998a328ba0153e091ff47e72115790ac2ee8bc2760389f6afa1aad5c6fde22b7e030e54260a66c82b - 06141ef4481120da22db750808004508040ed5274000350629d871600dd0c0a8030a1f90fe5008ceeae08b8c5855501800f9a1740000352b369d0f6a6ccc3241ebaffcf56f1448539de4424794e92d376150a7d779cbe3bddb9ddfcaa8a5516508f0e151f14905709d11cf5731f02d7a17594878a148d3513e3a40cf2b2083d94e4473933919183c689da0d9870cac1045e1177de021dc8cf35876b628efacc94f76c8e5ac1c52bcd063c40c1c7a507a7bfb6e7cbce4573c10de847f154540c455bcd3fa044dde17f833a53b457011148b769418de4fa0cb03f0f0003548307c15c798f83eff4dbd9ed1ac79fc2ab1e4a699a6ebfd7a2ab8aa41f212480dad50f92a41eef9bab19d8f5c535973e283fe09fefbb96d9c714528c5d05fc83d158ffecd438c30a95f38d58d3df3c554d5b3c0fb2fd75ae0fa38de87092bca3a79e7d934b673d7fcc3d2fb8b35874c199dbd82f51fd2903a3dd3195f0d5eac55c260cd63d4dae3ad90c13979401face609ca6608828a49e91cfc4e0861996bbc608ee8f94d7b32253ee99a627d0d14a0660cac7a4a0f814cb43cff81983b4ff2f9bb19933371be4e837bfea8af9f50810994b3c543eee966b178d423a73837533666efc50ff9c3b0ddca442e85505117ad1ee3d618048e77ff5f151e1305d04c0ed4ab12f38e587003d8648fb1c48328f0d97062b9e1d1e298991572bdc488532211f249b87f554d243ece0359d79c501df79feff20b6e64dee4f72d0eeb4258959dbaa0cdcd5b78544935f2bf8fdc8610623f0380cc3443597cfd2d969eb4b9c8c91583c1a09ad40784309daedc300f1231a51658e050d8146c116974dc8e1b80ccdd9de0e809cc5cf099ff31f3ba0aef36291e885750b482b999b1e8a12cc9831641b6c7afcee924cd9d95ecd4571145963f1f1dd65da39ed33274e57d8103139e8ea523c2c51ca3df4ba25f0d762fa708df0308adc600039f07d5f7968b48b921f6e2a5e597722343456d5e836a3478f907dbd26c9ffff11491be61937ce4e951d3e27321c5ced54c0cbe807eb6b555d63303b1e5835d867bed9237e50eca36b953a52c3ace2106cf0a76e616e64e43f3caf1b3e4c36a6f07ef36a8e4d0c459abb835a8937b011cd7fe22c8cb3a1915d3b35ec071d3ea30149024ddcb5cc6688fb1aaefdfa691fcc1e1abb789c93d9a50a3ae5a3c42f962bb1ea18d0ee41f947025dfe11b553710437c553acc05cd71eb8bcf7312ae7ba93e0999a7d250b3534f8a12ffb0863f813e50099b5ab99488bec6c9a7c3ddbabba689d8d5d074ac7a8a51be0dbd24e83946212900bda167da74502df6a4ca19ea0e96dccac3000000600000000b01000000000e3139393437303130323124091c1e3f66dd4380dfb31d6672e8247ca8334d3bafad5222abe08994b3752544f7fff26d145f9096623e882a351abcd8d62ccb381df30e6087034a250896c23148902ab7673325 - 06141ef4481120da22db7508080045080326d525400035062ac271600dd0c0a8030a1f90fe5008cee2608b8c5585501800f9cc090000869d266455aceba6d61bb5545b381d7eb65ef8f78b409dce42d67276a3e6d8e26cc58a5b713dad2e5c2690dbab11f9e3750beb6e6213e6c4b852c664a180f0c81421fdc2e5ee1e79aa094eb225e1d54f10706dd69088247dd5a813991d0a776f28023a45177a21a6dabc9b9180c2e9efcccafb1b6801d4b7a548efc5a523be32fa19ab786fc9eb0ab7c5c4bf9d92d77b95125fe19c4cd36ff5c4f55c4dc375a09b2d5c20ebe1025e8d410ca48e27926f18e71e5c12653d7f57e2ab861a7e07881056b1553b54f69602f18e49ccd2f37e80884b16eaa351eb988ce3d683255b29c19361f2b1c3df9384fc51ca53e62ca849ea9d7f84fe042238094200a80f30587ca1f8e02648d1c227a1609f72dac4fa87be933c89c7b8370882fd78c9395501ec38152a84209062fa5f05da560ffc27c8323b785175f6eb9c097a79aa989c7c635ea8d3b70447fe8dc0fd083fd2e3c587857e7fbe39c9153bbb482b8cc61718e4552b1ba2fe4857e4efc9c8bdbd556a48af3ce635f898f32d2f81063b4614c763126ded43cfec3c9a6ee69ffb22ad07ecdc0da74d2a5525969af87a39ea98ccb7fc26e281ed012ba411b215e6be4ee9510a1b66a0176d2569490c8236b5c8749c54fff7873a535efb9f6b0d087c5b84c7f8f78936f2a068678e3666187ce351965995cb62bd9991b9953f3fedefd37c773733cd19f7da499cf81ce27ada2cfdc9834a571e1bb2966d78ade710a4edd4494770f10304f3456f80302c5526187c8fe0a1ede70ea2d456adc593726445181b27ebf450d1414c502aadde9ef2707ea128e8601e4948fbee57f6c2ebfbeec38cdb48364db069ca62119002ba19570cd6b744669f30565b4ebc3ed470394f6dc85da8a085351b10234dc82bd33543780a730904566620ebcebf56c8ab84545847737d55babb66cccee94054e6fc1ffd0bbf0c801c7e29b477a2e2baae339b37b815a8ce8811c7fb87b8fd3be55a1174f6f55f35b5d42c4d96cfb40053fa55efa31a9ae334248567e06d421d227fb9a30765dc4e85b938aabbcedac32901598bc5772ecde797779c7034c22cd81a - - """.trimIndent() - .trim().split("\n").filterNot { it.isBlank() }.map { - val bytes = it.trim().autoHexToBytes() - if (bytes[0].toInt() == 0) { - bytes - } else bytes.dropTCPHead() - }.flatMap { it.toList() }.toByteArray() - data.read { decodeMultiServerToClientPackets() } -} - -fun ByteReadPacket.decodeMultiServerToClientPackets() { - PacketLogger.enable() - println("=======================处理服务器到客户端客户端=======================") - var count = 0 - while (remaining != 0L) { - processFullPacketWithoutLength(readBytes(readInt() - 4).toReadPacket()) - if (remaining != 0L) { - println() - println() - println() - println() - count++ - } else { - DebugLogger.info("=======================共有 $count 个包=======================") - } - } - println() -} - -private fun processFullPacketWithoutLength(packet: ByteReadPacket) { - packet.debugPrintThis("正在处理").apply { - require(remaining < Int.MAX_VALUE) { "rawInput is too long" } - // login - val flag1 = readInt() - - PacketLogger.verbose("flag1(0A/0B) = ${flag1.toUByte().toUHexString()}") - // 00 00 05 30 - // 00 00 00 0A // flag 1 - // 01 // packet type. 02: sso, 01: uni - // - // 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 40 3C 63 DC A2 8F FC E7 09 66 62 11 A3 5A B6 AB DC 6E A1 CA CF E2 0A 6F A8 6D 36 64 4E 22 4B A9 8A ED 07 7A 0A 9E F3 C7 7B 72 EF C1 C7 6E 9A 28 27 10 F8 2A 7F 37 49 B6 48 35 52 E9 CF 2A B5 F3 26 90 33 68 9B 5A 04 2A 8B F5 78 13 82 FE 3C 13 C4 F9 38 39 0E 02 4C 3D 91 0A 2A 94 3F 9F A6 52 B9 14 89 C5 D9 57 0F 96 F8 0E 7D 32 81 8E 10 DB C0 CA BE C7 3F EC D0 B1 F0 9D A2 4B 9F B3 8D E0 EB 1F 42 52 EA 5E 9E 76 E2 F4 13 9D 0E 7E 6D 0A E3 56 C3 EE 8A 80 24 DE FB 08 82 FB B7 AF CE 2A 69 16 E3 C3 79 5C C7 CD 44 BA AA 08 A2 51 0B 43 31 69 A1 12 D1 AE 48 15 AE 76 E9 AB BB D2 E0 16 03 EB 2D 47 A4 61 24 65 5E CC C5 03 B3 96 3E 7A 39 90 3D DB 63 56 2B 23 85 CE 5F 9E 04 20 45 31 79 7B BF 78 33 77 34 C1 8E 83 B3 50 88 2A 01 C0 C4 E4 BF 2D 0D B9 37 32 AB E0 BB 82 36 B1 4E 51 4B F7 07 6A 12 3E 79 EA 93 3D BD 06 4E AE 1C 49 82 17 14 00 09 59 40 A6 A9 01 56 1A 23 86 A8 33 B3 9A 70 7B 3A C1 F9 31 03 FD DB 4B 5C 7B F9 BB 43 94 65 A0 1C DA 2B 85 AA AD 7B 79 42 F2 EB 25 5E F0 DA B7 E7 AD 4B 25 02 36 BB 78 5F 83 7F F7 78 F0 99 D2 B5 A3 0C 4A 7F 0E B0 A6 C4 99 F7 9E 0B C6 4D FC F5 8D 6B 5F 35 27 36 D3 DB D0 46 C7 10 76 7D 96 91 48 EA 1C B2 B7 D7 2F D2 88 A8 4C 87 D6 A9 40 33 4C 76 C5 48 3E 32 4D C1 C3 7F 5C D9 B3 22 00 88 BE 04 82 64 A9 73 AA E1 65 1A EF 49 B4 54 74 53 FF 75 B6 E9 57 1B 89 2D 6F 2A 6A CE 23 BF 41 CB 55 B3 A0 53 87 AD A0 22 EE 6B 3F 4A 97 23 36 BF 7E 08 2D 0A 9E 2E 4B F2 2E 00 59 EC F1 21 34 45 75 DB 6B F2 EC 65 24 30 69 50 CC 45 78 00 AF C8 F6 3D 8E 03 60 CF CA A1 88 14 18 82 6F 56 58 D0 BC E0 48 FD AA 86 63 CA C1 01 63 07 16 4A 79 79 17 9D 1F E2 40 4B B6 77 6E 44 84 DE BE 02 4C 33 7A F5 2F 93 21 3E 17 62 38 81 95 E6 84 8B 7C C8 7B E2 23 FB 12 4F E8 42 5F 1D 48 92 84 B1 45 FF 69 97 3C 30 C9 09 E8 84 E8 07 0E 17 D4 A1 CC 35 D6 FE 7B D2 9A 44 8B 17 BF E7 D6 98 1D 98 D7 30 BE 55 19 A9 F4 D6 0D E8 18 80 35 85 B6 AB B9 20 32 C7 ED C6 AD A7 AE 19 48 B7 17 02 B3 45 C3 A2 B9 C9 B7 58 B5 8B 4C AF 52 AD A1 E1 62 45 AB 58 26 67 20 C7 64 AA DA 7E F3 70 8B C2 92 69 E3 3E 3E 6F 39 6F 2B 35 35 0F 00 FC 52 B5 5C 5B 73 FE F6 F5 10 55 36 7C 9A 84 FC A6 23 29 4A 75 49 7C 13 1C CA 54 A2 A2 FA 2A 63 A5 4C 9A B4 27 E8 5F 9F 23 96 B2 E7 AA E6 8B E0 E2 6A 75 8A B2 F4 E4 7E 09 E8 22 70 2A 42 8B E3 DC AD E8 A8 A2 92 71 6B A2 12 78 E1 DA CC 70 57 67 F5 B4 52 F3 B4 4C 17 AB 05 33 DA 6E 47 52 C5 B2 B7 9A D2 A8 BC 44 64 D3 26 1A 6B C6 C5 36 1C 2B 8F BD B7 27 91 3E C0 C2 FC 03 41 FE 02 D3 4B B1 E5 5F 5B 50 05 29 BD 3A 64 85 E3 8C FB 11 F2 1D 94 DB D7 78 AF AD 77 A3 9C D4 39 5D 8B EA DF 9D 08 CA 92 7C 5F D5 17 49 0E FA A1 21 1C 9F C3 88 1A DC E7 D8 82 80 85 86 32 99 15 E4 89 BA 91 2B 4B FB 87 EC 44 B4 D9 83 CC 79 77 A4 A0 D0 50 E3 4F 00 E7 DA DA 79 38 1E D8 04 86 16 CD 25 BE BA 76 E4 8C F9 86 91 69 6E C7 A0 EF 6B 44 2B C9 C3 DC 8D 2D 65 60 7A F4 37 02 D4 8F 38 D0 D5 20 30 DE A5 F5 A8 75 C7 EE 0B 0F 1B 88 C2 8A CC 6F 70 1D E4 D8 4E DD 04 A5 5B B8 04 B1 29 42 08 92 19 78 E2 26 EB 6B 07 49 DE 8A AF A3 41 72 1D E2 3C 62 0F 7E 7B DE A3 0F 71 8C 5D EC E9 96 96 45 A9 39 33 8A 87 C9 93 CE 3B 6D 75 50 21 1F 4C 03 E9 A7 AD 03 0F 5E A9 EE 60 CC EA 05 4F DF E1 B1 13 A6 7D C7 B9 37 58 53 3B 06 1A AD 98 E5 06 D9 74 2A B1 96 75 DE A6 B7 89 25 53 2A A3 07 B6 70 C6 86 1F 59 EB 53 08 57 6E 86 D7 A1 5C DB 26 D7 86 3E 97 BB FD 6A 0A 4C E1 81 B9 4C C1 A0 49 89 57 29 E0 CD 79 6F 0A 46 C1 C6 62 75 49 C6 9A B9 22 75 EE 10 C7 56 E6 D5 DE 4D EC 89 5A 6F AC 60 0F B3 CC 37 9E F2 BE 49 A7 77 3C 05 AE 92 66 C8 BE 16 E5 35 17 24 18 A5 CE B8 BB AE CD 88 DE 01 53 40 84 E0 06 C6 77 96 09 DF D7 76 3B CA C9 B5 B2 91 95 07 54 6F 51 EB 12 58 16 8A AF C3 E3 B9 4A EC 25 A5 D1 19 59 72 F5 E3 4F 7C 40 B2 D0 4E 9F 50 13 FB 86 C3 6A 88 32 5B 67 EC 4F 0E 0B 31 F8 0C 02 6C CE 8D 50 55 A2 B3 57 73 7C 78 D3 43 1F 48 33 51 E7 0A D0 6D 46 71 4A AD 66 50 F9 96 11 4F A5 5B 3C A0 3E 46 D2 CB 3B A1 03 84 9C 8E 4E 2D 83 69 2E 17 9B F8 36 63 F1 93 CA F9 32 57 2B AB 4E 14 A3 5A F1 39 B0 3F 0F 99 CC 9B FB 7E BC 0A AA C9 65 3C C8 B4 B0 1F - val flag2 = readByte().toInt() - PacketLogger.verbose("包类型(flag2) = $flag2. (可能是 ${if (flag2 == 2) "sso" else "uni"})") - - val flag3 = readByte().toInt() - - readString(readInt() - 4)//uin - - //debugPrint("remaining") - - (if (flag2 == 2) { - //PacketLogger.verbose("SSO, 尝试使用 16 zero 解密.") - kotlin.runCatching { - decryptBy(DECRYPTER_16_ZERO).also { PacketLogger.verbose("成功使用 16 zero 解密") } - } - } else { - //PacketLogger.verbose("Uni, 尝试使用 d2Key 解密.") - kotlin.runCatching { - decryptBy(D2Key).also { PacketLogger.verbose("成功使用 d2Key 解密") } - } - }).getOrElse { - PacketLogger.verbose("解密失败, 尝试其他各种key") - this.readBytes().tryDecryptOrNull()?.toReadPacket() - }?.debugPrintThis("sso/uni body=")?.let { - if (flag1 == 0x0A) { - parseSsoFrame(flag3, it) - } else { - parseSsoFrame(flag3, it) - } - }?.let { - val bytes = it.data.readBytes() - if (flag2 == 2 && it.packetFactory != null) { - PacketLogger.debug("Oicq Reuqest= " + bytes.toUHexString()) - try { - bytes.toReadPacket().parseOicqResponse { - debugIfFail { - if ((it.packetFactory as? OutgoingPacketFactory<*>)?.commandName == "wtlogin.login") { - DebugLogger.info("服务器发来了 wtlogin.login. 正在解析 key") - try { - val subCommand = readUShort().toInt() - println("subCommand=$subCommand") - val type = readUByte().toInt() - println("type=$type") - - discardExact(2) - val tlvMap: Map<Int, ByteArray> = this.readTLVMap() - println("tlvMap: ") - tlvMap.forEach { - println(it.key.toShort().toUHexString() + " = " + it.value.toUHexString()) - } - - tlvMap[0x119]?.let { t119Data -> - t119Data.decryptBy(tgtgtKey).toReadPacket().debugPrintThis("0x119data").apply { - discardExact(2) // always discarded. 00 1C - // 00 1C - // 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00 - val tlvMap119 = this.readTLVMap() - - userStKey = tlvMap119.getOrEmpty(0x10e) - wtSessionTicketKey = tlvMap119.getOrEmpty(0x133) - D2Key = tlvMap119.getOrEmpty(0x305) - DebugLogger.info("userStKey=${userStKey.toUHexString()}") - DebugLogger.info("wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}") - DebugLogger.info("D2Key=${D2Key.toUHexString()}") - } - } - } catch (e: Exception) { - e.printStackTrace() - } - } - } - } - } catch (e: Exception) { - e.printStackTrace() - } - } else // always discarded. 00 1C - // 00 1C - // 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00 - { - PacketLogger.debug("不是oicq response(可能是 UNI/PB)= " + bytes.toUHexString()) - } - } ?: inline { - PacketLogger.error("任何key都无法解密") - return - } - } -} - -private fun Map<Int, ByteArray>.getOrEmpty(key: Int): ByteArray { - return this[key] ?: byteArrayOf() -} - -var randomKey: ByteArray = byteArrayOf() -private fun ByteReadPacket.parseOicqResponse(body: ByteReadPacket.() -> Unit) { - readIoBuffer(readInt() - 4).withUse { - check(readByte().toInt() == 2) - this.discardExact(2) // 27 + 2 + body.size - this.discardExact(2) // const, =8001 - this.readUShort() // commandId - this.readShort() // const, =0x0001 - this.readUInt().toLong() // qq - val encryptionMethod = this.readUShort().toInt() - - this.discardExact(1) // const = 0 - @Suppress("UNUSED_VARIABLE") - val packet = when (encryptionMethod) { - 4 -> { // peer public key, ECDH - var data = this.decryptBy(shareKeyCalculatedByConstPubKey, 0, this.readRemaining - 1) - data.read { - println("第一层解密: ${data.toUHexString()}") - val peerShareKey = ECDH.calculateShareKey(loadPrivateKey(ecdhPrivateKeyS), readUShortLVByteArray().adjustToPublicKey()) - body(this.decryptBy(peerShareKey)) - } - } - 0 -> { - val data = if (0 == 0) { - ByteArrayPool.useInstance { byteArrayBuffer -> - val size = this.readRemaining - 1 - this.readFully(byteArrayBuffer, 0, size) - - runCatching { - byteArrayBuffer.decryptBy(shareKeyCalculatedByConstPubKey, size) - }.getOrElse { - byteArrayBuffer.decryptBy(randomKey, size) - } // 这里实际上应该用 privateKey(另一个random出来的key) - } - } else { - this.decryptBy(randomKey, 0, this.readRemaining - 1) - } - - PacketLogger.info("OicqRequest, Real body=" + data.toUHexString()) - body(data.toReadPacket()) - } - else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod") - } - } -} - -fun ByteReadPacket.readIoBuffer( - n: Int = remaining.toInt()//not that safe but adequate -): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) } - -/** - * 解析 SSO 层包装 - */ -@UseExperimental(ExperimentalUnsignedTypes::class) -private fun parseSsoFrame(flag3: Int, input: ByteReadPacket): KnownPacketFactories.IncomingPacket<*> { - val commandName: String - val ssoSequenceId: Int - - // head - input.readIoBuffer(input.readInt() - 4).withUse { - ssoSequenceId = readInt() - PacketLogger.verbose("sequenceId = $ssoSequenceId") - check(readInt() == 0) - val extraData = readBytes(readInt() - 4) - PacketLogger.verbose("sso(inner)extraData = ${extraData.toUHexString()}") - - commandName = readString(readInt() - 4) - DebugLogger.warning("commandName=$commandName") - readBytes(readInt() - 4) // unknown, sessionId? - //if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") - - check(readInt() == 0) - } - - // body - val packetFactory = KnownPacketFactories.findPacketFactory(commandName) - - if (packetFactory == null) { - println("找不到包 PacketFactory") - PacketLogger.verbose("传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}") - } - - var data = input.readBytes() - if (flag3 == 1) { - data = data.unzip(offset = 4) - } else { - - } - return KnownPacketFactories.IncomingPacket(packetFactory, ssoSequenceId, data.toReadPacket(), commandName) -} - - -/** - * 解析 Uni 层包装 - */ -@UseExperimental(ExperimentalUnsignedTypes::class) -private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingPacket<*> { - // 00 00 00 30 00 01 2F 7C 00 00 00 00 00 00 00 04 00 00 00 14 67 78 68 72 65 70 6F 72 74 2E 72 65 70 6F 72 74 00 00 00 08 66 82 D3 0B 00 00 00 00 - // 00 00 00 06 08 00 - - - //00 00 00 2D 00 01 2F 7E 00 00 00 00 00 00 00 04 00 00 00 11 4F 69 64 62 53 76 63 2E 30 78 35 39 66 00 00 00 08 66 82 D3 0B 00 00 00 00 - // 00 00 00 19 08 9F 0B 10 01 18 00 22 0C 10 00 18 00 20 00 A8 01 00 A0 06 01 - - val commandName: String - val ssoSequenceId: Int - - // head - input.readIoBuffer(input.readInt() - 4).withUse { - ssoSequenceId = readInt() - PacketLogger.verbose("sequenceId = $ssoSequenceId") - check(readInt() == 0) - val extraData = readBytes(readInt() - 4) - PacketLogger.verbose("sso(inner)extraData = ${extraData.toUHexString()}") - - commandName = readString(readInt() - 4) - DebugLogger.warning("commandName=$commandName") - readBytes(readInt() - 4) // unknown - //if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") - - check(readInt() == 0) - } - - // body - val packetFactory = KnownPacketFactories.findPacketFactory(commandName) - - if (packetFactory == null) { - println("找不到包 PacketFactory") - PacketLogger.verbose("传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}") - } - return KnownPacketFactories.IncomingPacket(packetFactory, ssoSequenceId, input, commandName) -} - -private inline fun <R> inline(block: () -> R): R = block() diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt deleted file mode 100644 index 749cb91a0..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:Suppress("DEPRECATION") - -package androidPacketTests - -import net.mamoe.mirai.utils.cryptor.decryptBy -import org.bouncycastle.jce.provider.JCEECPrivateKey -import org.bouncycastle.jce.spec.ECParameterSpec -import org.bouncycastle.jce.spec.ECPrivateKeySpec -import org.bouncycastle.math.ec.ECConstants -import org.bouncycastle.math.ec.ECCurve -import org.bouncycastle.util.encoders.Hex -import java.math.BigInteger -import java.security.interfaces.ECPrivateKey - -fun ByteArray.decryptBy16Zero() = this.decryptBy(ByteArray(16)) - -fun ByteArray.dropTCPHead(): ByteArray = this.drop(16 * 3 + 6).toByteArray() - - -@Suppress("LocalVariableName") -fun loadPrivateKey(s: String): ECPrivateKey { - fun fromHex( - hex: String - ): BigInteger { - return BigInteger(1, Hex.decode(hex)) - } - - // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 - // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 - val p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37") - val a = ECConstants.ZERO - val b = BigInteger.valueOf(3) - val n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D") - val h = BigInteger.valueOf(1) - - val curve: ECCurve = ECCurve.Fp(p, a, b) - //ECPoint G = curve.decodePoint(Hex.decode("03" - //+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D")); - //ECPoint G = curve.decodePoint(Hex.decode("03" -//+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D")); - val G = curve.decodePoint( - Hex.decode( - "04" - + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" - + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D" - ) - ) - - return JCEECPrivateKey( - "EC", - ECPrivateKeySpec( - fromHex(s), - ECParameterSpec(curve, G, n, h) - ) - ) - // return KeyFactory.getInstance("ECDH").generatePrivate(PKCS8EncodedKeySpec(s)) -} diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt index 14ee4b490..a782d38f3 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt @@ -17,7 +17,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.qqandroid.io.JceOutput import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.buildJcePacket -import net.mamoe.mirai.utils.cryptor.contentToString +import net.mamoe.mirai.utils.contentToString import net.mamoe.mirai.utils.io.toUHexString import kotlin.test.Test import kotlin.test.assertEquals diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt new file mode 100644 index 000000000..7c9793da7 --- /dev/null +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "NO_REFLECTION_IN_CLASS_PATH") + +package net.mamoe.mirai.utils.cryptor + +import net.mamoe.mirai.utils.MiraiDebugAPI + +// ProtoBuf utilities + + +@Suppress("FunctionName", "SpellCheckingInspection") +/* + * Type Meaning Used For + * 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum + * 1 64-bit fixed64, sfixed64, double + * 2 Length-delimi string, bytes, embedded messages, packed repeated fields + * 3 Start group Groups (deprecated) + * 4 End group Groups (deprecated) + * 5 32-bit fixed32, sfixed32, float + * + * https://www.jianshu.com/p/f888907adaeb + */ +@MiraiDebugAPI +fun ProtoFieldId(serializedId: UInt): ProtoFieldId = + ProtoFieldId( + protoFieldNumber(serializedId), + protoType(serializedId) + ) + +@MiraiDebugAPI +data class ProtoFieldId( + val fieldNumber: Int, + val type: ProtoType +) { + override fun toString(): String = "$type $fieldNumber" +} + +@Suppress("SpellCheckingInspection") +@MiraiDebugAPI +enum class ProtoType(val value: Byte, private val typeName: String) { + /** + * int32, int64, uint32, uint64, sint32, sint64, bool, enum + */ + VAR_INT(0x00, "varint"), + + /** + * fixed64, sfixed64, double + */ + BIT_64(0x01, " 64bit"), + + /** + * string, bytes, embedded messages, packed repeated fields + */ + LENGTH_DELIMI(0x02, "delimi"), + + /** + * Groups (deprecated) + */ + START_GROUP(0x03, "startg"), + + /** + * Groups (deprecated) + */ + END_GROUP(0x04, " endg"), + + /** + * fixed32, sfixed32, float + */ + BIT_32(0x05, " 32bit"), + ; + + override fun toString(): String = this.typeName + + companion object { + fun valueOf(value: Byte): ProtoType = values().firstOrNull { it.value == value } ?: error("Unknown ProtoType $value") + } +} + +/** + * 由 ProtoBuf 序列化后的 id 得到类型 + * + * serializedId = (fieldNumber << 3) | wireType + */ +@MiraiDebugAPI +fun protoType(number: UInt): ProtoType = + ProtoType.valueOf(number.toInt().shl(29).ushr(29).toByte()) + +/** + * ProtoBuf 序列化后的 id 转为序列前标记的 id + * + * serializedId = (fieldNumber << 3) | wireType + */ +@MiraiDebugAPI +fun protoFieldNumber(number: UInt): Int = number.toInt().ushr(3) diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt index c245d0cde..0f0d11343 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt @@ -15,23 +15,6 @@ import net.mamoe.mirai.network.BotNetworkHandler import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -/** - * 在各平台实现的默认的验证码处理器. - */ -actual var defaultLoginSolver: LoginSolver = object : LoginSolver() { - override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? { - error("should be implemented manually by you") - } - - override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? { - error("should be implemented manually by you") - } - - override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? { - error("should be implemented manually by you") - } -} - @Suppress("ClassName", "PropertyName") actual open class BotConfiguration actual constructor() { /** @@ -76,7 +59,7 @@ actual open class BotConfiguration actual constructor() { /** * 验证码处理器 */ - actual var loginSolver: LoginSolver = defaultLoginSolver + actual var loginSolver: LoginSolver = LoginSolver.Default actual companion object { /** @@ -115,4 +98,31 @@ inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath: */ @BotConfigurationDsl companion object ByDeviceDotJson +} + +/** + * 验证码, 设备锁解决器 + */ +actual abstract class LoginSolver { + actual abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? + actual abstract suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? + actual abstract suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? + + actual companion object { + actual val Default: LoginSolver + get() = object : LoginSolver() { + override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? { + error("should be implemented manually by you") + } + + override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? { + error("should be implemented manually by you") + } + + override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? { + error("should be implemented manually by you") + } + } + } + } \ No newline at end of file diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt index b153e249b..35d338f7f 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt @@ -1,5 +1,7 @@ package net.mamoe.mirai.utils +import android.os.Build + private var isAddSuppressedSupported: Boolean = true @MiraiInternalAPI @@ -9,7 +11,11 @@ actual fun Throwable.addSuppressed(e: Throwable) { return } try { - this.addSuppressed(e) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + this.addSuppressed(e) + } else { + isAddSuppressedSupported = false + } } catch (e: Exception) { isAddSuppressedSupported = false } diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt deleted file mode 100644 index aa7da3584..000000000 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -package net.mamoe.mirai.utils.cryptor - -import net.mamoe.mirai.utils.MiraiDebugAPI -import java.lang.reflect.Field -import kotlin.reflect.full.allSuperclasses - - -@MiraiDebugAPI -actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)?): String { - return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" + - this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") } - .distinctBy { it.name } - .filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" } - .joinToStringPrefixed( - prefix = prefix - ) { - it.isAccessible = true - if (filter != null) { - kotlin.runCatching { - if (!filter(it.name, it.get(this))) return@joinToStringPrefixed "" - } - } - it.name + "=" + kotlin.runCatching { - val value = it.get(this) - if (value == this) "<this>" - else value.contentToString(prefix) - }.getOrElse { "<!>" } - } + "\n$prefix}" -} - -internal fun Any.allFieldsFromSuperClassesMatching(classFilter: (Class<out Any>) -> Boolean): Sequence<Field> { - return (this::class.java.takeIf(classFilter)?.declaredFields?.asSequence() ?: sequenceOf<Field>()) + this::class.allSuperclasses - .asSequence() - .map { it.java } - .filter(classFilter) - .flatMap { it.declaredFields.asSequence() } -} \ No newline at end of file diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt index 71ff9515b..ed02843f3 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt @@ -20,8 +20,6 @@ import java.nio.channels.DatagramChannel import java.nio.channels.ReadableByteChannel import java.nio.channels.WritableByteChannel -actual typealias ClosedChannelException = java.nio.channels.ClosedChannelException - /** * 多平台适配的 DatagramChannel. */ diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt index 2ba68a6ba..3a74ec48a 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt @@ -75,6 +75,7 @@ private inline fun InputStream.readInSequence(block: (Int) -> Unit) { } } +@UseExperimental(MiraiInternalAPI::class) actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { this.checkOffsetAndLength(offset, length) if (length == 0) return ByteArray(0) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt index 79ef2ae49..6d72b984b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt @@ -22,7 +22,6 @@ import net.mamoe.mirai.network.ForceOfflineException import net.mamoe.mirai.network.LoginFailedException import net.mamoe.mirai.network.closeAndJoin import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.io.logStacktrace import kotlin.coroutines.CoroutineContext /* @@ -144,17 +143,16 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( } suspend fun doInit() { - repeat(2) { - try { - _network.init() - return - } catch (e: Exception) { - e.logStacktrace() + tryNTimesOrException(2) { + if (it != 0) { + delay(3000) + logger.warning("Init failed. Retrying in 3s...") } - logger.warning("Init failed. Retrying in 3s...") - delay(3000) + _network.init() + }?.let { + network.logger.error(it) + logger.error("cannot init. some features may be affected") } - logger.error("cannot init. some features may be affected") } logger.info("Initializing BotNetworkHandler") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt index 8ae42e95d..6a0c372c4 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt @@ -17,7 +17,6 @@ import net.mamoe.mirai.event.EventDisabled import net.mamoe.mirai.event.Listener import net.mamoe.mirai.event.ListeningStatus import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.io.logStacktrace import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext import kotlin.jvm.JvmField @@ -65,8 +64,8 @@ internal class Handler<in E : Event> MiraiLogger.warning( """Event processing: An exception occurred but no CoroutineExceptionHandler found, either in coroutineContext from Handler job, or in subscriberContext""".trimIndent() + , e ) - e.logStacktrace("Event processing(No CoroutineExceptionHandler found)") } // this.complete() // do not `completeExceptionally`, otherwise parentJob will fai`l. // ListeningStatus.STOPPED diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt index 26945a705..7057fe3ee 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt @@ -34,7 +34,6 @@ import kotlin.coroutines.EmptyCoroutineContext * @see CoroutineScope.incoming */ @UseExperimental(ExperimentalContracts::class) -@MessageDsl inline fun <R> CoroutineScope.subscribeMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline listeners: MessageSubscribersBuilder<MessagePacket<*, *>>.() -> R @@ -60,7 +59,6 @@ inline fun <R> CoroutineScope.subscribeMessages( * @see CoroutineScope.incoming */ @UseExperimental(ExperimentalContracts::class) -@MessageDsl inline fun <R> CoroutineScope.subscribeGroupMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline listeners: MessageSubscribersBuilder<GroupMessage>.() -> R @@ -81,7 +79,6 @@ inline fun <R> CoroutineScope.subscribeGroupMessages( * @see CoroutineScope.incoming */ @UseExperimental(ExperimentalContracts::class) -@MessageDsl inline fun <R> CoroutineScope.subscribeFriendMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline listeners: MessageSubscribersBuilder<FriendMessage>.() -> R @@ -102,7 +99,6 @@ inline fun <R> CoroutineScope.subscribeFriendMessages( * @see CoroutineScope.incoming */ @UseExperimental(ExperimentalContracts::class) -@MessageDsl inline fun <R> Bot.subscribeMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline listeners: MessageSubscribersBuilder<MessagePacket<*, *>>.() -> R @@ -125,7 +121,6 @@ inline fun <R> Bot.subscribeMessages( * @see CoroutineScope.incoming */ @UseExperimental(ExperimentalContracts::class) -@MessageDsl inline fun <R> Bot.subscribeGroupMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline listeners: MessageSubscribersBuilder<GroupMessage>.() -> R @@ -146,7 +141,6 @@ inline fun <R> Bot.subscribeGroupMessages( * @see CoroutineScope.incoming */ @UseExperimental(ExperimentalContracts::class) -@MessageDsl inline fun <R> Bot.subscribeFriendMessages( coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline listeners: MessageSubscribersBuilder<FriendMessage>.() -> R diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt index 3ebb748ec..5a59722cd 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt @@ -18,18 +18,17 @@ import kotlin.jvm.JvmStatic /** * 验证码, 设备锁解决器 */ -abstract class LoginSolver { +expect abstract class LoginSolver { abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? abstract suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? abstract suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? -} -/** - * 在各平台实现的默认的验证码处理器. - */ -expect var defaultLoginSolver: LoginSolver + companion object { + val Default: LoginSolver + } +} /** * [Bot] 配置 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt index 94cb8cc45..25a3ebfbd 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt @@ -29,6 +29,7 @@ import kotlin.jvm.JvmName * 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst] */ suspend fun ByteReadChannel.copyTo(dst: OutputStream) { + @UseExperimental(MiraiInternalAPI::class) ByteArrayPool.useInstance { do { val size = this.readAvailable(it) @@ -41,6 +42,7 @@ suspend fun ByteReadChannel.copyTo(dst: OutputStream) { * 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst] */ suspend fun ByteReadChannel.copyTo(dst: Output) { + @UseExperimental(MiraiInternalAPI::class) ByteArrayPool.useInstance { do { val size = this.readAvailable(it) @@ -72,6 +74,7 @@ suspend fun ByteReadChannel.copyTo(dst: kotlinx.coroutines.io.ByteWriteChannel) */ suspend fun ByteReadChannel.copyAndClose(dst: OutputStream) { try { + @UseExperimental(MiraiInternalAPI::class) ByteArrayPool.useInstance { do { val size = this.readAvailable(it) @@ -88,6 +91,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: OutputStream) { */ suspend fun ByteReadChannel.copyAndClose(dst: Output) { try { + @UseExperimental(MiraiInternalAPI::class) ByteArrayPool.useInstance { do { val size = this.readAvailable(it) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt new file mode 100644 index 000000000..a5a2d155e --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "NO_REFLECTION_IN_CLASS_PATH") + +package net.mamoe.mirai.utils + +import net.mamoe.mirai.utils.io.toUHexString +import kotlin.reflect.KClass +import kotlin.reflect.KProperty0 + +private val indent: String = " ".repeat(4) + +/** + * 将所有元素加入转换为多行的字符串表示. + */ +@MiraiDebugAPI +internal fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String { + return this.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent", transform = transform) +} + +/** + * 将内容格式化为较可读的字符串输出. + * + * 各数字类型及其无符号类型: 十六进制表示 + 十进制表示. e.g. `0x1000(4096)` + * [ByteArray] 和 [UByteArray]: 十六进制表示, 通过 [ByteArray.toUHexString] + * [Iterable], [Iterator], [Sequence]: 调用各自的 joinToString. + * [Map]: 多行输出. 每行显示一个值. 递归调用 [_miraiContentToString]. 嵌套结构将会以缩进表示 + * `data class`: 调用其 [toString] + * 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [_miraiContentToString]. 嵌套结构将会以缩进表示 + */ +@Suppress("FunctionName") // 这样就不容易被 IDE 提示 +@MiraiDebugAPI("Extremely slow") +//@Suppress("Unsupported") // false positive +fun Any?._miraiContentToString(prefix: String = ""): String = when (this) { + is Unit -> "Unit" + is UInt -> "0x" + this.toUHexString("") + "($this)" + is UByte -> "0x" + this.toUHexString() + "($this)" + is UShort -> "0x" + this.toUHexString("") + "($this)" + is ULong -> "0x" + this.toUHexString("") + "($this)" + is Int -> "0x" + this.toUHexString("") + "($this)" + is Byte -> "0x" + this.toUHexString() + "($this)" + is Short -> "0x" + this.toUHexString("") + "($this)" + is Long -> "0x" + this.toUHexString("") + "($this)" + + is Boolean -> if (this) "true" else "false" + + is ByteArray -> { + if (this.size == 0) "<Empty ByteArray>" + else this.toUHexString() + } + is UByteArray -> { + if (this.size == 0) "<Empty UByteArray>" + else this.toUHexString() + } + is ShortArray -> { + if (this.size == 0) "<Empty ShortArray>" + else this.iterator()._miraiContentToString() + } + is IntArray -> { + if (this.size == 0) "<Empty IntArray>" + else this.iterator()._miraiContentToString() + } + is LongArray -> { + if (this.size == 0) "<Empty LongArray>" + else this.iterator()._miraiContentToString() + } + is FloatArray -> { + if (this.size == 0) "<Empty FloatArray>" + else this.iterator()._miraiContentToString() + } + is DoubleArray -> { + if (this.size == 0) "<Empty DoubleArray>" + else this.iterator()._miraiContentToString() + } + is UShortArray -> { + if (this.size == 0) "<Empty ShortArray>" + else this.iterator()._miraiContentToString() + } + is UIntArray -> { + if (this.size == 0) "<Empty IntArray>" + else this.iterator()._miraiContentToString() + } + is ULongArray -> { + if (this.size == 0) "<Empty LongArray>" + else this.iterator()._miraiContentToString() + } + is Array<*> -> { + if (this.size == 0) "<Empty Array>" + else this.iterator()._miraiContentToString() + } + is BooleanArray -> { + if (this.size == 0) "<Empty BooleanArray>" + else this.iterator()._miraiContentToString() + } + + is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } + is Iterator<*> -> this.asSequence().joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } + is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } + is Map<*, *> -> this.entries.joinToString( + prefix = "{", + postfix = "}" + ) { it.key._miraiContentToString(prefix) + "=" + it.value._miraiContentToString(prefix) } + else -> { + if (this == null) "null" + else if (this::class.isData) this.toString() + else { + if (this::class.qualifiedName?.startsWith("net.mamoe.mirai.") == true) { + this.contentToStringReflectively(prefix + indent) + } else this.toString() + /* + (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + "{\n" + + this::class.members.asSequence().filterIsInstance<KProperty<*>>().filter { !it.isSuspend && it.visibility == KVisibility.PUBLIC } + .joinToStringPrefixed( + prefix = indent + ) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(indent) }.getOrElse { "<!>" } } + */ + } + } +} + +@MiraiDebugAPI +private fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)? = null): String { + val newPrefix = "$prefix " + return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" + + this.allMembersFromSuperClassesMatching { it.simpleName?.startsWith("net.mamoe.mirai") == true } + .distinctBy { it.name } + .filterNot { it.name.contains("$") || it.name == "Companion" || it.isConst || it.name == "serialVersionUID" } + .mapNotNull { + val value = it.get() + if (filter != null) { + kotlin.runCatching { + if (!filter(it.name, value)) + return@mapNotNull it.name to value + } + } + null + } + .joinToStringPrefixed( + prefix = newPrefix + ) { (name: String, value: Any?) -> + "$name=" + kotlin.runCatching { + if (value == this) "<this>" + else value._miraiContentToString(newPrefix) + }.getOrElse { "<!>" } + } + "\n$prefix}" +} + +private fun Any.thisClassAndSuperclassSequence(): Sequence<KClass<out Any>> { + return sequenceOf(this::class) + + this::class.supertypes.asSequence() + .mapNotNull { type -> type.classifier?.takeIf { it is KClass<*> } as? KClass<out Any> } +} + +private fun Any.allMembersFromSuperClassesMatching(classFilter: (KClass<out Any>) -> Boolean): Sequence<KProperty0<*>> { + return this.thisClassAndSuperclassSequence() + .filter { classFilter(it) } + .map { it.members } + .flatMap { it.asSequence() } + .mapNotNull { it as? KProperty0<*> } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt index ab25f8002..3e43b3fde 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt @@ -10,8 +10,8 @@ package net.mamoe.mirai.utils.cryptor import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.IoBuffer import kotlinx.io.pool.useInstance +import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.toByteArray import net.mamoe.mirai.utils.io.toUHexString @@ -20,7 +20,6 @@ import kotlin.experimental.xor import kotlin.jvm.JvmStatic import kotlin.random.Random - /** * 解密错误 */ @@ -29,98 +28,52 @@ class DecryptionFailedException : Exception { constructor(message: String?) : super(message) } - -// region encrypt - /** - * 使用 [key] 解密 [this] + * TEA 算法加密解密工具类. * - * @param key 长度至少为 16 - * @throws DecryptionFailedException 解密错误时 + * **注意**: 此为 Mirai 内部 API. 它可能会在任何时刻被改变. */ -fun ByteArray.encryptBy(key: ByteArray, length: Int = this.size): ByteArray = - TEA.encrypt(this, key, sourceLength = length) +@MiraiInternalAPI +object TEA { + // TODO: 2020/2/28 使用 stream 式输入以避免缓存 -/** - * 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密. - * - * @param key 长度至少为 16 - * @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程 - * @throws DecryptionFailedException 解密错误时 - */ -inline fun ByteReadPacket.encryptBy(key: ByteArray, offset: Int = 0, length: Int = remaining.toInt() - offset, consumer: (ByteArray) -> Unit) { - ByteArrayPool.useInstance { - this.readFully(it, offset, length) - consumer(it.encryptBy(key, length = length)) + /** + * 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密. + * + * @param key 长度至少为 16 + * @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程 + * @throws DecryptionFailedException 解密错误时 + */ + inline fun encrypt( + receiver: ByteReadPacket, + key: ByteArray, + offset: Int = 0, + length: Int = receiver.remaining.toInt() - offset, + consumer: (ByteArray) -> Unit + ) { + ByteArrayPool.useInstance { + receiver.readFully(it, offset, length) + consumer(encrypt(it, key, length = length)) + } } -} -// endregion + @JvmStatic + fun decrypt(receiver: ByteReadPacket, key: ByteArray, offset: Int = 0, length: Int = (receiver.remaining - offset).toInt()): ByteReadPacket = + decryptAsByteArray(receiver, key, offset, length) { data -> ByteReadPacket(data) } - -// region decrypt - -/** - * 使用 [key] 解密 [this]. - * - * @param key 固定长度 16 - * @throws DecryptionFailedException 解密错误时 - */ -fun ByteArray.decryptBy(key: ByteArray, length: Int = this.size): ByteArray = - TEA.decrypt(checkDataLengthAndReturnSelf(length), key, sourceLength = length) - -/** - * 使用 [key] 解密 [this]. - * [key] 将会被读取掉前 16 个字节 - * 将会使用 [ByteArrayPool] 来缓存 [key]. - * - * @param key 长度至少为 16 - * @throws DecryptionFailedException 解密错误时 - */ -fun ByteArray.decryptBy(key: IoBuffer, length: Int = this.size): ByteArray { - checkDataLengthAndReturnSelf(length) - return ByteArrayPool.useInstance { keyBuffer -> - key.readFully(keyBuffer, 0, key.readRemaining) - TEA.decrypt(this, keyBuffer, sourceLength = length) + inline fun <R> decryptAsByteArray( + receiver: ByteReadPacket, + key: ByteArray, + offset: Int = 0, + length: Int = (receiver.remaining - offset).toInt(), + consumer: (ByteArray) -> R + ): R { + return ByteArrayPool.useInstance { + receiver.readFully(it, offset, length) + consumer(decrypt(it, key, length)) + }.also { receiver.close() } } -} -/** - * 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 解密. - * - * @param key 长度至少为 16 - * @throws DecryptionFailedException 解密错误时 - */ -fun IoBuffer.decryptBy(key: ByteArray, offset: Int = 0, length: Int = readRemaining - offset): ByteArray { - return ByteArrayPool.useInstance { - this.readFully(it, offset, length) - it.checkDataLengthAndReturnSelf(length) - TEA.decrypt(it, key, length) - } -} - -// endregion - -// region ByteReadPacket extension - -fun ByteReadPacket.decryptBy(key: ByteArray, offset: Int = 0, length: Int = (this.remaining - offset).toInt()): ByteReadPacket = decryptAsByteArray(key, offset, length) { data -> ByteReadPacket(data) } - -fun ByteReadPacket.decryptBy(key: IoBuffer, offset: Int = 0, length: Int = (this.remaining - offset).toInt()): ByteReadPacket = decryptAsByteArray(key, offset, length) { data -> ByteReadPacket(data) } - -inline fun <R> ByteReadPacket.decryptAsByteArray(key: ByteArray, offset: Int = 0, length: Int = (this.remaining - offset).toInt(), consumer: (ByteArray) -> R): R = - ByteArrayPool.useInstance { - readFully(it, offset, length) - consumer(it.decryptBy(key, length)) - }.also { close() } - -inline fun <R> ByteReadPacket.decryptAsByteArray(key: IoBuffer, offset: Int = 0, length: Int = (this.remaining - offset).toInt(), consumer: (ByteArray) -> R): R = - ByteArrayPool.useInstance { - readFully(it, offset, length) - consumer(it.decryptBy(key, length)) - }.also { close() } - -// endregion -private object TEA { private const val UINT32_MASK = 0xffffffffL private fun doOption(data: ByteArray, key: ByteArray, length: Int, encrypt: Boolean): ByteArray { @@ -345,15 +298,25 @@ private object TEA { private fun fail(): Nothing = throw DecryptionFailedException() - @PublishedApi + /** + * 使用 [key] 加密 [source] + * + * @param key 长度至少为 16 + * @throws DecryptionFailedException 解密错误时 + */ @JvmStatic - internal fun encrypt(source: ByteArray, key: ByteArray, sourceLength: Int = source.size): ByteArray = - doOption(source, key, sourceLength, true) + fun encrypt(source: ByteArray, key: ByteArray, length: Int = source.size): ByteArray = + doOption(source, key, length, true) - @PublishedApi + /** + * 使用 [key] 解密 [source] + * + * @param key 长度至少为 16 + * @throws DecryptionFailedException 解密错误时 + */ @JvmStatic - internal fun decrypt(source: ByteArray, key: ByteArray, sourceLength: Int = source.size): ByteArray = - doOption(source, key, sourceLength, false) + fun decrypt(source: ByteArray, key: ByteArray, length: Int = source.size): ByteArray = + doOption(source, key, length, false) private fun ByteArray.pack(offset: Int, len: Int): Long { var result: Long = 0 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt deleted file mode 100644 index 61dc928be..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "NO_REFLECTION_IN_CLASS_PATH") - -package net.mamoe.mirai.utils.cryptor - -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.readBytes -import kotlinx.io.core.readUInt -import kotlinx.io.core.readULong -import net.mamoe.mirai.utils.MiraiDebugAPI -import net.mamoe.mirai.utils.MiraiExperimentalAPI -import net.mamoe.mirai.utils.io.* -import kotlin.jvm.JvmStatic - -// ProtoBuf utilities - - -@Suppress("FunctionName", "SpellCheckingInspection") -/* - * Type Meaning Used For - * 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum - * 1 64-bit fixed64, sfixed64, double - * 2 Length-delimi string, bytes, embedded messages, packed repeated fields - * 3 Start group Groups (deprecated) - * 4 End group Groups (deprecated) - * 5 32-bit fixed32, sfixed32, float - * - * https://www.jianshu.com/p/f888907adaeb - */ -@MiraiDebugAPI -fun ProtoFieldId(serializedId: UInt): ProtoFieldId = - ProtoFieldId( - protoFieldNumber(serializedId), - protoType(serializedId) - ) - -@MiraiDebugAPI -data class ProtoFieldId( - val fieldNumber: Int, - val type: ProtoType -) { - override fun toString(): String = "$type $fieldNumber" -} - -@Suppress("SpellCheckingInspection") -@MiraiDebugAPI -enum class ProtoType(val value: Byte, private val typeName: String) { - /** - * int32, int64, uint32, uint64, sint32, sint64, bool, enum - */ - VAR_INT(0x00, "varint"), - - /** - * fixed64, sfixed64, double - */ - BIT_64(0x01, " 64bit"), - - /** - * string, bytes, embedded messages, packed repeated fields - */ - LENGTH_DELIMI(0x02, "delimi"), - - /** - * Groups (deprecated) - */ - START_GROUP(0x03, "startg"), - - /** - * Groups (deprecated) - */ - END_GROUP(0x04, " endg"), - - /** - * fixed32, sfixed32, float - */ - BIT_32(0x05, " 32bit"), - ; - - override fun toString(): String = this.typeName - - companion object { - fun valueOf(value: Byte): ProtoType = values().firstOrNull { it.value == value } ?: error("Unknown ProtoType $value") - } -} - -/** - * 由 ProtoBuf 序列化后的 id 得到类型 - * - * serializedId = (fieldNumber << 3) | wireType - */ -@MiraiDebugAPI -fun protoType(number: UInt): ProtoType = - ProtoType.valueOf(number.toInt().shl(29).ushr(29).toByte()) - -/** - * ProtoBuf 序列化后的 id 转为序列前标记的 id - * - * serializedId = (fieldNumber << 3) | wireType - */ -@MiraiDebugAPI -fun protoFieldNumber(number: UInt): Int = number.toInt().ushr(3) - -@MiraiDebugAPI -class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, Any> by map { - companion object { - @JvmStatic - internal val indent: String = " " - } - - override fun toString(): String { - return this.entries.joinToString(prefix = "ProtoMap(size=$size){\n$indent", postfix = "\n}", separator = "\n$indent") { - "${it.key}=" + it.value.contentToString() - } - } - - fun toStringPrefixed(prefix: String): String { - return this.entries.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent") { - "${it.key}=" + it.value.contentToString(prefix) - } - } - /* - override fun put(key: ProtoFieldId, value: Any): Any? { - println("${key}=" + value.contentToString()) - return null - }*/ -} - -/** - * 将所有元素加入转换为多行的字符串表示. - */ -@MiraiDebugAPI -fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String { - return this.joinToString(prefix = "$prefix${ProtoMap.indent}", separator = "\n$prefix${ProtoMap.indent}", transform = transform) -} - -/** - * 将内容格式化为较可读的字符串输出. - * - * 各数字类型及其无符号类型: 十六进制表示 + 十进制表示. e.g. `0x1000(4096)` - * [ByteArray] 和 [UByteaArray]: 十六进制表示, 通过 [ByteArray.toUHexString] - * [ProtoMap]: 调用 [ProtoMap.toStringPrefixed] - * [Iterable], [Iterator], [Sequence]: 调用各自的 joinToString. - * [Map]: 多行输出. 每行显示一个值. 递归调用 [contentToString]. 嵌套结构将会以缩进表示 - * `data class`: 调用其 [toString] - * 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [contentToString]. 嵌套结构将会以缩进表示 - */ -@MiraiDebugAPI("Extremely slow") -//@Suppress("Unsupported") // false positive -fun Any?.contentToString(prefix: String = ""): String = when (this) { - is Unit -> "Unit" - is UInt -> "0x" + this.toUHexString("") + "($this)" - is UByte -> "0x" + this.toUHexString() + "($this)" - is UShort -> "0x" + this.toUHexString("") + "($this)" - is ULong -> "0x" + this.toUHexString("") + "($this)" - is Int -> "0x" + this.toUHexString("") + "($this)" - is Byte -> "0x" + this.toUHexString() + "($this)" - is Short -> "0x" + this.toUHexString("") + "($this)" - is Long -> "0x" + this.toUHexString("") + "($this)" - - is UVarInt -> "0x" + this.toUHexString("") + "($this)" - - is Boolean -> if (this) "true" else "false" - - is ByteArray -> { - if (this.size == 0) "<Empty ByteArray>" - else this.toUHexString() - } - is UByteArray -> { - if (this.size == 0) "<Empty UByteArray>" - else this.toUHexString() - } - is ShortArray -> { - if (this.size == 0) "<Empty ShortArray>" - else this.iterator().contentToString() - } - is IntArray -> { - if (this.size == 0) "<Empty IntArray>" - else this.iterator().contentToString() - } - is LongArray -> { - if (this.size == 0) "<Empty LongArray>" - else this.iterator().contentToString() - } - is FloatArray -> { - if (this.size == 0) "<Empty FloatArray>" - else this.iterator().contentToString() - } - is DoubleArray -> { - if (this.size == 0) "<Empty DoubleArray>" - else this.iterator().contentToString() - } - is UShortArray -> { - if (this.size == 0) "<Empty ShortArray>" - else this.iterator().contentToString() - } - is UIntArray -> { - if (this.size == 0) "<Empty IntArray>" - else this.iterator().contentToString() - } - is ULongArray -> { - if (this.size == 0) "<Empty LongArray>" - else this.iterator().contentToString() - } - is Array<*> -> { - if (this.size == 0) "<Empty Array>" - else this.iterator().contentToString() - } - is BooleanArray -> { - if (this.size == 0) "<Empty BooleanArray>" - else this.iterator().contentToString() - } - - is ProtoMap -> "ProtoMap(size=$size){\n" + this.toStringPrefixed("$prefix${ProtoMap.indent}${ProtoMap.indent}") + "\n$prefix${ProtoMap.indent}}" - is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString(prefix) } - is Iterator<*> -> this.asSequence().joinToString(prefix = "[", postfix = "]") { it.contentToString(prefix) } - is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString(prefix) } - is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString(prefix) + "=" + it.value.contentToString(prefix) } - else -> { - if (this == null) "null" - else if (this::class.isData) this.toString() - else { - if (this::class.qualifiedName?.startsWith("net.mamoe.mirai.") == true) { - this.contentToStringReflectively(prefix + ProtoMap.indent) - } else this.toString() - /* - (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + "{\n" + - this::class.members.asSequence().filterIsInstance<KProperty<*>>().filter { !it.isSuspend && it.visibility == KVisibility.PUBLIC } - .joinToStringPrefixed( - prefix = ProtoMap.indent - ) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(ProtoMap.indent) }.getOrElse { "<!>" } } - */ - } - } -} - -@MiraiExperimentalAPI("Extremely slow") -@MiraiDebugAPI("Extremely slow") -expect fun Any.contentToStringReflectively(prefix: String = "", filter: ((String, Any?) -> Boolean)? = null): String - -@MiraiDebugAPI -@Suppress("UNCHECKED_CAST") -fun ByteReadPacket.readProtoMap(length: Long = this.remaining): ProtoMap { - val map = ProtoMap(mutableMapOf()) - - - val expectingRemaining = this.remaining - length - while (this.remaining != expectingRemaining) { - require(this.remaining > expectingRemaining) { "Expecting to read $length bytes, but read ${expectingRemaining + length - this.remaining}" } - - try { - val id = ProtoFieldId(readUVarInt()) - - fun readValue(): Any = when (id.type) { - ProtoType.VAR_INT -> UVarInt(readUVarInt()) - ProtoType.BIT_32 -> readUInt() - ProtoType.BIT_64 -> readULong() - ProtoType.LENGTH_DELIMI -> tryReadProtoMapOrByteArray(readUVarInt().toInt()) - - ProtoType.START_GROUP -> Unit - ProtoType.END_GROUP -> Unit - } - - if (map.containsKey(id)) { - if (map[id] !is MutableList<*>) map[id] = mutableListOf(map[id]!!) - (map[id] as MutableList<Any>) += readValue() - } else { - map[id] = readValue() - } - } catch (e: IllegalStateException) { - e.logStacktrace() - return map - } - } - return map -} - -private fun ByteReadPacket.tryReadProtoMapOrByteArray(length: Int): Any { - val bytes = this.readBytes(length) - return try { - bytes.toReadPacket().readProtoMap().apply { require(none { it.key.type == ProtoType.START_GROUP || it.key.type == ProtoType.END_GROUP }) } - } catch (e: Exception) { - bytes - } -} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt index 9aaf2c67a..12d327afa 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt @@ -30,5 +30,13 @@ object ByteArrayPool : DefaultPool<ByteArray>(256) { override fun produceInstance(): ByteArray = ByteArray(BUFFER_SIZE) override fun clearInstance(instance: ByteArray): ByteArray = instance + + fun checkBufferSize(size: Int) { + require(size <= BUFFER_SIZE) { "sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE" } + } + + fun checkBufferSize(size: Long) { + require(size <= BUFFER_SIZE) { "sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE" } + } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt index c19705eae..9e4b98f1a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt @@ -11,7 +11,6 @@ package net.mamoe.mirai.utils.io import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.Closeable -import kotlinx.io.errors.IOException import net.mamoe.mirai.utils.MiraiInternalAPI /** @@ -32,11 +31,6 @@ expect class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Cl val isOpen: Boolean } -/** - * Channel 被关闭 - */ -expect class ClosedChannelException : IOException - /** * 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误. */ diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt deleted file mode 100644 index 75c5d9941..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:JvmName("Varint") -@file:Suppress("EXPERIMENTAL_API_USAGE") - -package net.mamoe.mirai.utils.io - -import kotlinx.io.core.Input -import kotlinx.io.core.Output -import kotlin.experimental.or -import kotlin.jvm.JvmName -import kotlin.jvm.JvmSynthetic - -/** - * Tool class for VarInt or VarLong operations. - * - * Some code from http://wiki.vg/Protocol. - * - * Source project: [Nukkit](http://github.com/nukkit/nukkit) - * - * @author MagicDroidX from Nukkit Project - * @author lmlstarqaq from Nukkit Project - */ - -internal fun encodeZigZag32(signedInt: Int): Long { - return (signedInt shl 1 xor (signedInt shr 31)).toLong() -} - -@JvmSynthetic -internal fun decodeZigZag32(uint: UInt): Int { - return decodeZigZag32(uint.toLong()) -} - -internal fun decodeZigZag32(uint: Long): Int { - return (uint shr 1).toInt() xor -(uint and 1).toInt() -} - -internal fun encodeZigZag64(signedLong: Long): Long { - return signedLong shl 1 xor (signedLong shr 63) -} - -internal fun decodeZigZag64(signedLong: Long): Long { - return signedLong.ushr(1) xor -(signedLong and 1) -} - - -inline class UVarInt( - val data: UInt -) - -@JvmSynthetic -fun Input.readUVarInt(): UInt { - return read(this, 5).toUInt() -} - - -fun Input.readVarLong(): Long { - return decodeZigZag64(readUVarLong().toLong()) -} - - -@JvmSynthetic -fun Input.readUVarLong(): ULong { - return read(this, 10).toULong() -} - -fun Output.writeVarInt(signedInt: Int) { - this.writeUVarInt(encodeZigZag32(signedInt)) -} - -@JvmSynthetic -fun Output.writeUVarInt(uint: UInt) { - return writeUVarInt(uint.toLong()) -} - -fun Output.writeUVarInt(uint: Long) { - this.write0(uint) -} - -fun Output.writeVarLong(signedLong: Long) { - this.writeUVarLong(encodeZigZag64(signedLong)) -} - -fun Output.writeUVarLong(ulong: Long) { - this.write0(ulong) -} - -fun UVarInt.toByteArray(): ByteArray { - val list = mutableListOf<Byte>() - var value = this.data.toLong() - do { - var temp = (value and 127).toByte() - value = value ushr 7 - if (value != 0L) { - temp = temp or 128.toByte() - } - list += temp - } while (value != 0L) - return list.toByteArray() -} - -fun UVarInt.toUHexString(separator: String = " "): String = buildString { - var value = data.toLong() - - var isFirst = true - do { - if (!isFirst) { - append(separator) - } - var temp = (value and 127).toByte() - value = value ushr 7 - if (value != 0L) { - temp = temp or 128.toByte() - } - append(temp.toUByte().fixToUHex()) - isFirst = false - } while (value != 0L) -} - -private fun Output.write0(long: Long) { - var value = long - do { - var temp = (value and 127).toByte() - value = value ushr 7 - if (value != 0L) { - temp = temp or 128.toByte() - } - this.writeByte(temp) - } while (value != 0L) -} - -private fun read(stream: Input, maxSize: Int): Long { - var value: Long = 0 - var size = 0 - var b = stream.readByte().toInt() - while (b and 0x80 == 0x80) { - value = value or ((b and 0x7F).toLong() shl size++ * 7) - require(size < maxSize) { "VarLong too big(expecting maxSize=$maxSize)" } - b = stream.readByte().toInt() - } - - return value or ((b and 0x7F).toLong() shl size * 7) -} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt new file mode 100644 index 000000000..131513920 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.utils.io + +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.io.InputStream +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.Input +import kotlinx.io.core.readAvailable +import kotlinx.io.pool.useInstance +import net.mamoe.mirai.utils.MiraiInternalAPI + + +/** + * 由 [chunkedFlow] 分割得到的区块 + */ +class ChunkedInput( + /** + * 区块的数据. + * 由 [ByteArrayPool] 缓存并管理, 只可在 [Flow.collect] 中访问. + * 它的大小由 [ByteArrayPool.BUFFER_SIZE] 决定, 而有效(有数据)的大小由 [bufferSize] 决定. + * + * **注意**: 不要将他带出 [Flow.collect] 作用域, 否则将造成内存泄露 + */ + val buffer: ByteArray, + internal var size: Int +) { + /** + * [buffer] 的有效大小 + */ + val bufferSize: Int get() = size +} + +/** + * 创建将 [ByteReadPacket] 以固定大小分割的 [Sequence]. + * + * 对于一个 1000 长度的 [ByteReadPacket] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence], + * 其长度分别为: 300, 300, 300, 100. + * + * 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence] + */ +@UseExperimental(MiraiInternalAPI::class) +fun ByteReadPacket.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput> { + ByteArrayPool.checkBufferSize(sizePerPacket) + if (this.remaining <= sizePerPacket.toLong()) { + ByteArrayPool.useInstance { buffer -> + return flowOf(ChunkedInput(buffer, this.readAvailable(buffer))) + } + } + return flow { + ByteArrayPool.useInstance { buffer -> + val chunkedInput = ChunkedInput(buffer, 0) + do { + chunkedInput.size = this@chunkedFlow.readAvailable(buffer) + emit(chunkedInput) + } while (this@chunkedFlow.isNotEmpty) + } + } +} + +/** + * 创建将 [ByteReadChannel] 以固定大小分割的 [Sequence]. + * + * 对于一个 1000 长度的 [ByteReadChannel] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence], + * 其长度分别为: 300, 300, 300, 100. + */ +@UseExperimental(MiraiInternalAPI::class) +fun ByteReadChannel.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput> { + ByteArrayPool.checkBufferSize(sizePerPacket) + if (this.isClosedForRead) { + return flowOf() + } + return flow { + ByteArrayPool.useInstance { buffer -> + val chunkedInput = ChunkedInput(buffer, 0) + do { + chunkedInput.size = this@chunkedFlow.readAvailable(buffer, 0, buffer.size) + emit(chunkedInput) + } while (!this@chunkedFlow.isClosedForRead) + } + } +} + + +/** + * 创建将 [Input] 以固定大小分割的 [Sequence]. + * + * 对于一个 1000 长度的 [Input] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence], + * 其长度分别为: 300, 300, 300, 100. + */ +@UseExperimental(MiraiInternalAPI::class, ExperimentalCoroutinesApi::class) +internal fun Input.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput> { + ByteArrayPool.checkBufferSize(sizePerPacket) + + if (this.endOfInput) { + return flowOf() + } + + return flow { + ByteArrayPool.useInstance { buffer -> + val chunkedInput = ChunkedInput(buffer, 0) + while (!this@chunkedFlow.endOfInput) { + chunkedInput.size = this@chunkedFlow.readAvailable(buffer) + emit(chunkedInput) + } + } + } +} + +/** + * 创建将 [ByteReadPacket] 以固定大小分割的 [Sequence]. + * + * 对于一个 1000 长度的 [ByteReadPacket] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence], + * 其长度分别为: 300, 300, 300, 100. + * + * 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence] + */ +@UseExperimental(MiraiInternalAPI::class, ExperimentalCoroutinesApi::class) +internal fun InputStream.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput> { + ByteArrayPool.checkBufferSize(sizePerPacket) + + return flow { + ByteArrayPool.useInstance { buffer -> + val chunkedInput = ChunkedInput(buffer, 0) + while (this@chunkedFlow.available() != 0) { + chunkedInput.size = this@chunkedFlow.read(buffer) + emit(chunkedInput) + } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugging.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugging.kt deleted file mode 100644 index ed1abaa03..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugging.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:Suppress("NOTHING_TO_INLINE") -@file:JvmMultifileClass -@file:JvmName("Utils") - -package net.mamoe.mirai.utils.io - -import kotlinx.io.core.* -import kotlinx.io.pool.useInstance -import net.mamoe.mirai.utils.DefaultLogger -import net.mamoe.mirai.utils.MiraiDebugAPI -import net.mamoe.mirai.utils.MiraiLoggerWithSwitch -import net.mamoe.mirai.utils.withSwitch -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmMultifileClass -import kotlin.jvm.JvmName - - -@MiraiDebugAPI("Unsatble") -val DebugLogger : MiraiLoggerWithSwitch = DefaultLogger("Packet Debug").withSwitch(false) - -@MiraiDebugAPI("Unstable") -inline fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this) - -@MiraiDebugAPI("Low efficiency.") -inline fun String.debugPrintThis(name: String): String { - DebugLogger.debug("$name=$this") - return this -} - -@MiraiDebugAPI("Low efficiency.") -inline fun ByteArray.debugPrintThis(name: String): ByteArray { - DebugLogger.debug(name + "=" + this.toUHexString()) - return this -} - -@MiraiDebugAPI("Low efficiency.") -inline fun IoBuffer.debugPrintThis(name: String): IoBuffer { - ByteArrayPool.useInstance { - val count = this.readAvailable(it) - DebugLogger.debug(name + "=" + it.toUHexString(offset = 0, length = count)) - return it.toIoBuffer(0, count) - } -} - -@MiraiDebugAPI("Low efficiency.") -inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer { - ByteArrayPool.useInstance { - val count = this.readAvailable(it) - block(it.toIoBuffer(0, count)) - return it.toIoBuffer(0, count) - } -} - -@MiraiDebugAPI("Low efficiency.") -inline fun Input.debugDiscardExact(n: Number, name: String = "") { - DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString()) -} - -@MiraiDebugAPI("Low efficiency.") -inline fun ByteReadPacket.debugPrintThis(name: String = ""): ByteReadPacket { - ByteArrayPool.useInstance { - val count = this.readAvailable(it) - DebugLogger.debug("ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count)) - return it.toReadPacket(0, count) - } -} - -/** - * 备份数据, 并在 [block] 失败后执行 [onFail]. - * - * 此方法非常低效. 请仅在测试环境使用. - */ -@MiraiDebugAPI("Low efficiency") -@UseExperimental(ExperimentalContracts::class) -inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R { - - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - callsInPlace(onFail, InvocationKind.UNKNOWN) - } - ByteArrayPool.useInstance { - val count = this.readAvailable(it) - try { - return it.toReadPacket(0, count).use(block) - } catch (e: Throwable) { - onFail(it.take(count).toByteArray()).readAvailable(it) - DebugLogger.debug("Error in ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count)) - throw e - } - } -} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt index 9ba1d6e27..88d110485 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt @@ -20,26 +20,12 @@ import kotlinx.io.core.* import kotlinx.io.pool.useInstance import net.mamoe.mirai.utils.MiraiDebugAPI import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.cryptor.contentToString import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic -@Suppress("NOTHING_TO_INLINE") -inline fun Input.discardExact(n: Short) = this.discardExact(n.toInt()) - -@Suppress("NOTHING_TO_INLINE") -@JvmSynthetic -inline fun Input.discardExact(n: UShort) = this.discardExact(n.toInt()) - -@Suppress("NOTHING_TO_INLINE") -@JvmSynthetic -inline fun Input.discardExact(n: UByte) = this.discardExact(n.toInt()) - -@Suppress("NOTHING_TO_INLINE") -inline fun Input.discardExact(n: Byte) = this.discardExact(n.toInt()) - -fun ByteReadPacket.transferTo(outputStream: OutputStream) { +@UseExperimental(MiraiInternalAPI::class) +fun ByteReadPacket.copyTo(outputStream: OutputStream) { ByteArrayPool.useInstance { while (this.isNotEmpty) { outputStream.write(it, 0, this.readAvailable(it)) @@ -56,21 +42,13 @@ inline fun <R> ByteReadPacket.useBytes( block(it, n) } +@MiraiInternalAPI inline fun ByteReadPacket.readPacketExact( n: Int = remaining.toInt()//not that safe but adequate ): ByteReadPacket = this.readBytes(n).toReadPacket() -inline fun Input.readUByteLVString(): String = String(this.readUByteLVByteArray()) - -inline fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) - -inline fun Input.readUByteLVByteArray(): ByteArray = this.readBytes(this.readUByte().toInt()) - -inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt()) - private inline fun <R> inline(block: () -> R): R = block() - typealias TlvMap = MutableMap<Int, ByteArray> inline fun TlvMap.getOrFail(tag: Int): ByteArray { @@ -81,12 +59,13 @@ inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteAr return this[tag] ?: error(lazyMessage(tag)) } +@Suppress("FunctionName") @MiraiInternalAPI -inline fun Input.readTLVMap(tagSize: Int = 2, suppressDuplication: Boolean = true): TlvMap = readTLVMap(true, tagSize, suppressDuplication) +inline fun Input._readTLVMap(tagSize: Int = 2, suppressDuplication: Boolean = true): TlvMap = _readTLVMap(true, tagSize, suppressDuplication) @MiraiDebugAPI -@Suppress("DuplicatedCode") -fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplication: Boolean = true): TlvMap { +@Suppress("DuplicatedCode", "FunctionName") +fun Input._readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplication: Boolean = true): TlvMap { val map = mutableMapOf<Int, ByteArray>() var key = 0 @@ -108,11 +87,14 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplica }.toUByte() != UByte.MAX_VALUE) { if (map.containsKey(key)) { + @Suppress("ControlFlowWithEmptyBody") if (!suppressDuplication) { - DebugLogger.error( + /* + @Suppress("DEPRECATION") + MiraiLogger.error( @Suppress("IMPLICIT_CAST_TO_ANY") """ - Error readTLVMap: + Error readTLVMap: duplicated key ${when (tagSize) { 1 -> key.toByte() 2 -> key.toShort() @@ -122,13 +104,13 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplica map=${map.contentToString()} duplicating value=${this.readUShortLVByteArray().toUHexString()} """.trimIndent() - ) + )*/ } else { this.discardExact(this.readShort().toInt() and 0xffff) } } else { try { - map[key] = this.readUShortLVByteArray() + map[key] = this.readBytes(readUShort().toInt()) } catch (e: Exception) { // BufferUnderflowException, java.io.EOFException // if (expectingEOF) { // return map diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt index b2d444edd..671e023b3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt @@ -14,8 +14,9 @@ package net.mamoe.mirai.utils.io import kotlinx.io.core.* +import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.coerceAtMostOrFail -import net.mamoe.mirai.utils.cryptor.encryptBy +import net.mamoe.mirai.utils.cryptor.TEA import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName @@ -67,5 +68,6 @@ fun BytePacketBuilder.writeHex(uHex: String) { /** * 会使用 [ByteArrayPool] 缓存 */ +@UseExperimental(MiraiInternalAPI::class) inline fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) = - BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) } \ No newline at end of file + TEA.encrypt(BytePacketBuilder().apply(encoder).build(), key) { decrypted -> writeFully(decrypted) } \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt index 5f9115b07..9e63ed131 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt @@ -31,40 +31,12 @@ import javax.imageio.ImageIO import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -/** - * 平台默认的验证码识别器. - * - * 可被修改, 除覆盖配置外全局生效. - */ -actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver() - - -interface LoginSolverInputReader{ - suspend fun read(question:String):String? - - suspend operator fun invoke(question: String):String?{ - return read(question) - } -} -class DefaultLoginSolverInputReader: LoginSolverInputReader{ - override suspend fun read(question: String): String? { - return readLine() - } -} - class DefaultLoginSolver( - val reader: LoginSolverInputReader = DefaultLoginSolverInputReader(), - val overrideLogger:MiraiLogger? = null + private val input: suspend () -> String, + private val overrideLogger: MiraiLogger? = null ) : LoginSolver() { - fun getLogger(bot: Bot):MiraiLogger{ - if(overrideLogger!=null){ - return overrideLogger - } - return bot.logger - } - override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? = loginSolverLock.withLock { - val logger = getLogger(bot) + val logger = overrideLogger ?: bot.logger val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() } withContext(Dispatchers.IO) { tempFile.createNewFile() @@ -86,39 +58,38 @@ class DefaultLoginSolver( } } logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车") - return reader("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")!!.takeUnless { it.isEmpty() || it.length != 4 }.also { + return input().takeUnless { it.isEmpty() || it.length != 4 }.also { logger.info("正在提交[$it]中...") } } override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? = loginSolverLock.withLock { - val logger = getLogger(bot) + val logger = overrideLogger ?: bot.logger logger.info("需要滑动验证码") logger.info("请在任意浏览器中打开以下链接并完成验证码. ") logger.info("完成后请输入任意字符 ") logger.info(url) - return reader("完成后请输入任意字符").also { + return input().also { logger.info("正在提交中...") } } override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? = loginSolverLock.withLock { - val logger = getLogger(bot) + val logger = overrideLogger ?: bot.logger logger.info("需要进行账户安全认证") logger.info("该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题") logger.info("完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次") logger.info("请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符") logger.info("这步操作将在后续的版本中优化") logger.info(url) - return reader("完成后请输入任意字符").also { + return input().also { logger.info("正在提交中...") } } - } // Copied from Ktor CIO -public fun File.writeChannel( +private fun File.writeChannel( coroutineContext: CoroutineContext = Dispatchers.IO ): ByteWriteChannel = GlobalScope.reader(CoroutineName("file-writer") + coroutineContext, autoFlush = true) { @Suppress("BlockingMethodInNonBlockingContext") @@ -134,7 +105,7 @@ private val loginSolverLock = Mutex() /** * @author NaturalHG */ -public fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Double = 0.95): String { +private fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Double = 0.95): String { val newHeight = (this.height * (outputWidth.toDouble() / this.width)).toInt() val tmp = this.getScaledInstance(outputWidth, newHeight, Image.SCALE_SMOOTH) val image = BufferedImage(outputWidth, newHeight, BufferedImage.TYPE_INT_ARGB) @@ -229,7 +200,7 @@ actual open class BotConfiguration actual constructor() { /** * 验证码处理器 */ - actual var loginSolver: LoginSolver = defaultLoginSolver + actual var loginSolver: LoginSolver = LoginSolver.Default actual companion object { /** @@ -279,4 +250,18 @@ inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath: */ @BotConfigurationDsl companion object ByDeviceDotJson +} + +/** + * 验证码, 设备锁解决器 + */ +actual abstract class LoginSolver { + actual abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? + actual abstract suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? + actual abstract suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? + + actual companion object { + actual val Default: LoginSolver + get() = DefaultLoginSolver(input = { withContext(Dispatchers.IO) { readLine() } ?: error("No standard input") }) + } } \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt index 61366628f..4861595a2 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt @@ -56,6 +56,7 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress actual val Http: HttpClient get() = HttpClient(CIO) +@UseExperimental(MiraiInternalAPI::class) actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { this.checkOffsetAndLength(offset, length) if (length == 0) return ByteArray(0) diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt deleted file mode 100644 index 8d72af7aa..000000000 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -package net.mamoe.mirai.utils.cryptor - -import net.mamoe.mirai.utils.MiraiDebugAPI -import java.lang.reflect.Field -import kotlin.reflect.full.allSuperclasses - - -val FIELD_TRY_SET_ACCESSIBLE = Field::class.java.declaredMethods.firstOrNull { it.name == "trySetAccessible" } - -@MiraiDebugAPI -actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)?): String { - val newPrefix = prefix + ProtoMap.indent - return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" + - this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") } - .distinctBy { it.name } - .filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" } - .filterNot { it.isEnumConstant } - .map { - FIELD_TRY_SET_ACCESSIBLE?.invoke(it, true) ?: kotlin.run { it.isAccessible = true } - val value = it.get(this) - if (filter != null) { - kotlin.runCatching { - if (!filter(it.name, value)) return@map it.name to FIELD_TRY_SET_ACCESSIBLE - } - } - it.name to value - } - .filterNot { it.second === FIELD_TRY_SET_ACCESSIBLE } - .joinToStringPrefixed( - prefix = newPrefix - ) { (name, value) -> - "$name=" + kotlin.runCatching { - if (value == this) "<this>" - else value.contentToString(newPrefix) - }.getOrElse { "<!>" } - } + "\n$prefix}" -} - -internal fun Any.allFieldsFromSuperClassesMatching(classFilter: (Class<out Any>) -> Boolean): Sequence<Field> { - return (this::class.java.takeIf(classFilter)?.declaredFields?.asSequence() ?: sequenceOf<Field>()) + this::class.allSuperclasses - .asSequence() - .map { it.java } - .filter(classFilter) - .flatMap { it.declaredFields.asSequence() } -} \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt index da52975bd..1cf48c36f 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt @@ -21,8 +21,6 @@ import java.nio.channels.ReadableByteChannel import java.nio.channels.WritableByteChannel -actual typealias ClosedChannelException = java.nio.channels.ClosedChannelException - /** * 多平台适配的 DatagramChannel. */ diff --git a/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt b/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt index 117b07b76..a603177bb 100644 --- a/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt +++ b/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt @@ -9,16 +9,20 @@ package mirai.test.testCaptchaPacket -import net.mamoe.mirai.utils.cryptor.decryptBy +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.cryptor.TEA.decrypt import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.toUHexString +@MiraiInternalAPI fun main() { val key = "65 F7 F3 14 E3 94 10 1F DD 95 84 A3 F5 9F AD 94".hexToBytes() val data = - "8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29".hexToBytes() - .decryptBy(key) + decrypt( + "8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29".hexToBytes(), + key + ) println(data.toUHexString()) //00 02 00 00 08 04 01 E0 00 00 04 56 00 00 00 01 00 00 15 E3 01 00 38 58 CE A0 12 81 31 5C 5E 36 23 5B E4 0E 05 A6 47 BF 7C 1A 7A 35 37 59 90 17 50 66 0C 07 03 77 E4 48 DB 28 0A CF C3 A9 B7 C0 95 D3 9D 00 AA A5 EB FB D6 85 8D 10 61 5A D0 01 03 00 19 02 CA 53 7E F0 7B 32 82 EC 9F DE CF 51 8B A4 93 26 76 EC 42 1C 02 00 74 58 14 00 05 00 00 00 00 00 04 6C 73 64 61 00 40 CE 99 84 E8 F1 59 31 B0 3F 6C 4D 44 09 E4 82 77 96 67 03 A7 3A EA 8F 36 B9 20 79 7E C9 0F 75 3C 2A C3 E1 E5 C6 00 B3 5E 91 5B 47 63 EF AF 30 C0 48 2F 58 23 96 CF 65 2F 4C 75 95 A6 CA 5A 2C 5C 00 10 E1 50 C9 F4 F6 F4 2F D1 7F E9 8C AB B6 1C 38 7B