Explorar el Código

Extract login subcommands to simplify WtLogin, add exchange_emp, #833

Him188 hace 5 años
padre
commit
bb348930d8

+ 1 - 1
mirai-core/build.gradle.kts

@@ -121,7 +121,7 @@ kotlin {
         jvmTest {
             dependencies {
                 implementation("org.pcap4j:pcap4j-distribution:1.8.2")
-                implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14")
+              //  implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14")
             }
         }
     }

+ 8 - 5
mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt

@@ -41,6 +41,9 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.ConfigPushSvc
 import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
 import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
 import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin2
+import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin20
+import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin9
 import net.mamoe.mirai.internal.utils.*
 import net.mamoe.mirai.network.*
 import net.mamoe.mirai.utils.*
@@ -175,12 +178,12 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
         fun loginSolverNotNull() = bot.configuration.loginSolver.notnull()
 
         var response: WtLogin.Login.LoginPacketResponse =
-            WtLogin.Login.SubCommand9(bot.client, allowSlider).sendAndExpect()
+            WtLogin9(bot.client, allowSlider).sendAndExpect()
         mainloop@ while (true) {
             when (response) {
                 is WtLogin.Login.LoginPacketResponse.UnsafeLogin -> {
                     loginSolverNotNull().onSolveUnsafeDeviceLoginVerify(bot, response.url)
-                    response = WtLogin.Login.SubCommand9(bot.client, allowSlider).sendAndExpect()
+                    response = WtLogin9(bot.client, allowSlider).sendAndExpect()
                 }
 
                 is WtLogin.Login.LoginPacketResponse.Captcha -> when (response) {
@@ -190,7 +193,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
                             //refresh captcha
                             result = "ABCD"
                         }
-                        response = WtLogin.Login.SubCommand2.SubmitPictureCaptcha(bot.client, response.sign, result)
+                        response = WtLogin2.SubmitPictureCaptcha(bot.client, response.sign, result)
                             .sendAndExpect()
                         continue@mainloop
                     }
@@ -224,7 +227,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
                             }
                             throw error
                         }
-                        response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
+                        response = WtLogin2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
                         continue@mainloop
                     }
                 }
