From 384e19980bc250d99b1bcec806c72b9fff0fd8f6 Mon Sep 17 00:00:00 2001 From: sandtechnology <20417547+sandtechnology@users.noreply.github.com> Date: Tue, 21 Mar 2023 22:43:48 +0800 Subject: [PATCH] [core] Handle rare case on packet pipeline (#2450) * Handle rare case on packet pipeline Fix #2449, should help #1603 * Fix and improve tips and improve the readability of code * Improve wording of tips Co-authored-by: Him188 * Change d2Key error type to PROTOCOL_UPDATED * Reformat code --------- Co-authored-by: Him188 --- .../kotlin/network/components/PacketCodec.kt | 126 +++++++++++------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt index 3cd1fa143f4..b40cf119941 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt @@ -123,7 +123,15 @@ internal class PacketCodecImpl : PacketCodec { val raw = try { when (encryptMethod) { 2 -> TEA.decrypt(buffer, DECRYPTER_16_ZERO, size) - 1 -> TEA.decrypt(buffer, client.wLoginSigInfo.d2Key, size) + 1 -> { + TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse { + throw PacketCodecException( + "Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal", + PROTOCOL_UPDATED + ) + }, size) + } + 0 -> buffer else -> throw PacketCodecException("Unknown encrypt type=$encryptMethod", PROTOCOL_UPDATED) }.let { decryptedData -> @@ -163,7 +171,7 @@ internal class PacketCodecImpl : PacketCodec { raw.sequenceId, raw.body.withUse { try { - parseOicqResponse(client) + parseOicqResponse(client, raw.commandName) } catch (e: Throwable) { throw PacketCodecException(e, PacketCodecException.Kind.OTHER) } @@ -268,63 +276,85 @@ internal class PacketCodecImpl : PacketCodec { private fun ByteReadPacket.parseOicqResponse( client: SsoSession, + commandName: String ): ByteArray { - readByte().toInt().let { - check(it == 2) { "$it" } - } - this.discardExact(2) - this.discardExact(2) - this.readUShort() - this.readShort() - this.readUInt().toLong() - val encryptionMethod = this.readUShort().toInt() + val qqEcdh = (client as QQAndroidClient).bot.components[EcdhInitialPublicKeyUpdater].getQQEcdh() + fun decrypt(encryptionMethod: Int): ByteArray { + return when (encryptionMethod) { + 4 -> { + val size = (this.remaining - 1).toInt() + val data = + TEA.decrypt( + this.readBytes(), + qqEcdh.initialQQShareKey, + length = size + ) - this.discardExact(1) - val qqEcdh = - (client as QQAndroidClient).bot.components[EcdhInitialPublicKeyUpdater].getQQEcdh() - return when (encryptionMethod) { - 4 -> { - val size = (this.remaining - 1).toInt() - val data = + val peerShareKey = + qqEcdh.calculateQQShareKey(Ecdh.Instance.importPublicKey(readUShortLVByteArray())) + TEA.decrypt(data, peerShareKey) + } + + 3 -> { + val size = (this.remaining - 1).toInt() + // session TEA.decrypt( this.readBytes(), - qqEcdh.initialQQShareKey, + client.wLoginSigInfo.wtSessionTicketKey, length = size ) + } - val peerShareKey = - qqEcdh.calculateQQShareKey(Ecdh.Instance.importPublicKey(readUShortLVByteArray())) - TEA.decrypt(data, peerShareKey) - } - - 3 -> { - val size = (this.remaining - 1).toInt() - // session - TEA.decrypt( - this.readBytes(), - client.wLoginSigInfo.wtSessionTicketKey, - length = size - ) - } - - 0 -> { - if (client.loginState == 0) { - val size = (this.remaining - 1).toInt() - val byteArrayBuffer = this.readBytes(size) - - runCatching { - TEA.decrypt(byteArrayBuffer, qqEcdh.initialQQShareKey, length = size) - }.getOrElse { - TEA.decrypt(byteArrayBuffer, client.randomKey, length = size) + 0 -> { + if (client.loginState == 0) { + val size = (this.remaining - 1).toInt() + val byteArrayBuffer = this.readBytes(size) + + runCatching { + TEA.decrypt(byteArrayBuffer, qqEcdh.initialQQShareKey, length = size) + }.getOrElse { + TEA.decrypt(byteArrayBuffer, client.randomKey, length = size) + } + } else { + val size = (this.remaining - 1).toInt() + TEA.decrypt(this.readBytes(), client.randomKey, length = size) } - } else { - val size = (this.remaining - 1).toInt() - TEA.decrypt(this.readBytes(), client.randomKey, length = size) } + + else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod") } + } - else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod") + val packetType = readByte().toInt() + if (packetType != 2) { + val fullPacketDump = copy().readBytes().toUHexString() + var decryptedData: String? = null + if (remaining > 15) { + discardExact(12) + val encryptionMethod = this.readUShort().toInt() + discardExact(1) + decryptedData = kotlin.runCatching { + decrypt(encryptionMethod).toUHexString() + }.getOrNull() + } + throw PacketCodecException( + "Received unknown oicq packet type = $packetType, command name = $commandName, ignoring..." + + "\nPlease report this message to https://github.com/mamoe/mirai/issues/new/choose, \n" + + "Full packet dump: $fullPacketDump\n" + + "Decrypted data (contains your encrypted password, please change your password after reporting issue): $decryptedData", + PROTOCOL_UPDATED + ) } + + this.discardExact(2) + this.discardExact(2) + this.readUShort() + this.readShort() + this.readUInt().toLong() + val encryptionMethod = this.readUShort().toInt() + + this.discardExact(1) + return decrypt(encryptionMethod) } /** @@ -363,4 +393,4 @@ internal class RawIncomingPacket constructor( * Can be passed to [PacketFactory] */ val body: ByteArray, -) \ No newline at end of file +)