From ea1f43b9c53b40acad3d82bc7ef9bf69fabe6d21 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 3 Apr 2021 22:31:14 +0800 Subject: [PATCH] Fast login (#1154) * wtlogin10 * Fast login Packet Implement (#1125) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Refresh Token when exchanging and solve connection dropping issue (#1128) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Schedule token exchanging every 10 minutes, solve connection dropping issue * Refresh Token when exchanging, and correct token expire time * Remove useless params for doFastLogin * Fix missed register and tgt update (#1131) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Schedule token exchanging every 10 minutes, solve connection dropping issue * Refresh Token when exchanging, and correct token expire time * Remove useless params for doFastLogin * Fix missed register and tgt update * Add login lock * Add login lock * Remove key refresh * Remove heartbeat period override * Login: Update tlv and solve constant connection dropping issue (#1150) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Schedule token exchanging every 10 minutes, solve connection dropping issue * Refresh Token when exchanging, and correct token expire time * Remove useless params for doFastLogin * Fix missed register and tgt update * Update tlv, add tlv11d and tlv11a decoding * Add stat heartbeat, solve constant connection dropping issue * Update apidump for new configuration * Add comment for statHeartbeatPeriodMillis * Change old naming * Add since version Co-authored-by: Him188 Co-authored-by: sandtechnology <20417547+sandtechnology@users.noreply.github.com> --- ...binary-compatibility-validator-android.api | 2 + .../api/binary-compatibility-validator.api | 2 + .../kotlin/utils/BotConfiguration.kt | 11 +- .../handler/QQAndroidBotNetworkHandler.kt | 146 ++++++++++++--- .../src/commonMain/kotlin/network/keys.kt | 14 +- .../network/protocol/packet/PacketFactory.kt | 1 + .../kotlin/network/protocol/packet/Tlv.kt | 19 ++ .../protocol/packet/login/ConfigPushSvc.kt | 2 +- .../network/protocol/packet/login/StatSvc.kt | 25 ++- .../network/protocol/packet/login/WtLogin.kt | 171 ++++++++++++++---- .../packet/login/wtlogin/WtLogin10.kt | 89 +++++++++ .../packet/login/wtlogin/WtLoginExt.kt | 24 ++- 12 files changed, 437 insertions(+), 69 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt diff --git a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api index 67f0a130a..b222f997e 100644 --- a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api +++ b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api @@ -5518,6 +5518,7 @@ public class net/mamoe/mirai/utils/BotConfiguration { public final fun getProtocol ()Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol; public final fun getReconnectPeriodMillis ()J public final fun getReconnectionRetryTimes ()I + public final fun getStatHeartbeatPeriodMillis ()J public final fun getWorkingDir ()Ljava/io/File; public final synthetic fun inheritCoroutineContext (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun isConvertLineSeparator ()Z @@ -5560,6 +5561,7 @@ public class net/mamoe/mirai/utils/BotConfiguration { public final fun setProtocol (Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;)V public final fun setReconnectPeriodMillis (J)V public final fun setReconnectionRetryTimes (I)V + public final fun setStatHeartbeatPeriodMillis (J)V public final fun setWorkingDir (Ljava/io/File;)V } diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index 298a44093..d99b976cc 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -5518,6 +5518,7 @@ public class net/mamoe/mirai/utils/BotConfiguration { public final fun getProtocol ()Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol; public final fun getReconnectPeriodMillis ()J public final fun getReconnectionRetryTimes ()I + public final fun getStatHeartbeatPeriodMillis ()J public final fun getWorkingDir ()Ljava/io/File; public final synthetic fun inheritCoroutineContext (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun isConvertLineSeparator ()Z @@ -5560,6 +5561,7 @@ public class net/mamoe/mirai/utils/BotConfiguration { public final fun setProtocol (Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;)V public final fun setReconnectPeriodMillis (J)V public final fun setReconnectionRetryTimes (I)V + public final fun setStatHeartbeatPeriodMillis (J)V public final fun setWorkingDir (Ljava/io/File;)V } diff --git a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt index 83fcba85d..a0b1becb7 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt @@ -142,9 +142,16 @@ public open class BotConfiguration { // open for Java // Connection /////////////////////////////////////////////////////////////////////////// - /** 心跳周期. 过长会导致被服务器断开连接. */ + /** 连接心跳包周期. 过长会导致被服务器断开连接. */ public var heartbeatPeriodMillis: Long = 60.secondsToMillis + /** + * 状态心跳包周期. 过长会导致掉线. + * 该值会在登录时根据服务器下发的配置自动进行更新. + * @since 2.6 + */ + public var statHeartbeatPeriodMillis: Long = 300.secondsToMillis + /** * 每次心跳时等待结果的时间. * 一旦心跳超时, 整个网络服务将会重启 (将消耗约 1s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响. @@ -545,4 +552,4 @@ internal val deviceInfoStub: (Bot) -> DeviceInfo = { MiraiLogger.TopLevel.warning("未指定设备信息, 已使用随机设备信息. 请查看 BotConfiguration.deviceInfo 以获取更多信息.") MiraiLogger.TopLevel.warning("Device info isn't specified. Please refer to BotConfiguration.deviceInfo for more information") DeviceInfo.random() -} \ No newline at end of file +} diff --git a/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt index d78f50b48..6e0566947 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt @@ -35,10 +35,7 @@ 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.WtLogin15 -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.network.protocol.packet.login.wtlogin.* import net.mamoe.mirai.internal.utils.* import net.mamoe.mirai.network.* import net.mamoe.mirai.utils.* @@ -63,6 +60,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo private var _packetReceiverJob: Job? = null private var heartbeatJob: Job? = null + private var statHeartbeatJob: Job? = null private val packetReceiveLock: Mutex = Mutex() @@ -96,6 +94,25 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo }.also { _packetReceiverJob = it } } + private fun startStatHeartbeatJobOrKill(cancelCause: CancellationException? = null): Job { + statHeartbeatJob?.cancel(cancelCause) + + return this@QQAndroidBotNetworkHandler.launch(CoroutineName("statHeartbeatJob")) statHeartbeatJob@{ + while (this.isActive) { + delay(bot.configuration.statHeartbeatPeriodMillis) + val failException = doStatHeartbeat() + if (failException != null) { + delay(bot.configuration.firstReconnectDelayMillis) + + bot.launch { + BotOfflineEvent.Dropped(bot, failException).broadcast() + } + return@statHeartbeatJob + } + } + }.also { statHeartbeatJob = it } + } + private fun startHeartbeatJobOrKill(cancelCause: CancellationException? = null): Job { heartbeatJob?.cancel(cancelCause) @@ -121,6 +138,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo override suspend fun closeEverythingAndRelogin(host: String, port: Int, cause: Throwable?, step: Int) { heartbeatJob?.cancel(CancellationException("relogin", cause)) heartbeatJob?.join() + statHeartbeatJob?.cancel(CancellationException("relogin", cause)) + statHeartbeatJob?.join() _packetReceiverJob?.cancel(CancellationException("relogin", cause)) _packetReceiverJob?.join() if (::channel.isInitialized) { @@ -134,7 +153,6 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo } channel = PlatformSocket() - bot.initClient() while (isActive) { try { @@ -156,9 +174,81 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo } } } + logger.info { "Connected to server $host:$port" } + if (bot.client.wLoginSigInfoInitialized) { + // do fast login + } else { + bot.initClient() + } + startPacketReceiverJobOrKill(CancellationException("relogin", cause)) + if (bot.client.wLoginSigInfoInitialized) { + // do fast login + kotlin.runCatching { + doFastLogin() + }.onFailure { + bot.initClient() + doSlowLogin(host, port, cause, step) + } + } else { + doSlowLogin(host, port, cause, step) + } + + + // println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}") + registerClientOnline() + startStatHeartbeatJobOrKill() + startHeartbeatJobOrKill() + bot.eventChannel.subscribeOnce(this.coroutineContext) { + val bot = (bot as QQAndroidBot) + if (bot.firstLoginSucceed && bot.client.wLoginSigInfoInitialized) { + launch { + while (isActive) { + bot.client.wLoginSigInfo.vKey.run { + //由过期时间最短的且不会被skey更换更新的vkey计算重新登录的时间 + val delay = (expireTime - creationTime).seconds - 5.minutes + logger.info { "Scheduled refresh login session in ${delay.toHumanReadableString()}." } + delay(delay) + } + runCatching { + doFastLogin() + registerClientOnline() + }.onFailure { + logger.warning("Failed to refresh login session.", it) + } + } + } + launch { + while (isActive) { + bot.client.wLoginSigInfo.sKey.run { + val delay = (expireTime - creationTime).seconds - 5.minutes + logger.info { "Scheduled key refresh in ${delay.toHumanReadableString()}." } + delay(delay) + } + runCatching { + refreshKeys() + }.onFailure { + logger.error("Failed to refresh key.", it) + } + } + } + } + } + } + + private val fastLoginOrSendPacketLock = Mutex() + + private suspend fun doFastLogin(): Boolean { + fastLoginOrSendPacketLock.withLock { + val login10 = WtLogin10(bot.client).sendAndExpect(ignoreLock = true) + return login10 is WtLogin.Login.LoginPacketResponse.Success + } + } + + private suspend fun doSlowLogin(host: String, port: Int, cause: Throwable?, step: Int) { + fun LoginSolver?.notnull(): LoginSolver { checkNotNull(this) { "No LoginSolver found. Please provide by BotConfiguration.loginSolver. " + @@ -263,24 +353,6 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo } } - // println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}") - registerClientOnline() - startHeartbeatJobOrKill() - - launch { - while (isActive) { - bot.client.wLoginSigInfo.sKey.run { - val delay = (expireTime - creationTime).seconds - 5.minutes - logger.info { "Scheduled key refresh in ${delay.toHumanReadableString()}." } - delay(delay) - } - runCatching { - refreshKeys() - }.onFailure { - logger.error("Failed to refresh key.", it) - } - } - } } suspend fun refreshKeys() { @@ -429,6 +501,17 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo logger.info { "Syncing friend message history: Success." } } + private suspend fun doStatHeartbeat(): Throwable? { + return retryCatching(2) { + StatSvc.SimpleGet(bot.client) + .sendAndExpect( + timeoutMillis = bot.configuration.heartbeatTimeoutMillis, + retry = 2 + ) + return null + }.exceptionOrNull() + } + private suspend fun doHeartBeat(): Throwable? { return retryCatching(2) { Heartbeat.Alive(bot.client) @@ -675,16 +758,27 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo suspend inline fun OutgoingPacketWithRespType.sendAndExpect( timeoutMillis: Long = 5000, - retry: Int = 2 + retry: Int = 2, + ignoreLock: Boolean = false, ): E { - return (this as OutgoingPacket).sendAndExpect(timeoutMillis, retry) + return (this as OutgoingPacket).sendAndExpect(timeoutMillis, retry, ignoreLock) } /** * 发送一个包, 挂起协程直到接收到指定的返回包或超时 */ @Suppress("UNCHECKED_CAST") - suspend fun OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E { + suspend fun OutgoingPacket.sendAndExpect( + timeoutMillis: Long = 5000, + retry: Int = 2, + ignoreLock: Boolean = false + ): E { + return if (!ignoreLock) fastLoginOrSendPacketLock.withLock { + sendAndExpectImpl(timeoutMillis, retry) + } else sendAndExpectImpl(timeoutMillis, retry) + } + + private suspend fun OutgoingPacket.sendAndExpectImpl(timeoutMillis: Long, retry: Int): E { require(timeoutMillis > 100) { "timeoutMillis must > 100" } require(retry in 0..10) { "retry must in 0..10" } diff --git a/mirai-core/src/commonMain/kotlin/network/keys.kt b/mirai-core/src/commonMain/kotlin/network/keys.kt index 76159b262..636b591b4 100644 --- a/mirai-core/src/commonMain/kotlin/network/keys.kt +++ b/mirai-core/src/commonMain/kotlin/network/keys.kt @@ -13,6 +13,7 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.Input import kotlinx.io.core.readBytes import kotlinx.io.core.readUShort +import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.network.getRandomByteArray import net.mamoe.mirai.internal.network.protocol.packet.PacketLogger import net.mamoe.mirai.internal.utils.crypto.TEA @@ -111,8 +112,19 @@ internal class WLoginSigInfo( // val pt4Token: ByteArray, var wtSessionTicket: WtSessionTicket, var wtSessionTicketKey: ByteArray, - var deviceToken: ByteArray + var deviceToken: ByteArray, + var encryptedDownloadSession: EncryptedDownloadSession? = null ) { + + //图片加密下载 + //是否加密从bigdatachannel处得知 + @Serializable + internal class EncryptedDownloadSession( + val appId: Long,//1600000226L + val stKey: ByteArray, + val stSig: ByteArray + ) + override fun toString(): String { return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.toUHexString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.toUHexString()}, userStSig=$userStSig, userStKey=${userStKey.toUHexString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.toUHexString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.toUHexString()}, sid=$sid, aqSig=$aqSig, psKey=$psKeyMap, superKey=${superKey.toUHexString()}, payToken=${payToken.toUHexString()}, pf=${pf.toUHexString()}, pfKey=${pfKey.toUHexString()}, da2=${da2.toUHexString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}, deviceToken=${deviceToken.toUHexString()})" } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index ad328e122..bd7d46538 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -127,6 +127,7 @@ internal object KnownPacketFactories { WtLogin.ExchangeEmp, StatSvc.Register, StatSvc.GetOnlineStatus, + StatSvc.SimpleGet, StatSvc.GetDevLoginInfo, MessageSvcPbGetMsg, MessageSvcPushForceOffline, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt index ae8fb0b87..d2d9bd1a7 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt @@ -222,6 +222,16 @@ internal fun BytePacketBuilder.t100( } shouldEqualsTo 22 } +internal fun BytePacketBuilder.t10a( + tgt: ByteArray, +) { + writeShort(0x10a) + writeShortLVPacket { + writeFully(tgt) + } +} + + internal fun BytePacketBuilder.t107( picType: Int, capType: Int = 0, @@ -326,6 +336,15 @@ internal fun BytePacketBuilder.t142( } } +internal fun BytePacketBuilder.t143( + d2: ByteArray +) { + writeShort(0x143) + writeShortLVPacket { + writeFully(d2) + } +} + internal fun BytePacketBuilder.t112( nonNumberUin: ByteArray ) { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt index 52cdd4c80..f7dc045c5 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt @@ -143,7 +143,7 @@ internal class ConfigPushSvc { serverListPush.mobileSSOServerList } - bot.logger.info { "Server list: ${pushServerList.joinToString()}." } + bot.network.logger.info { "Server list: ${pushServerList.joinToString()}." } if (pushServerList.isNotEmpty()) { bot.serverList.clear() diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt index 1a9d29d56..626dd1ccf 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt @@ -95,6 +95,29 @@ internal class StatSvc { } } + internal object SimpleGet : OutgoingPacketFactory("StatSvc.SimpleGet") { + internal object Response : Packet { + override fun toString(): String = "Response(SimpleGet.Response)" + } + + operator fun invoke( + client: QQAndroidClient + ): OutgoingPacket = buildLoginOutgoingPacket( + client, + bodyType = 1, + extraData = client.wLoginSigInfo.d2.data, + key = client.wLoginSigInfo.d2Key + ) { + writeSsoPacket(client, client.subAppId, commandName, sequenceId = it) { + + } + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + return Response + } + } + internal object Register : OutgoingPacketFactory("StatSvc.register") { internal class Response( @@ -106,7 +129,7 @@ internal class StatSvc { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { val packet = readUniPacket(SvcRespRegister.serializer()) packet.iHelloInterval.let { - bot.configuration.heartbeatPeriodMillis = it.times(1000).toLong() + bot.configuration.statHeartbeatPeriodMillis = it.times(1000).toLong() } return Response(packet) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt index b5513c622..e9519ed6e 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt @@ -101,6 +101,7 @@ internal class WtLogin { } data class Error( + val code: Int, val title: String, val message: String, val errorInfo: String @@ -137,7 +138,7 @@ internal class WtLogin { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse { - discardExact(2) // subCommand + val subCommand = readUShort().toInt() // subCommand // println("subCommand=$subCommand") val type = readUByte() // println("type=$type") @@ -162,13 +163,13 @@ internal class WtLogin { // }.readBytes().md5() // } return when (type.toInt()) { - 0 -> onLoginSuccess(tlvMap, bot) + 0 -> onLoginSuccess(subCommand, tlvMap, bot) 2 -> onSolveLoginCaptcha(tlvMap, bot) 160, 239 /*-96*/ -> onUnsafeDeviceLogin(tlvMap) 204 /*-52*/ -> onDevLockLogin(tlvMap, bot) // 1, 15 -> onErrorMessage(tlvMap) ?: error("Cannot find error message") else -> { - onErrorMessage(tlvMap) + onErrorMessage(type.toInt(), tlvMap) ?: error("Cannot find error message, unknown login result type: $type, TLVMap = ${tlvMap._miraiContentToString()}") } } @@ -229,7 +230,7 @@ internal class WtLogin { error("UNKNOWN CAPTCHA, tlvMap=" + tlvMap._miraiContentToString()) } - fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { + fun onLoginSuccess(subCommand: Int, tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { val client = bot.client //println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) @@ -237,7 +238,13 @@ internal class WtLogin { // tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } tlvMap[0x161]?.let { client.analysisTlv161(it) } tlvMap[0x119]?.let { t119Data -> - TEA.decrypt(t119Data, client.tgtgtKey).read { + TEA.decrypt( + t119Data, if (subCommand == 11) { + client.wLoginSigInfo.d2Key.md5() + } else { + 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 @@ -265,7 +272,13 @@ internal class WtLogin { tlvMap119[0x118]?.let { client.mainDisplayName = it } tlvMap119[0x108]?.let { client.ksid = it } - + tlvMap119[0x11a]?.read { + val faceId = readShort().toInt() + val age = readByte().toInt() + val gender = readByte().toInt() + val nickLength = readByte().toInt() + bot.nick = readString(nickLength) + } var openId: ByteArray? = null var openKey: ByteArray? = null tlvMap119[0x125]?.read { @@ -296,6 +309,7 @@ internal class WtLogin { }.getOrElse { ByteReadPacket(byteArrayOf()) } ) } + tlvMap119[0x167]?.let { val imgType = byteArrayOf(readByte()) val imgFormat = byteArrayOf(readByte()) @@ -325,6 +339,21 @@ internal class WtLogin { val creationTime = currentTimeSeconds() val expireTime = creationTime + 21600 + val changeTokenTimeMap = tlvMap119[0x138]?.read { + val tlvMap138 = mutableMapOf() + val count = readInt() + repeat(count) { + val key = readShort().toInt() + val value = readInt().toLong() + tlvMap138[key] = value + } + tlvMap138 + } ?: emptyMap() + + if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) { + changeTokenTimeMap._miraiContentToString().soutv("tokenChangeTime") + } + val outPSKeyMap: PSKeyMap? val outPt4TokenMap: Pt4TokenMap? if (tlvMap119[0x512] != null) { @@ -342,28 +371,60 @@ internal class WtLogin { outPt4TokenMap = null } - var a1: ByteArray? = tlvMap119.getOrFail(0x106) - var noPicSig: ByteArray? = tlvMap119[0x16a] - tlvMap119[0x531]?.let { - analysisTlv0x531(it) { arg1, arg2 -> - a1 = arg1 - noPicSig = arg2 - } - } - if (client.wLoginSigInfoInitialized) { client.wLoginSigInfo.apply { - userStWebSig = UserStWebSig(tlvMap119.getOrEmpty(0x103), creationTime, expireTime) - userStKey = tlvMap119.getOrEmpty(0x10e) - userStSig = UserStSig((tlvMap119.getOrEmpty(0x114)), creationTime) - appPri = tlvMap119[0x11f]?.let { it.read { discardExact(4); readUInt().toLong() } } - ?: 4294967295L - sKey = SKey(tlvMap119.getOrEmpty(0x120), creationTime, expireTime) - wtSessionTicket = WtSessionTicket(tlvMap119.getOrEmpty(0x133), creationTime) - wtSessionTicketKey = tlvMap119.getOrEmpty(0x134) - deviceToken = tlvMap119.getOrEmpty(0x322) + superKey = tlvMap119.getOrDefault(0x16d, superKey) + d2 = D2( + tlvMap119.getOrDefault(0x143, d2.data), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x143, 1728000L) + ) + d2Key = tlvMap119.getOrDefault(0x305, d2Key) + tgt = tlvMap119.getOrDefault(0x10a, tgt) + tgtKey = tlvMap119.getOrDefault(0x10d, tgtKey) + a2ExpiryTime = creationTime + changeTokenTimeMap.getOrDefault(0x10a, 2160000L) + userStWebSig = UserStWebSig( + tlvMap119.getOrDefault(0x103, userStWebSig.data), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x103, 6000L) + ) + userStKey = tlvMap119.getOrDefault(0x10e, userStKey) + userStSig = UserStSig((tlvMap119.getOrDefault(0x114, userStSig.data)), creationTime) + appPri = tlvMap119[0x11f]?.let { + it.read { + //change interval (int time) + discardExact(4) + readUInt().toLong() + } + } + ?: appPri + sKey = SKey( + tlvMap119.getOrEmpty(0x120), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x120, 86400L) + ) + wtSessionTicket = WtSessionTicket( + tlvMap119.getOrDefault( + 0x133, + client.wLoginSigInfo.wtSessionTicket.data + ), creationTime + ) + + wtSessionTicketKey = tlvMap119.getOrDefault(0x134, client.wLoginSigInfo.wtSessionTicketKey) + deviceToken = tlvMap119.getOrDefault(0x322, deviceToken) + encryptedDownloadSession = tlvMap119[0x11d]?.let { + client.analysisTlv11d(it) + } ?: encryptedDownloadSession } } else { + var a1: ByteArray? = tlvMap119.getOrFail(0x106) + var noPicSig: ByteArray? = tlvMap119[0x16a] + tlvMap119[0x531]?.let { + analysisTlv0x531(it) { arg1, arg2 -> + a1 = arg1 + noPicSig = arg2 + } + } client.wLoginSigInfo = WLoginSigInfo( uin = client.uin, encryptA1 = a1, @@ -375,28 +436,65 @@ internal class WtLogin { imgUrl = client.reserveUinInfo?.imgUrl ?: byteArrayOf(), mainDisplayName = tlvMap119[0x118] ?: error("Cannot find tlv 0x118") ), // defaults {}, from asyncContext._G - appPri = tlvMap119[0x11f]?.let { it.read { discardExact(4); readUInt().toLong() } } + appPri = tlvMap119[0x11f]?.let { + it.read { + //change interval (int time) + discardExact(4) + readUInt().toLong() + } + } ?: 4294967295L, // defaults {}, from asyncContext._G - a2ExpiryTime = expireTime, // or from asyncContext._t403.get_body_data() + a2ExpiryTime = creationTime + changeTokenTimeMap.getOrDefault( + 0x10a, + 2160000L + ), // or from asyncContext._t403.get_body_data() loginBitmap = 0, - tgt = tlvMap119.getOrEmpty(0x10a), + tgt = tlvMap119.getOrFail(0x10a), a2CreationTime = creationTime, tgtKey = tlvMap119.getOrEmpty(0x10d), // from asyncContext._login_bitmap userStSig = UserStSig((tlvMap119.getOrEmpty(0x114)), creationTime), userStKey = tlvMap119.getOrEmpty(0x10e), - userStWebSig = UserStWebSig(tlvMap119.getOrEmpty(0x103), creationTime, expireTime), + userStWebSig = UserStWebSig( + tlvMap119.getOrEmpty(0x103), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x103, 6000L) + ), userA5 = UserA5(tlvMap119.getOrEmpty(0x10b), creationTime), - userA8 = UserA8(tlvMap119.getOrEmpty(0x102), creationTime, expireTime), - lsKey = LSKey(tlvMap119.getOrEmpty(0x11c), creationTime, expireTime), - sKey = SKey(tlvMap119.getOrEmpty(0x120), creationTime, expireTime), + userA8 = UserA8( + tlvMap119.getOrEmpty(0x102), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x102, 72000L) + ), + lsKey = LSKey( + tlvMap119.getOrEmpty(0x11c), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x11c, 1641600L) + ), + sKey = SKey( + tlvMap119.getOrEmpty(0x120), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x120, 86400L) + ), userSig64 = UserSig64(tlvMap119.getOrEmpty(0x121), creationTime), openId = openId.orEmpty(), openKey = OpenKey(openKey.orEmpty(), creationTime), - vKey = VKey(tlvMap119.getOrEmpty(0x136), creationTime, expireTime), + vKey = VKey( + tlvMap119.getOrEmpty(0x136), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x136, 1728000L) + ), accessToken = AccessToken(tlvMap119.getOrEmpty(0x136), creationTime), - d2 = D2(tlvMap119.getOrEmpty(0x143), creationTime, expireTime), + d2 = D2( + tlvMap119.getOrFail(0x143), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x143, 1728000L) + ), d2Key = tlvMap119.getOrEmpty(0x305), - sid = Sid(tlvMap119.getOrEmpty(0x164), creationTime, expireTime), + sid = Sid( + tlvMap119.getOrEmpty(0x164), + creationTime, + creationTime + changeTokenTimeMap.getOrDefault(0x164, 1728000L) + ), aqSig = AqSig(tlvMap119.getOrEmpty(0x171), creationTime), psKeyMap = outPSKeyMap.orEmpty().toMutableMap(), pt4TokenMap = outPt4TokenMap.orEmpty().toMutableMap(), @@ -407,7 +505,10 @@ internal class WtLogin { da2 = tlvMap119.getOrEmpty(0x203), wtSessionTicket = WtSessionTicket(tlvMap119.getOrEmpty(0x133), creationTime), wtSessionTicketKey = tlvMap119.getOrEmpty(0x134), - deviceToken = tlvMap119.getOrEmpty(0x322) + deviceToken = tlvMap119.getOrEmpty(0x322), + encryptedDownloadSession = tlvMap119[0x11d]?.let { + client.analysisTlv11d(it) + } ) } //bot.network.logger.error(client.wLoginSigInfo.psKeyMap["qun.qq.com"]?.data?.encodeToString()) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt new file mode 100644 index 000000000..215c41879 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt @@ -0,0 +1,89 @@ +/* + * 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.protocol.packet.* +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin +import net.mamoe.mirai.internal.utils.GuidSource +import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag +import net.mamoe.mirai.internal.utils.guidFlag +import net.mamoe.mirai.utils.generateDeviceInfoData +import net.mamoe.mirai.utils.md5 +import net.mamoe.mirai.utils.toReadPacket + +internal object WtLogin10 : WtLoginExt { + + const val appId: Long = 16L + operator fun invoke( + client: QQAndroidClient, + ) = WtLogin.ExchangeEmp.buildLoginOutgoingPacket(client, bodyType = 2, key = ByteArray(16)) { sequenceId -> + writeSsoPacket( + client, + client.subAppId, + WtLogin.ExchangeEmp.commandName, + extraData = client.wLoginSigInfo.tgt.toReadPacket(), + sequenceId = sequenceId + ) { + writeOicqRequestPacket( + client, + EncryptMethodECDH(client.ecdh), + 0x0810 + ) { + writeShort(11) // subCommand + writeShort(17) + t100(appId, 100, client.appClientVersion, client.ssoVersion, client.mainSigMap) + t10a(client.wLoginSigInfo.tgt) + t116(client.miscBitMap, client.subSigMap) + t108(client.ksid) + 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.wLoginSigInfo.d2Key.md5() + ) + //t112(client.account.phoneNumber.encodeToByteArray()) + t143(client.wLoginSigInfo.d2.data) + t142(client.apkId) + t154(sequenceId) + t18(appId, uin = client.uin) + t141(client.device.simInfo, client.networkType, client.device.apn) + t8(2052) + //t511() + t147(appId, client.apkVersionName, client.apkSignatureMd5) + t177(client.buildTime, client.sdkVersion) + t187(client.device.macAddress) + t188(client.device.androidId) + t194(client.device.imsiMd5) + t511( + listOf( + "tenpay.com", "openmobile.qq.com", "docs.qq.com", "connect.qq.com", + "qzone.qq.com", "vip.qq.com", "qun.qq.com", "game.qq.com", "qqweb.qq.com", + "office.qq.com", "ti.qq.com", "mail.qq.com", "qzone.com", "mma.qq.com" + ) + ) + //t544() + + } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt index 6aeefefb1..4dd40f643 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt @@ -12,6 +12,7 @@ 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.WLoginSigInfo import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.internal.network.protocol.packet.Tlv import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin @@ -40,14 +41,14 @@ internal inline fun WtLoginExt.analysisTlv0x531( internal interface WtLoginExt { // so as not to register to global extension - fun onErrorMessage(tlvMap: TlvMap): WtLogin.Login.LoginPacketResponse.Error? { + fun onErrorMessage(type: Int, 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) + WtLogin.Login.LoginPacketResponse.Error(type, title, content, otherInfo) } ?: tlvMap[0x146]?.read { discardExact(2) // ver discardExact(2) // code @@ -56,7 +57,7 @@ internal interface WtLoginExt { // so as not to register to global extension val message = readUShortLVString() val errorInfo = readUShortLVString() - WtLogin.Login.LoginPacketResponse.Error(title, message, errorInfo) + WtLogin.Login.LoginPacketResponse.Error(type, title, message, errorInfo) } } @@ -116,6 +117,23 @@ internal interface WtLoginExt { // so as not to register to global extension } } + /** + * Encrypt sig and key for pic downloading + */ + fun QQAndroidClient.analysisTlv11d(t11d: ByteArray): WLoginSigInfo.EncryptedDownloadSession = t11d.read { + val appid = readInt().toLong().and(4294967295L) + val stKey = ByteArray(16) + readAvailable(stKey) + val stSigLength = readUShort().toInt() + val stSig = ByteArray(stSigLength) + readAvailable(stSig) + WLoginSigInfo.EncryptedDownloadSession( + appid, + stKey, + stSig + ) + } + /** * pwd flag */