@@ -243,7 +246,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
                 }
 
                 is WtLogin.Login.LoginPacketResponse.DeviceLockLogin -> {
-                    response = WtLogin.Login.SubCommand20(
+                    response = WtLogin20(
                         bot.client,
                         response.t402
                     ).sendAndExpect()

+ 8 - 0
mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt

@@ -307,6 +307,11 @@ internal open class QQAndroidClient(
     var reserveUinInfo: ReserveUinInfo? = null
     lateinit var wLoginSigInfo: WLoginSigInfo
     var tlv113: ByteArray? = null
+
+    /**
+     * from tlvMap119
+     */
+    var tlv16a: ByteArray? = null
     lateinit var qrPushSig: ByteArray
 
     lateinit var mainDisplayName: ByteArray
@@ -373,6 +378,9 @@ internal class LoginExtraData(
 internal class WLoginSigInfo(
     val uin: Long,
     val encryptA1: ByteArray?, // sigInfo[0]
+    /**
+     * WARNING, please check [QQAndroidClient.tlv16a]
+     */
     val noPicSig: ByteArray?, // sigInfo[1]
     val G: ByteArray, // sigInfo[2]
     val dpwd: ByteArray,

+ 2 - 1
mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
+ * Copyright 2019-2021 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.
@@ -122,6 +122,7 @@ internal val PacketLogger: MiraiLoggerWithSwitch by lazy {
 internal object KnownPacketFactories {
     object OutgoingFactories : List<OutgoingPacketFactory<*>> by mutableListOf(
         WtLogin.Login,
+        WtLogin.ExchangeEmp,
         StatSvc.Register,
         StatSvc.GetOnlineStatus,
         StatSvc.GetDevLoginInfo,

+ 66 - 2
mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
+ * Copyright 2019-2021 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.
@@ -15,10 +15,16 @@ import kotlinx.io.core.BytePacketBuilder
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.toByteArray
 import kotlinx.io.core.writeFully
+import net.mamoe.mirai.internal.network.QQAndroidClient
+import net.mamoe.mirai.internal.network.guid
 import net.mamoe.mirai.internal.network.protocol.LoginType
+import net.mamoe.mirai.internal.utils.GuidSource
+import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag
 import net.mamoe.mirai.internal.utils.NetworkType
+import net.mamoe.mirai.internal.utils.guidFlag
 import net.mamoe.mirai.internal.utils.io.*
 import net.mamoe.mirai.utils.currentTimeMillis
+import net.mamoe.mirai.utils.generateDeviceInfoData
 import net.mamoe.mirai.utils.md5
 import net.mamoe.mirai.utils.toByteArray
 import kotlin.random.Random
@@ -79,6 +85,26 @@ internal fun BytePacketBuilder.t18(
     } shouldEqualsTo 22
 }
 
+internal fun BytePacketBuilder.t106(
+    appId: Long = 16L,
+    client: QQAndroidClient
+) {
+    return t106(
+        appId,
+        client.subAppId /* maybe 1*/,
+        client.appClientVersion,
+        client.uin,
+        true,
+        client.account.passwordMd5,
+        0,
+        client.uin.toByteArray(),
+        client.tgtgtKey,
+        true,
+        client.device.guid,
+        LoginType.PASSWORD,
+        client.ssoVersion
+    )
+}
 
 internal fun BytePacketBuilder.t106(
     appId: Long = 16L,
@@ -291,6 +317,29 @@ internal fun BytePacketBuilder.t112(
     }
 }
 
+internal fun BytePacketBuilder.t144(
+    client: QQAndroidClient
+) {
+    return t144(
+        androidId = client.device.androidId,
+        androidDevInfo = client.device.generateDeviceInfoData(),
+        osType = client.device.osType,
+        osVersion = client.device.version.release,
+        networkType = client.networkType,
+        simInfo = client.device.simInfo,
+        unknown = byteArrayOf(),
+        apn = client.device.apn,
+        isGuidFromFileNull = false,
+        isGuidAvailable = true,
+        isGuidChanged = false,
+        guidFlag = guidFlag(GuidSource.FROM_STORAGE, MacOrAndroidIdChangeFlag(0)),
+        buildModel = client.device.model,
+        guid = client.device.guid,
+        buildBrand = client.device.brand,
+        tgtgtKey = client.tgtgtKey
+    )
+}
+
 internal fun BytePacketBuilder.t144(
     // t109
     androidId: ByteArray,
@@ -499,7 +548,22 @@ internal fun BytePacketBuilder.t141(
 }
 
 internal fun BytePacketBuilder.t511(
-    domains: List<String>
+    domains: List<String> = listOf(
+        "tenpay.com",
+        "openmobile.qq.com",
+        "docs.qq.com",
+        "connect.qq.com",
+        "qzone.qq.com",
+        "vip.qq.com",
+        "gamecenter.qq.com",
+        "qun.qq.com",
+        "game.qq.com",
+        "qqweb.qq.com",
+        "office.qq.com",
+        "ti.qq.com",
+        "mail.qq.com",
+        "mma.qq.com",
+    )
 ) {
     writeShort(0x511)
     writeShortLVPacket {

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 231
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt


+ 54 - 0
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt

@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019-2021 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.internal.network.protocol.packet.login.wtlogin
+
+import net.mamoe.mirai.internal.network.QQAndroidClient
+import net.mamoe.mirai.internal.network.guid
+import net.mamoe.mirai.internal.network.protocol.packet.*
+import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+
+internal object WtLogin15 : WtLoginExt {
+    private const val subCommand = 15.toShort()
+
+    private const val appId = 16L
+
+    operator fun invoke(
+        client: QQAndroidClient,
+    ) = WtLogin.ExchangeEmp.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
+        writeSsoPacket(client, client.subAppId, WtLogin.ExchangeEmp.commandName, sequenceId = sequenceId) {
+            writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
+                writeShort(subCommand) // subCommand
+                writeShort(21)
+
+                t18(16, uin = client.uin)
+                t1(client.uin, client.device.ipAddress)
+                t106(appId, client)
+                t116(client.miscBitMap, client.subSigMap)
+                t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap)
+                t107(0)
+                t142(client.apkId)
+                t144(client)
+                t145(client.device.guid)
+                t16a(client.tlv16a ?: byteArrayOf()) // new
+                t154(sequenceId)
+                t141(client.device.simInfo, client.networkType, client.device.apn)
+                t8(2052)
+                t511()
+                t147(appId, client.apkVersionName, client.apkSignatureMd5)
+                t177(buildTime = client.buildTime, buildVersion = client.sdkVersion)
+                t187(client.device.macAddress)
+                t188(client.device.androidId)
+                t194(client.device.imsiMd5)
+                t202(client.device.wifiBSSID, client.device.wifiSSID)
+                t516()
+            }
+        }
+    }
+}

+ 50 - 0
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt

@@ -0,0 +1,50 @@
+/*
+ * 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.internal.network.protocol.packet.login.wtlogin
+
+import net.mamoe.mirai.internal.network.QQAndroidClient
+import net.mamoe.mirai.internal.network.protocol.packet.*
+import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+
+
+internal object WtLogin2 : WtLoginExt {
+    fun SubmitSliderCaptcha(
+        client: QQAndroidClient,
+        ticket: String
+    ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
+        writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) {
+            writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
+                writeShort(2) // subCommand
+                writeShort(4) // count of TLVs
+                t193(ticket)
+                t8(2052)
+                t104(client.t104)
+                t116(client.miscBitMap, client.subSigMap)
+            }
+        }
+    }
+
+    fun SubmitPictureCaptcha(
+        client: QQAndroidClient,
+        captchaSign: ByteArray,
+        captchaAnswer: String
+    ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
+        writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) {
+            writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
+                writeShort(2) // subCommand
+                writeShort(4) // count of TLVs
+                t2(captchaAnswer, captchaSign, 0)
+                t8(2052)
+                t104(client.t104)
+                t116(client.miscBitMap, client.subSigMap)
+            }
+        }
+    }
+}

+ 35 - 0
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt

@@ -0,0 +1,35 @@
+/*
+ * 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.internal.network.protocol.packet.login.wtlogin
+
+import kotlinx.io.core.toByteArray
+import net.mamoe.mirai.internal.network.QQAndroidClient
+import net.mamoe.mirai.internal.network.guid
+import net.mamoe.mirai.internal.network.protocol.packet.*
+import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+import net.mamoe.mirai.utils.md5
+
+internal object WtLogin20 : WtLoginExt {
+    operator fun invoke(
+        client: QQAndroidClient,
+        t402: ByteArray
+    ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
+        writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) {
+            writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
+                writeShort(20) // subCommand
+                writeShort(4) // count of TLVs, probably ignored by server?
+                t8(2052)
+                t104(client.t104)
+                t116(client.miscBitMap, client.subSigMap)
+                t401((client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5())
+            }
+        }
+    }
+}

+ 114 - 0
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt

@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019-2021 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.internal.network.protocol.packet.login.wtlogin
+
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.readBytes
+import kotlinx.io.core.toByteArray
+import net.mamoe.mirai.internal.network.QQAndroidClient
+import net.mamoe.mirai.internal.network.guid
+import net.mamoe.mirai.internal.network.protocol.packet.*
+import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+
+internal object WtLogin9 : WtLoginExt {
+    private const val appId = 16L
+
+    operator fun invoke(
+        client: QQAndroidClient,
+        allowSlider: Boolean
+    ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
+        writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) {
+            writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
+                writeShort(9) // subCommand
+                writeShort(if (allowSlider) 0x18 else 0x17) // count of TLVs, probably ignored by server?
+                //writeShort(LoginType.PASSWORD.value.toShort())
+
+                t18(appId, client.appClientVersion, client.uin)
+                t1(client.uin, client.device.ipAddress)
+                t106(appId, client)
+
+                /* // from GetStWithPasswd
+                int mMiscBitmap = this.mMiscBitmap;
+                if (t.uinDeviceToken) {
+                    mMiscBitmap = (this.mMiscBitmap | 0x2000000);
+                }
+
+
+                // defaults true
+                if (ConfigManager.get_loginWithPicSt()) appIdList = longArrayOf(1600000226L)
+                */
+                t116(client.miscBitMap, client.subSigMap)
+                t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap)
+                t107(0)
+                t108(client.device.imei.toByteArray())
+
+                // t108(byteArrayOf())
+                // ignored: t104()
+                t142(client.apkId)
+
+                // if login with non-number uin
+                // t112()
+                t144(client)
+
+                //this.build().debugPrint("傻逼")
+                t145(client.device.guid)
+                t147(appId, client.apkVersionName, client.apkSignatureMd5)
+
+                /*
+                if (client.miscBitMap and 0x80 != 0) {
+                    t166(1)
+                }
+                */
+
+                // ignored t16a because array5 is null
+
+                t154(sequenceId)
+                t141(client.device.simInfo, client.networkType, client.device.apn)
+                t8(2052)
+
+                t511()
+
+                // ignored t172 because rollbackSig is null
+                // ignored t185 because loginType is not SMS
+                // ignored t400 because of first login
+
+                t187(client.device.macAddress)
+                t188(client.device.androidId)
+                t194(client.device.imsiMd5)
+                if (allowSlider) {
+                    t191()
+                }
+
+                /*
+                t201(N = byteArrayOf())*/
+
+                t202(client.device.wifiBSSID, client.device.wifiSSID)
+
+                t177(
+                    buildTime = client.buildTime,
+                    buildVersion = client.sdkVersion,
+                )
+                t516()
+                t521()
+
+                t525(buildPacket {
+                    t536(buildPacket {
+                        //com.tencent.loginsecsdk.ProtocolDet#packExtraData
+                        writeByte(1) // const
+                        writeByte(0) // data count
+                    }.readBytes())
+                })
+                // this.build().debugPrint("傻逼")
+
+                // ignored t318 because not logging in by QR
+            }
+        }
+    }
+}

+ 184 - 0
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt

@@ -0,0 +1,184 @@
+/*
+ * 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.internal.network.protocol.packet.login.wtlogin
+
+import kotlinx.io.core.*
+import net.mamoe.mirai.internal.network.LoginExtraData
+import net.mamoe.mirai.internal.network.QQAndroidClient
+import net.mamoe.mirai.internal.network.guid
+import net.mamoe.mirai.internal.network.protocol.packet.Tlv
+import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+import net.mamoe.mirai.internal.network.protocol.packet.t145
+import net.mamoe.mirai.internal.network.readUShortLVByteArray
+import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray
+import net.mamoe.mirai.utils.*
+
+
+internal inline fun WtLoginExt.analysisTlv0x531(
+    t531: ByteArray,
+    handler: (a1: ByteArray, noPicSig: ByteArray) -> Unit
+) {
+    val map = t531.toReadPacket().withUse { _readTLVMap() }
+
+    val t106 = map[0x106]
+    val t16a = map[0x16a]
+    val t113 = map[0x113]
+    val t10c = map[0x10c]
+
+    if (t106 != null && t16a != null && t113 != null && t10c != null) {
+        handler(t106 + t10c, t16a)
+    }
+}
+
+internal interface WtLoginExt { // so as not to register to global extension
+
+    fun onErrorMessage(tlvMap: TlvMap): WtLogin.Login.LoginPacketResponse.Error? {
+        return tlvMap[0x149]?.read {
+            discardExact(2) //type
+            val title: String = readUShortLVString()
+            val content: String = readUShortLVString()
+            val otherInfo: String = readUShortLVString()
+
+            WtLogin.Login.LoginPacketResponse.Error(title, content, otherInfo)
+        } ?: tlvMap[0x146]?.read {
+            discardExact(2) // ver
+            discardExact(2)  // code
+
+            val title = readUShortLVString()
+            val message = readUShortLVString()
+            val errorInfo = readUShortLVString()
+
+            WtLogin.Login.LoginPacketResponse.Error(title, message, errorInfo)
+        }
+    }
+
+    fun TlvMap.getOrEmpty(key: Int): ByteArray {
+        return this[key] ?: byteArrayOf()
+    }
+
+    /**
+     * @throws error
+     */
+    fun QQAndroidClient.parseWFastLoginInfoDataOutA1(t169: ByteArray): ByteReadPacket {
+        val map = t169.toReadPacket().withUse { _readTLVMap() }
+
+        val t106 = map[0x106]
+        val t10c = map[0x10c]
+        val t16a = map[0x16a]
+
+        check(t106 != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x106!!" }
+        check(t10c != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x10c!!" }
+        check(t16a != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x16a!!" }
+
+        return buildPacket {
+            writeByte(64)
+            writeShort(4)
+
+            // TLV
+            writeShort(0x106)
+            writeShortLVByteArray(t106)
+
+            writeShort(0x10c)
+            writeShortLVByteArray(t10c)
+
+            writeShort(0x16a)
+            writeShortLVByteArray(t16a)
+
+            t145(device.guid)
+        }
+    }
+
+    /**
+     * login extra data
+     */
+    fun QQAndroidClient.analysisTlv537(t537: ByteArray) = t537.read {
+        //discardExact(2)
+        loginExtraData = LoginExtraData( // args are to correct order
+            uin = readUInt().toLong(),
+            ip = readBytes(readByte().toInt() and 0xff),
+            time = readInt(), // correct
+            version = readInt()
+        )
+    }
+
+    /**
+     * pwd flag
+     */
+    fun QQAndroidClient.analysisTlv186(t186: ByteArray) = t186.read {
+        discardExact(1)
+        pwdFlag = readByte().toInt() == 1
+    }
+
+    /**
+     * 设置 [QQAndroidClient.uin]
+     */
+    fun QQAndroidClient.analysisTlv113(t113: ByteArray) = t113.read {
+        _uin = readUInt().toLong()
+
+        /*
+        // nothing to do
+
+          if (!asyncContext.ifQQLoginInQim(class_1048.productType)) {
+              this.field_61436.method_62330(this.field_61436.field_63973, this.field_61436.uin);
+          }
+         */
+    }
+
+    /**
+     * 设置 [QQAndroidClient.timeDifference] 和 [QQAndroidClient.ipFromT149]
+     */
+    fun QQAndroidClient.analysisTlv130(t130: ByteArray) = t130.read {
+        discardExact(2)
+        timeDifference = readUInt().toLong() - currentTimeSeconds()
+        ipFromT149 = readBytes(4)
+    }
+
+    fun QQAndroidClient.analysisTlv150(t150: ByteArray) {
+        this.t150 = Tlv(t150)
+    }
+
+    fun QQAndroidClient.analysisTlv161(t161: ByteArray) {
+        val tlv = t161.toReadPacket().apply { discardExact(2) }.withUse { _readTLVMap() }
+
+        tlv[0x173]?.let { analysisTlv173(it) }
+        tlv[0x17f]?.let { analysisTlv17f(it) }
+        tlv[0x172]?.let { rollbackSig = it }
+    }
+
+    /**
+     * server host
+     */
+    fun QQAndroidClient.analysisTlv173(t173: ByteArray) {
+        t173.read {
+            val type = readByte()
+            val host = readUShortLVString()
+            val port = readShort()
+
+            bot.logger.warning("服务器: host=$host, port=$port, type=$type")
+            // SEE oicq_request.java at method analysisT173
+        }
+    }
+
+    /**
+     * ipv6 address
+     */
+    fun QQAndroidClient.analysisTlv17f(t17f: ByteArray) {
+        t17f.read {
+            val type = readByte()
+            val host = readUShortLVString()
+            val port = readShort()
+
+            bot.logger.warning("服务器 ipv6: host=$host, port=$port, type=$type")
+            // SEE oicq_request.java at method analysisT17f
+        }
+    }
+
+    fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray())
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio