소스 검색

[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 <[email protected]>

* Change d2Key error type to PROTOCOL_UPDATED

* Reformat code

---------

Co-authored-by: Him188 <[email protected]>
sandtechnology 3 년 전
부모
커밋
28b1032acd
1개의 변경된 파일78개의 추가작업 그리고 48개의 파일을 삭제
  1. 78 48
      mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt

+ 78 - 48
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,
-)
+)