From 4d251a2a3a799ebbe7f8b610b66a9c2873f2cb5a Mon Sep 17 00:00:00 2001 From: sandtechnology Date: Sun, 22 Jan 2023 03:22:21 +0800 Subject: [PATCH] Handle rare case on packet pipeline Fix #2449, should help #1603 --- .../kotlin/network/components/PacketCodec.kt | 121 +++++++++++------- 1 file changed, 76 insertions(+), 45 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt index dd9fc306dd..d8007cb838 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt @@ -119,7 +119,15 @@ internal class PacketCodecImpl : PacketCodec { when (flag2) { 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", + OTHER + ) + }, size) + } + 0 -> buffer else -> throw PacketCodecException("Unknown flag2=$flag2", PROTOCOL_UPDATED) }.let { decryptedData -> @@ -139,7 +147,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) } @@ -242,60 +250,83 @@ 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") + } + } + readByte().toInt().let { + if (it != 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 = $it, command name=$commandName, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose, \n" + + "Full packet dump: $fullPacketDump" + + "Decrypted data: $decryptedData", + OTHER + ) } - else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod") } + this.discardExact(2) + this.discardExact(2) + this.readUShort() + this.readShort() + this.readUInt().toLong() + val encryptionMethod = this.readUShort().toInt() + + this.discardExact(1) + return decrypt(encryptionMethod); } /**