From 7a6e930fc7aab4d2e2c0ad6b0a8e19e0be80de21 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 26 Jan 2021 19:32:27 +0800 Subject: [PATCH] Support key refresh (#901) * Add skey tests * t106 * Fix some protocol errors. skey refresh * login extra * Support Key refresh, close #833 * apiDump * Merge with dev --- .../api/binary-compatibility-validator.api | 2 + .../commonMain/kotlin/LowLevelApiAccessor.kt | 10 + .../src/commonMain/kotlin/utils/DeviceInfo.kt | 13 + mirai-core/src/commonMain/kotlin/MiraiImpl.kt | 4 + .../src/commonMain/kotlin/QQAndroidBot.kt | 11 + .../kotlin/network/DebuggingProperties.kt | 14 + .../network/QQAndroidBotNetworkHandler.kt | 28 +- .../kotlin/network/QQAndroidClient.kt | 44 ++- .../src/commonMain/kotlin/network/keys.kt | 77 +++--- .../network/protocol/packet/EncryptMethod.kt | 15 +- .../protocol/packet/OutgoingPacketAndroid.kt | 6 +- .../network/protocol/packet/PacketFactory.kt | 30 +- .../kotlin/network/protocol/packet/Tlv.kt | 94 +++++-- .../network/protocol/packet/login/StatSvc.kt | 1 - .../network/protocol/packet/login/WtLogin.kt | 260 +++++++++--------- .../packet/login/wtlogin/WtLogin15.kt | 159 +++++++++-- .../packet/login/wtlogin/WtLogin20.kt | 10 +- .../protocol/packet/login/wtlogin/WtLogin9.kt | 12 +- .../packet/login/wtlogin/WtLoginExt.kt | 40 ++- .../kotlin/utils/MiraiProtocolInternal.kt | 6 +- 20 files changed, 552 insertions(+), 284 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/network/DebuggingProperties.kt diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index 8d7117256..2126f049c 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -179,6 +179,7 @@ public abstract interface class net/mamoe/mirai/LowLevelApiAccessor { public abstract fun recallGroupMessageRaw (Lnet/mamoe/mirai/Bot;J[I[ILkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun recallGroupTempMessageRaw (Lnet/mamoe/mirai/Bot;JJ[I[II)Z public abstract fun recallGroupTempMessageRaw (Lnet/mamoe/mirai/Bot;JJ[I[IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun refreshKeys (Lnet/mamoe/mirai/Bot;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLnet/mamoe/mirai/data/GroupAnnouncement;)Ljava/lang/String; public abstract fun sendGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLnet/mamoe/mirai/data/GroupAnnouncement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public synthetic fun solveBotInvitedJoinGroupRequestEvent (Lnet/mamoe/mirai/Bot;JJJZ)Lkotlin/Unit; @@ -5409,6 +5410,7 @@ public final class net/mamoe/mirai/utils/DeviceInfo { public final fun getDevice ()[B public final fun getDisplay ()[B public final fun getFingerprint ()[B + public final fun getGuid ()[B public final fun getImei ()Ljava/lang/String; public final fun getImsiMd5 ()[B public final fun getIpAddress ()[B diff --git a/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt b/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt index 9f3222ba8..a25736526 100644 --- a/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt +++ b/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt @@ -37,6 +37,16 @@ public annotation class LowLevelApi */ @LowLevelApi public interface LowLevelApiAccessor { + /** + * 主动刷新 keys, 如 SKey, PSKey 等. + * + * 通常 mirai 会自动刷新, 不需要手动刷新. + * + * @since 2.2 + */ + @MiraiExperimentalApi + public suspend fun refreshKeys(bot: Bot) + /** * 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]). * diff --git a/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt b/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt index 5c8ce87ff..4314d292a 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.utils import kotlinx.io.core.toByteArray import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient import kotlinx.serialization.json.Json import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoNumber @@ -42,6 +43,10 @@ public class DeviceInfo( public val androidId: ByteArray get() = display public val ipAddress: ByteArray get() = byteArrayOf(192.toByte(), 168.toByte(), 1, 123) + @Transient + @MiraiInternalApi + public val guid: ByteArray = generateGuid(androidId, macAddress) + @Serializable public class Version( public val incremental: ByteArray = "5891938".toByteArray(), @@ -127,6 +132,14 @@ public fun DeviceInfo.generateDeviceInfoData(): ByteArray { ) } +/** + * Defaults "%4;7t>;28 OutgoingPacketWithRespType.sendAndExpect( + timeoutMillis: Long = 5000, + retry: Int = 2 + ): E = network.run { sendAndExpect(timeoutMillis, retry) } + + suspend inline fun OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E = + network.run { sendAndExpect(timeoutMillis, retry) } + /** * 获取 获取群公告 所需的 bkn 参数 * */ diff --git a/mirai-core/src/commonMain/kotlin/network/DebuggingProperties.kt b/mirai-core/src/commonMain/kotlin/network/DebuggingProperties.kt new file mode 100644 index 000000000..12ac8e02d --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/DebuggingProperties.kt @@ -0,0 +1,14 @@ +/* + * 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 + +internal object DebuggingProperties { + const val SHOW_TLV_MAP_ON_LOGIN_SUCCESS = false +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt index f1bb0f75f..f004c7944 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt @@ -44,6 +44,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 @@ -55,6 +56,8 @@ import java.util.concurrent.ConcurrentLinkedQueue import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext +import kotlin.time.minutes +import kotlin.time.seconds @Suppress("MemberVisibilityCanBePrivate") internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bot: QQAndroidBot) : BotNetworkHandler() { @@ -252,8 +255,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo is WtLogin.Login.LoginPacketResponse.DeviceLockLogin -> { response = WtLogin20( - bot.client, - response.t402 + bot.client ).sendAndExpect() continue@mainloop } @@ -279,6 +281,25 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo bot.otherClientsLock.withLock { updateOtherClientsList() } + + 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() { + WtLogin15(bot.client).sendAndExpect() } private suspend fun registerClientOnline() { @@ -778,7 +799,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo return retryCatchingExceptions( retry + 1, - except = CancellationException::class.cast() // CancellationException means network closed so don't retry + except = CancellationException::class.cast() // explicit cast due for stupid IDE. + // CancellationException means network closed so don't retry ) { withPacketListener(commandName, sequenceId) { listener -> return withTimeout(timeoutMillis) { // may throw CancellationException diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index 37d7f30f1..3b469a868 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -15,8 +15,10 @@ import kotlinx.atomicfu.AtomicBoolean import kotlinx.atomicfu.AtomicInt import kotlinx.atomicfu.atomic import kotlinx.coroutines.CompletableDeferred +import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.String import kotlinx.io.core.toByteArray +import kotlinx.io.core.writeFully import net.mamoe.mirai.data.OnlineStatus import net.mamoe.mirai.internal.BotAccount import net.mamoe.mirai.internal.QQAndroidBot @@ -24,6 +26,7 @@ import net.mamoe.mirai.internal.network.protocol.SyncingCacheList import net.mamoe.mirai.internal.network.protocol.data.jce.FileStoragePushFSSvcList 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.get_mpasswd import net.mamoe.mirai.internal.utils.MiraiProtocolInternal import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.crypto.ECDH @@ -31,14 +34,8 @@ import net.mamoe.mirai.utils.* import java.util.concurrent.CopyOnWriteArraySet import kotlin.random.Random -internal val DeviceInfo.guid: ByteArray get() = generateGuid(androidId, macAddress) -/** - * Defaults "%4;7t>;28 = CopyOnWriteArraySet() lateinit var wFastLoginInfo: WFastLoginInfo var reserveUinInfo: ReserveUinInfo? = null lateinit var wLoginSigInfo: WLoginSigInfo + val wLoginSigInfoInitialized get() = ::wLoginSigInfo.isInitialized - /** - * from tlvMap119 - */ - var tlv16a: ByteArray? = null + var G: ByteArray = device.guid // sigInfo[2] + var dpwd: ByteArray = get_mpasswd().toByteArray() + var randSeed: ByteArray = EMPTY_BYTE_ARRAY // t403 + + var tlv113: ByteArray? = null + + var t402: ByteArray? = null lateinit var qrPushSig: ByteArray lateinit var mainDisplayName: ByteArray var transportSequenceId = 1 + var lastT106Full: ByteArray? = null + lateinit var t104: ByteArray /** @@ -284,6 +288,16 @@ internal open class QQAndroidClient( val bdhSession: CompletableDeferred = CompletableDeferred() } +internal fun BytePacketBuilder.writeLoginExtraData(loginExtraData: LoginExtraData) { + loginExtraData.run { + writeLong(uin) + writeByte(ip.size.toByte()) + writeFully(ip) + writeInt(time) + writeInt(version) + } +} + internal class BdhSession( val sigSession: ByteArray, val sessionKey: ByteArray, diff --git a/mirai-core/src/commonMain/kotlin/network/keys.kt b/mirai-core/src/commonMain/kotlin/network/keys.kt index d892f63fa..415d1092a 100644 --- a/mirai-core/src/commonMain/kotlin/network/keys.kt +++ b/mirai-core/src/commonMain/kotlin/network/keys.kt @@ -45,17 +45,13 @@ internal class WFastLoginInfo( internal class WLoginSimpleInfo( val uin: Long, // uin - val face: Int, // ubyte actually - val age: Int, // ubyte - val gender: Int, // ubyte - val nick: String, // ubyte lv string val imgType: ByteArray, val imgFormat: ByteArray, val imgUrl: ByteArray, val mainDisplayName: ByteArray ) { override fun toString(): String { - return "WLoginSimpleInfo(uin=$uin, face=$face, age=$age, gender=$gender, nick='$nick', imgType=${imgType.toUHexString()}, imgFormat=${imgFormat.toUHexString()}, imgUrl=${imgUrl.toUHexString()}, mainDisplayName=${mainDisplayName.toUHexString()})" + return "WLoginSimpleInfo(uin=$uin, imgType=${imgType.toUHexString()}, imgFormat=${imgFormat.toUHexString()}, imgUrl=${imgUrl.toUHexString()}, mainDisplayName=${mainDisplayName.toUHexString()})" } } @@ -77,51 +73,48 @@ internal class WLoginSigInfo( * WARNING, please check [QQAndroidClient.tlv16a] */ val noPicSig: ByteArray?, // sigInfo[1] - val G: ByteArray, // sigInfo[2] - val dpwd: ByteArray, - val randSeed: ByteArray, val simpleInfo: WLoginSimpleInfo, - val appPri: Long, - val a2ExpiryTime: Long, - val loginBitmap: Long, - val tgt: ByteArray, - val a2CreationTime: Long, - val tgtKey: ByteArray, - val userStSig: UserStSig, + var appPri: Long, + var a2ExpiryTime: Long, + var loginBitmap: Long, + var tgt: ByteArray, + var a2CreationTime: Long, + var tgtKey: ByteArray, + var userStSig: UserStSig, /** * TransEmpPacket 加密使用 */ - val userStKey: ByteArray, - val userStWebSig: UserStWebSig, - val userA5: UserA5, - val userA8: UserA8, - val lsKey: LSKey, - val sKey: SKey, - val userSig64: UserSig64, - val openId: ByteArray, - val openKey: OpenKey, - val vKey: VKey, - val accessToken: AccessToken, - val d2: D2, - val d2Key: ByteArray, - val sid: Sid, - val aqSig: AqSig, - val psKeyMap: PSKeyMap, - val pt4TokenMap: Pt4TokenMap, - val superKey: ByteArray, - val payToken: ByteArray, - val pf: ByteArray, - val pfKey: ByteArray, - val da2: ByteArray, - // val pt4Token: ByteArray, - val wtSessionTicket: WtSessionTicket, - val wtSessionTicketKey: ByteArray, - val deviceToken: ByteArray + var userStKey: ByteArray, + var userStWebSig: UserStWebSig, + var userA5: UserA5, + var userA8: UserA8, + var lsKey: LSKey, + var sKey: SKey, + var userSig64: UserSig64, + var openId: ByteArray, + var openKey: OpenKey, + var vKey: VKey, + var accessToken: AccessToken, + var d2: D2, + var d2Key: ByteArray, + var sid: Sid, + var aqSig: AqSig, + var psKeyMap: PSKeyMap, + var pt4TokenMap: Pt4TokenMap, + var superKey: ByteArray, + var payToken: ByteArray, + var pf: ByteArray, + var pfKey: ByteArray, + var da2: ByteArray, + // val pt4Token: ByteArray, + var wtSessionTicket: WtSessionTicket, + var wtSessionTicketKey: ByteArray, + var deviceToken: ByteArray ) { override fun toString(): String { - return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, G=${G.toUHexString()}, dpwd=${dpwd.toUHexString()}, randSeed=${randSeed.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()})" + 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/EncryptMethod.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.kt index 9212bf445..86b8c5e42 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.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. @@ -42,6 +42,19 @@ internal interface EncryptMethodSessionKey : EncryptMethod { } } +internal class EncryptMethodSessionKeyNew( + val wtSessionTicket: ByteArray, // t133 + val wtSessionTicketKey: ByteArray, // t134 +) : EncryptMethod { + override val id: Int get() = 69 + + override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket = + buildPacket { + writeShortLVByteArray(wtSessionTicket) + encryptAndWrite(wtSessionTicketKey, body) + } +} + internal class EncryptMethodSessionKeyLoginState2(override val sessionKey: ByteArray) : EncryptMethodSessionKey { override val currentLoginState: Int get() = 2 diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacketAndroid.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacketAndroid.kt index ce4a91c89..58585e71c 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacketAndroid.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacketAndroid.kt @@ -135,7 +135,7 @@ internal val NO_ENCRYPT: ByteArray = ByteArray(0) /** * com.tencent.qphone.base.util.CodecWarpper#encodeRequest(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int, int, java.lang.String, byte, byte, byte, byte[], byte[], boolean) */ -internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket( +internal inline fun OutgoingPacketFactory.buildLoginOutgoingPacket( client: QQAndroidClient, bodyType: Byte, extraData: ByteArray = EMPTY_BYTE_ARRAY, @@ -143,10 +143,10 @@ internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket( commandName: String = this.commandName, key: ByteArray = KEY_16_ZEROS, body: BytePacketBuilder.(sequenceId: Int) -> Unit -): OutgoingPacket { +): OutgoingPacketWithRespType { val sequenceId: Int = client.nextSsoSequenceId() - return OutgoingPacket(name, commandName, sequenceId, buildPacket { + return OutgoingPacketWithRespType(name, commandName, sequenceId, buildPacket { writeIntLVPacket(lengthOffset = { it + 4 }) { writeInt(0x00_00_00_0A) writeByte(bodyType) 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 814d45eee..1eaf2695e 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -394,7 +394,8 @@ internal object KnownPacketFactories { this.discardExact(1) val packet = when (encryptionMethod) { 4 -> { - var data = TEA.decrypt(this, bot.client.ecdh.keyPair.initialShareKey, (this.remaining - 1).toInt()) + var data = + TEA.decrypt(this, bot.client.ecdh.keyPair.initialShareKey, length = (this.remaining - 1).toInt()) val peerShareKey = bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey()) @@ -402,19 +403,26 @@ internal object KnownPacketFactories { packetFactory.decode(bot, data) } + 3 -> { + // session + val data = TEA.decrypt( + this, + bot.client.wLoginSigInfo.wtSessionTicketKey, + length = (this.remaining - 1).toInt() + ) + + packetFactory.decode(bot, data) + } 0 -> { val data = if (bot.client.loginState == 0) { - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - ByteArrayPool.useInstance(this.remaining.toInt()) { byteArrayBuffer -> - val size = (this.remaining - 1).toInt() - this.readFully(byteArrayBuffer, 0, size) + val size = (this.remaining - 1).toInt() + val byteArrayBuffer = this.readBytes(size) - runCatching { - TEA.decrypt(byteArrayBuffer, bot.client.ecdh.keyPair.initialShareKey, size) - }.getOrElse { - TEA.decrypt(byteArrayBuffer, bot.client.randomKey, size) - }.toReadPacket() - } + runCatching { + TEA.decrypt(byteArrayBuffer, bot.client.ecdh.keyPair.initialShareKey, size) + }.getOrElse { + TEA.decrypt(byteArrayBuffer, bot.client.randomKey, size) + }.toReadPacket() } else { TEA.decrypt(this, bot.client.randomKey, 0, (this.remaining - 1).toInt()) } 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 7aa646bbe..ae8fb0b87 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt @@ -11,24 +11,39 @@ package net.mamoe.mirai.internal.network.protocol.packet -import kotlinx.io.core.BytePacketBuilder -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.toByteArray -import kotlinx.io.core.writeFully +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.LoginType +import net.mamoe.mirai.internal.network.writeLoginExtraData 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 net.mamoe.mirai.utils.* import kotlin.random.Random +private val Char.isHumanReadable get() = this in '0'..'9' || this in 'a'..'z' || this in 'A'..'Z' || this in """ <>?,.";':/\][{}~!@#$%^&*()_+-=`""" || this in "\n\r" + +internal fun TlvMap.smartToString(leadingLineBreak: Boolean = true, sorted: Boolean = true): String { + fun ByteArray.valueToString(): String { + val str = this.encodeToString() + return if (str.all { it.isHumanReadable }) str + else this.toUHexString() + } + + val map = if (sorted) entries.sortedBy { it.key } else this.entries + + return buildString { + if (leadingLineBreak) appendLine() + appendLine("count=${map.size}") + appendLine(map.joinToString("\n") { (key, value) -> + "0x" + key.toShort().toUHexString("") + " = " + value.valueToString() + }) + } +} + /** * 显式表示一个 [ByteArray] 是一个 tlv 的 body */ @@ -41,7 +56,7 @@ internal fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) { writeShort(1) // _ip_ver writeInt(Random.nextInt()) writeInt(uin.toInt()) - writeInt(currentTimeMillis().toInt()) + writeInt(currentTimeSeconds().toInt()) writeFully(ip) writeShort(0) } shouldEqualsTo 20 @@ -76,7 +91,7 @@ internal fun BytePacketBuilder.t18( writeShort(0x18) writeShortLVPacket { writeShort(1) //_ping_version - writeInt(1536) //_sso_version + writeInt(0x00_00_06_00) //_sso_version=1536 writeInt(appId.toInt()) writeInt(appClientVersion) writeInt(uin.toInt()) @@ -106,6 +121,9 @@ internal fun BytePacketBuilder.t106( ) } +/** + * A1 + */ internal fun BytePacketBuilder.t106( appId: Long = 16L, subAppId: Long, @@ -143,7 +161,7 @@ internal fun BytePacketBuilder.t106( writeLong(uin) } - writeInt(currentTimeMillis().toInt()) + writeInt(currentTimeSeconds().toInt()) writeFully(ByteArray(4)) // ip // no need to write actual ip writeByte(isSavePassword.toByte()) writeFully(passwordMd5) @@ -607,6 +625,11 @@ internal fun BytePacketBuilder.t185() { } internal fun BytePacketBuilder.t400( + /** + * if (var1[2] != null && var1[2].length > 0) { + this._G = (byte[])var1[2].clone(); + } + */ g: ByteArray, // 用于加密这个 tlv uin: Long, guid: ByteArray, @@ -617,15 +640,14 @@ internal fun BytePacketBuilder.t400( ) { writeShort(0x400) writeShortLVPacket { - writeByte(1) // version - writeLong(uin) - encryptAndWrite(g) { + writeByte(1) // version + writeLong(uin) writeFully(guid) writeFully(dpwd) writeInt(appId.toInt()) writeInt(subAppId.toInt()) - writeLong(currentTimeMillis()) + writeInt(currentTimeSeconds().toInt()) writeFully(randomSeed) } } @@ -757,8 +779,38 @@ internal fun BytePacketBuilder.t536( // 1334 } } +internal fun BytePacketBuilder.t536( // 1334 + loginExtraData: Collection +) { + writeShort(0x536) + writeShortLVPacket { + //com.tencent.loginsecsdk.ProtocolDet#packExtraData + writeByte(1) // const + writeByte(loginExtraData.size.toByte()) // data count + for (extraData in loginExtraData) { + writeLoginExtraData(extraData) + } + } +} + internal fun BytePacketBuilder.t525( - t536: ByteReadPacket + loginExtraData: Collection, +) { + writeShort(0x525) + writeShortLVPacket { + writeShort(1) + t536(loginExtraData) + } +} + +internal fun BytePacketBuilder.t525( + t536: ByteReadPacket = buildPacket { + t536(buildPacket { + //com.tencent.loginsecsdk.ProtocolDet#packExtraData + writeByte(1) // const + writeByte(0) // data count + }.readBytes()) + } ) { writeShort(0x525) writeShortLVPacket { @@ -767,6 +819,14 @@ internal fun BytePacketBuilder.t525( } } +internal fun BytePacketBuilder.t544( // 1334 +) { + writeShort(0x544) + writeShortLVPacket { + writeFully(byteArrayOf(0, 0, 0, 11)) // means native throws exception + } +} + internal fun BytePacketBuilder.t318( tgtQR: ByteArray // unknown ) { 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 4cd53e86b..3c48bb9ef 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 @@ -27,7 +27,6 @@ import net.mamoe.mirai.internal.message.contextualBugReportException import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.getRandomByteArray -import net.mamoe.mirai.internal.network.guid import net.mamoe.mirai.internal.network.protocol.data.jce.* import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769 import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline 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 f90ed3195..b5513c622 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 @@ -13,9 +13,11 @@ package net.mamoe.mirai.internal.network.protocol.packet.login import kotlinx.io.core.* import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.* +import net.mamoe.mirai.internal.network.DebuggingProperties.SHOW_TLV_MAP_ON_LOGIN_SUCCESS import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLoginExt import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.analysisTlv0x531 +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.orEmpty import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.crypto.TEA import net.mamoe.mirai.internal.utils.soutv @@ -76,7 +78,7 @@ internal class WtLogin { writeShort(17) // subCommand writeShort(12) t100(16, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap) - t108(client.device.imei.toByteArray()) + t108(client.ksid) t109(client.device.androidId) t8(2052) t142(client.apkId) @@ -128,7 +130,7 @@ internal class WtLogin { } } - class DeviceLockLogin(val t402: ByteArray) : LoginPacketResponse() { + object DeviceLockLogin : LoginPacketResponse() { override fun toString(): String = "WtLogin.Login.LoginPacketResponse.DeviceLockLogin" } } @@ -142,13 +144,28 @@ internal class WtLogin { discardExact(2) val tlvMap: TlvMap = this._readTLVMap() + + if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) { + tlvMap.smartToString().soutv("tlvMap outer") + } + // tlvMap.printTLVMap() tlvMap[0x161]?.let { bot.client.analysisTlv161(it) } + tlvMap[0x403]?.let { bot.client.randSeed = it } + tlvMap[0x402]?.let { bot.client.t402 = it } + + // tlvMap[0x402]?.let { t402 -> +// bot.client.G = buildPacket { +// writeFully(bot.client.device.guid) +// writeFully(bot.client.dpwd) +// writeFully(tlvMap[0x402] ?: EMPTY_BYTE_ARRAY) +// }.readBytes().md5() + // } return when (type.toInt()) { 0 -> onLoginSuccess(tlvMap, bot) 2 -> onSolveLoginCaptcha(tlvMap, bot) 160, 239 /*-96*/ -> onUnsafeDeviceLogin(tlvMap) - 204 /*-52*/ -> onSMSVerifyNeeded(tlvMap, bot) + 204 /*-52*/ -> onDevLockLogin(tlvMap, bot) // 1, 15 -> onErrorMessage(tlvMap) ?: error("Cannot find error message") else -> { onErrorMessage(tlvMap) @@ -158,13 +175,17 @@ internal class WtLogin { } - private fun onSMSVerifyNeeded( + private fun onDevLockLogin( tlvMap: TlvMap, bot: QQAndroidBot ): LoginPacketResponse.DeviceLockLogin { bot.client.t104 = tlvMap.getOrFail(0x104) + bot.run { + // client.dpwd = getRandomString(16).toByteArray() + client.G = (client.device.guid + client.dpwd + tlvMap.getOrFail(0x402)).md5() + } // println("403: " + tlvMap[0x403]?.toUHexString()) - return LoginPacketResponse.DeviceLockLogin(tlvMap.getOrFail(0x402)) + return LoginPacketResponse.DeviceLockLogin } private fun onUnsafeDeviceLogin(tlvMap: TlvMap): LoginPacketResponse.UnsafeLogin { @@ -222,8 +243,11 @@ internal class WtLogin { // 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 val tlvMap119 = this._readTLVMap() - // added for exchange_emp sub command 15 - tlvMap119[0x16a]?.let { bot.client.tlv16a = it } + if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) { + tlvMap119.smartToString().soutv("TlvMap119") + } + + tlvMap119[0x106]?.let { client.analyzeTlv106(it) } // ??? tlvMap119[0x1c]?.read { @@ -242,18 +266,11 @@ internal class WtLogin { tlvMap119[0x118]?.let { client.mainDisplayName = it } tlvMap119[0x108]?.let { client.ksid = it } - var openId: ByteArray - var openKey: ByteArray - - when (val t125 = tlvMap119[0x125]) { - null -> { - openId = byteArrayOf() - openKey = byteArrayOf() - } - else -> t125.read { - openId = readUShortLVByteArray() - openKey = readUShortLVByteArray() - } + var openId: ByteArray? = null + var openKey: ByteArray? = null + tlvMap119[0x125]?.read { + openId = readUShortLVByteArray() + openKey = readUShortLVByteArray() } /* @@ -289,66 +306,44 @@ internal class WtLogin { client.qrPushSig = tlvMap119[0x317] ?: byteArrayOf() - val face: Int - val gender: Int - val nick: String - val age: Int - when (val t11a = tlvMap119[0x11a]) { - null -> { - face = 0 - age = 0 - gender = 0 - nick = "" - } - else -> t11a.read { - face = readUShort().toInt() - age = readUByte().toInt() - gender = readUByte().toInt() - nick = readString(readByte().toInt() and 0xff) - } + var payToken: ByteArray? = null + tlvMap119[0x199]?.read { + openId = readUShortLVByteArray() + payToken = readUShortLVByteArray() } - val payToken: ByteArray - when (val t199 = tlvMap119[0x199]) { - null -> payToken = byteArrayOf() - else -> t199.read { - openId = readUShortLVByteArray() - payToken = readUShortLVByteArray() - } - } - - val pf: ByteArray - val pfKey: ByteArray - when (val t200 = tlvMap119[0x200]) { - null -> { - pf = byteArrayOf() - pfKey = byteArrayOf() - } - else -> t200.read { - pf = readUShortLVByteArray() - pfKey = readUShortLVByteArray() - } + var pf: ByteArray? = null + var pfKey: ByteArray? = null + tlvMap119[0x200]?.let { + pf = readUShortLVByteArray() + pfKey = readUShortLVByteArray() } // TODO sigMap??? =0x21410e0 // from qq val creationTime = currentTimeSeconds() - val expireTime = creationTime + 2160000L + val expireTime = creationTime + 21600 - val outPSKeyMap: PSKeyMap = mutableMapOf() - val outPt4TokenMap: Pt4TokenMap = mutableMapOf() + val outPSKeyMap: PSKeyMap? + val outPt4TokenMap: Pt4TokenMap? + if (tlvMap119[0x512] != null) { + outPSKeyMap = mutableMapOf() + outPt4TokenMap = mutableMapOf() + parsePSKeyMapAndPt4TokenMap( + tlvMap119[0x512]!!, + creationTime, + expireTime, + outPSKeyMap, + outPt4TokenMap + ) + } else { + outPSKeyMap = null + outPt4TokenMap = null + } - parsePSKeyMapAndPt4TokenMap( - tlvMap119[0x512] ?: error("Cannot find tlv 0x512, which is pskeyMap and pt4tokenMap"), - creationTime, - expireTime, - outPSKeyMap, - outPt4TokenMap - ) - - var a1: ByteArray? = null - var noPicSig: ByteArray? = null + var a1: ByteArray? = tlvMap119.getOrFail(0x106) + var noPicSig: ByteArray? = tlvMap119[0x16a] tlvMap119[0x531]?.let { analysisTlv0x531(it) { arg1, arg2 -> a1 = arg1 @@ -356,58 +351,65 @@ internal class WtLogin { } } - client.wLoginSigInfo = WLoginSigInfo( - uin = client.uin, - encryptA1 = a1, - noPicSig = noPicSig, - G = byteArrayOf(), // defaults {}, from asyncContext._G - dpwd = byteArrayOf(), // defaults {}, from asyncContext._G - randSeed = tlvMap119.getOrEmpty(0x403), // or from asyncContext._t403.get_body_data() - simpleInfo = WLoginSimpleInfo( + 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) + } + } else { + client.wLoginSigInfo = WLoginSigInfo( uin = client.uin, - face = face, - age = age, - gender = gender, - nick = nick, - imgType = client.reserveUinInfo?.imgType ?: byteArrayOf(), - imgFormat = client.reserveUinInfo?.imgFormat ?: byteArrayOf(), - imgUrl = client.reserveUinInfo?.imgUrl ?: byteArrayOf(), - mainDisplayName = tlvMap119[0x118] ?: error("Cannot find tlv 0x118") - ), - appPri = tlvMap119[0x11f]?.let { it.read { discardExact(4); readUInt().toLong() } } - ?: 4294967295L, - a2ExpiryTime = expireTime, - loginBitmap = 0, // from asyncContext._login_bitmap - tgt = tlvMap119.getOrEmpty(0x10a), - a2CreationTime = creationTime, - tgtKey = tlvMap119.getOrEmpty(0x10d), - sKey = SKey(tlvMap119.getOrEmpty(0x120), creationTime, expireTime), - userSig64 = UserSig64(tlvMap119.getOrEmpty(0x121), creationTime), - accessToken = AccessToken(tlvMap119.getOrEmpty(0x136), creationTime), - openId = openId, - openKey = OpenKey(openKey, creationTime), - d2 = D2(tlvMap119.getOrEmpty(0x143), creationTime, expireTime), - d2Key = tlvMap119.getOrEmpty(0x305), - sid = Sid(tlvMap119.getOrEmpty(0x164), creationTime, expireTime), - aqSig = AqSig(tlvMap119.getOrEmpty(0x171), creationTime), - psKeyMap = outPSKeyMap, - pt4TokenMap = outPt4TokenMap, - superKey = tlvMap119.getOrEmpty(0x16d), - payToken = payToken, - pf = pf, - pfKey = pfKey, - da2 = tlvMap119.getOrEmpty(0x203), - wtSessionTicket = WtSessionTicket(tlvMap119.getOrEmpty(0x133), creationTime), - wtSessionTicketKey = tlvMap119.getOrEmpty(0x134), - deviceToken = tlvMap119.getOrEmpty(0x322), - vKey = VKey(tlvMap119.getOrEmpty(0x136), creationTime, expireTime), - userStWebSig = UserStWebSig(tlvMap119.getOrEmpty(0x103), creationTime, expireTime), - userStSig = UserStSig((tlvMap119.getOrEmpty(0x114)), creationTime), - userStKey = tlvMap119.getOrEmpty(0x10e), - lsKey = LSKey(tlvMap119.getOrEmpty(0x11c), creationTime, expireTime), - userA5 = UserA5(tlvMap119.getOrEmpty(0x10b), creationTime), - userA8 = UserA8(tlvMap119.getOrEmpty(0x102), creationTime, expireTime) - ) + encryptA1 = a1, + noPicSig = noPicSig, + simpleInfo = WLoginSimpleInfo( + uin = client.uin, + imgType = client.reserveUinInfo?.imgType ?: byteArrayOf(), + imgFormat = client.reserveUinInfo?.imgFormat ?: byteArrayOf(), + 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() } } + ?: 4294967295L, // defaults {}, from asyncContext._G + a2ExpiryTime = expireTime, // or from asyncContext._t403.get_body_data() + loginBitmap = 0, + tgt = tlvMap119.getOrEmpty(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), + 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), + userSig64 = UserSig64(tlvMap119.getOrEmpty(0x121), creationTime), + openId = openId.orEmpty(), + openKey = OpenKey(openKey.orEmpty(), creationTime), + vKey = VKey(tlvMap119.getOrEmpty(0x136), creationTime, expireTime), + accessToken = AccessToken(tlvMap119.getOrEmpty(0x136), creationTime), + d2 = D2(tlvMap119.getOrEmpty(0x143), creationTime, expireTime), + d2Key = tlvMap119.getOrEmpty(0x305), + sid = Sid(tlvMap119.getOrEmpty(0x164), creationTime, expireTime), + aqSig = AqSig(tlvMap119.getOrEmpty(0x171), creationTime), + psKeyMap = outPSKeyMap.orEmpty().toMutableMap(), + pt4TokenMap = outPt4TokenMap.orEmpty().toMutableMap(), + superKey = tlvMap119.getOrEmpty(0x16d), + payToken = payToken.orEmpty(), + pf = pf.orEmpty(), + pfKey = pfKey.orEmpty(), + da2 = tlvMap119.getOrEmpty(0x203), + wtSessionTicket = WtSessionTicket(tlvMap119.getOrEmpty(0x133), creationTime), + wtSessionTicketKey = tlvMap119.getOrEmpty(0x134), + deviceToken = tlvMap119.getOrEmpty(0x322) + ) + } //bot.network.logger.error(client.wLoginSigInfo.psKeyMap["qun.qq.com"]?.data?.encodeToString()) } } @@ -419,22 +421,8 @@ internal class WtLogin { internal object ExchangeEmp : OutgoingPacketFactory("wtlogin.exchange_emp"), WtLoginExt { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Login.LoginPacketResponse { - discardExact(2) // subCommand - // println("subCommand=$subCommand") - val type = readUByte() - // println("type=$type") - - discardExact(2) - val tlvMap: TlvMap = this._readTLVMap() - // tlvMap.printTLVMap() - tlvMap[0x161]?.let { bot.client.analysisTlv161(it) } - tlvMap.soutv("RESP") - return when (type.toInt()) { - 0 -> Login.onLoginSuccess(tlvMap, bot) // todo - else -> { - onErrorMessage(tlvMap) - ?: error("Cannot find error message, unknown login result type: $type, TLVMap = ${tlvMap._miraiContentToString()}") - } + return Login.run { + decode(bot) } } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt index 1aeff7d3b..30fb73899 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt @@ -10,9 +10,12 @@ 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 +import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray +import java.security.SecureRandom +import java.util.* +import kotlin.math.abs internal object WtLogin15 : WtLoginExt { private const val subCommand = 15.toShort() @@ -21,34 +24,134 @@ internal object WtLogin15 : WtLoginExt { 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) + ) = WtLogin.ExchangeEmp.buildOutgoingUniPacket(client, bodyType = 2, key = ByteArray(16)) { sequenceId -> +// writeSsoPacket(client, client.subAppId, WtLogin.ExchangeEmp.commandName, sequenceId = sequenceId) { + writeOicqRequestPacket( + client, + EncryptMethodSessionKeyNew( + client.wLoginSigInfo.wtSessionTicket.data, + client.wLoginSigInfo.wtSessionTicketKey + ), + 0x0810 + ) { + writeShort(subCommand) // subCommand + writeShort(24) - 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() + +// writeFully(("00 18 00 16 00 01 00 00 06 00 00 00 00 10 00 00 00 00 76 E4 B8 DD 00 00 00 00 00 01 00 14 00 01 5A 11 60 11 76 E4 B8 DD 60 0D 44 35 90 E8 11 1B 00 00 " + +// //"01 06 " + +// //client.wLoginSigInfo.encryptA1!!.size.toShort().toUHexString() + " " + client.wLoginSigInfo.encryptA1!!.toUHexString() + " "+ +// "01 06 00 78 CD 52 75 7E B7 CF A0 FC 58 15 1E 79 29 86 F0 28 06 AE 4D E4 EF 39 6B 8B 64 19 90 7E DF 2B 71 48 53 E6 71 19 4A 7F 81 06 EB 19 A2 CA 8B B3 47 D9 07 73 04 71 99 B3 F2 7E 2B F0 93 6B A0 FA 94 D3 15 41 55 6C EF 87 96 47 22 5C 6B 10 0F EC 27 3A 8F 83 0A 75 8B 95 EA 81 02 AA C6 E7 C8 6C 7B C3 93 4E EC 08 2E DB 9B 3E 4B 17 7C 06 1C 66 31 F4 EE 70 55 87 49 A2 5B 25 " + +// "01 16 00 0E 00 0A F7 FF 7C 00 01 " + +// "04 00 01 5F 5E 10 E2 01 00 00 16 00 01 00 00 00 0F 00 00 00 10 20 02 FD E2 00 00 00 00 02 10 10 E0 01 07 00 06 00 00 00 00 00 01 01 44 01 B8 20 3F 4F ED A0 CD 3B CC 07 47 A3 91 8B B5 C8 18 EA D1 45 8A 0F F3 1F CD 95 85 00 7C AD 7F 18 7C 43 B6 A6 C3 FF 53 D5 F6 F4 E6 75 1D 4F AF A4 DF C5 EB 3D 6F C5 C3 21 E9 C7 66 8A EF 4A BD 45 CC D2 A5 34 E4 1D F5 9B 07 9B 65 6A 35 BD B4 0B D1 94 43 18 1C 48 1D B5 2C B5 62 FA E9 E1 35 14 13 BC EE BF FA FD 98 A4 72 A8 1A 71 9C 77 2D 4D BF 58 3A 0A 72 D4 B0 A2 DA 6C FC 04 49 F5 05 3D EF 60 E5 92 9A 04 96 AA A4 22 4E 13 8F 63 3B D9 54 B8 DC CC 2D A0 0B 8B B9 9A D7 8A D0 E4 A4 EE 4F 2E 8D 52 86 35 74 70 93 A0 21 0D 98 DE A4 5B B9 79 A5 8E 31 8D A5 AC 0B DB A8 65 6C 93 1C EE FB E9 A2 FD 22 90 0B A5 41 3D 1F 2B A9 84 FF D8 64 DF 94 48 B3 D6 20 47 F3 12 D3 63 F0 84 1C 6A 1E 0A 8B 13 02 EA F2 C3 3E 41 87 59 5A 65 80 E4 7C 3E FC 52 70 20 26 ED 27 91 01 C0 4D 50 23 B9 1F 59 77 AE D5 4D C8 57 DB 86 E9 5B 98 0C 95 A2 15 AB 01 3E 10 D5 3B 01 57 FE C8 88 80 4D 1A 8A 4D 64 89 C3 7F E6 73 D3 04 C8 EA 98 E2 F3 82 48 7D FC D7 CF 07 " + +// "F4 33 F1 1E D3 1C 0D 48 37 3A 50 0B 39 28 AB F3 4F BF C9 D8 70 6F B9 F2 FB 46 5A 4B 21 DE D8 B8 30 A1 FC C6 09 60 4E 07 21 28 F7 CC 7A A0 07 1C 87 42 90 D3 40 07 1B 35 52 56 31 E2 C0 6D 1F 79 43 6C 63 46 D4 92 EE 30 A2 D2 D6 0F 79 46 2A EF C7 C6 CF 54 1D 03 FE 80 D4 28 87 AF 2D 1A FF 71 99 FC 23 09 79 B0 9D B4 E9 0F 4E E3 D2 79 10 2C C7 6E 30 34 A5 66 2E 33 00 08 D0 58 2B 7F D8 E6 21 2E 7E 30 01 42 00 18 00 00 00 14 63 6F 6D 2E 74 65 6E 63 65 6E 74 2E 6D 6F 62 69 6C 65 71 71 01 45 00 10 E2 9A BF 20 0D 00 CA F0 4D 28 CA 66 BF 31 6E AF 01 6A 00 38 CA 78 6A 94 A7 A4 F3 BC 28 58 3C FF 53 41 4C A3 5B 98 AB 7C 21 FB 34 D9 28 59 91 D5 15 12 04 A0 7E 27 25 DF 7A A0 06 87 EB 13 12 10 CD 80 85 78 17 F9 1C D6 21 A7 AF 8C 01 54 00 04 00 01 38 E4 01 41 00 1C 00 01 00 10 43 68 69 6E 61 20 4D 6F 62 69 6C 65 20 47 53 4D 00 02 00 04 77 69 66 69 00 08 00 08 00 00 00 00 08 04 00 00 05 11 00 D3 00 0E 01 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 01 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 01 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 01 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 01 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 01 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 01 00 11 67 61 6D 65 63 65 6E 74 65 72 2E 71 71 2E 63 6F 6D 01 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 01 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 01 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 01 00 09 74 69 2E 71 71 2E 63 6F 6D 01 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 01 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 01 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 01 47 00 1D 00 00 00 10 00 05 38 2E 35 2E 35 00 10 A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D 01 77 00 11 01 5F EC 50 93 00 0A 36 2E 30 2E 30 2E 32 34 36 33 " + +// "04 00 00 48 2F C5 1F 51 62 56 7E A0 39 92 E9 DC 0D 55 00 E5 BA 24 8D 38 47 8F 1C 7F 6A 91 54 62 D9 B3 25 B0 2D 10 A2 15 5E B4 C7 5F 76 CA AC EF 76 88 92 F4 FF 16 55 98 EB 01 BB 4D 34 95 9E 8E 80 59 4E B3 99 1E 80 CD 4E 27 A4 8E" + +// " 01 87 00 10 30 86 EC F4 ED 94 5D D2 6F 88 8A 39 46 6C 22 7D 01 88 00 10 9D B6 A8 DF CB C1 79 06 EB 5D 95 FA 20 5A 9E 11 01 94 00 10 73 A1 26 09 B8 99 62 29 04 95 B9 9E 5C DA 22 8C 02 02 00 1B 00 10 36 85 4A A7 02 92 35 CE 9F B5 A2 D0 7A E4 88 F8 00 07 22 38 32 45 46 45 22 05 16 00 04 00 00 00 00 05 21 00 06 00 00 00 00 00 00 05 25 00 47 00 01 05 36 00 41 01 03 00 00 00 00 76 E4 B8 DD 04 1B 11 E8 90 60 0D 3C 28 20 02 FD E2 00 00 00 00 76 E4 B8 DD 04 1B 11 E8 90 60 0D 3C 28 20 02 FD E2 00 00 00 00 76 E4 B8 DD 04 1B 11 E8 90 60 0D 3C 95 20 02 FD E2 " + +// "").hexToBytes()) +// return@writeOicqRequestPacket + + t18(appId, uin = client.uin) + t1(client.uin, ByteArray(4)) + + // t106(client = client) + writeShort(0x106) + val encryptA1 = client.wLoginSigInfo.encryptA1!! + writeShortLVByteArray(encryptA1) +// kotlin.run { +// val key = (client.account.passwordMd5 + ByteArray(4) + client.uin.toInt().toByteArray()).md5() +// kotlin.runCatching { +// TEA.decrypt(encryptA1, key).toUHexString() +// }.soutv("DEC") // success +// } + + // val a1 = kotlin.runCatching { + // TEA.decrypt(encryptA1, buildPacket { + // writeFully(client.device.guid) + // writeFully(client.dpwd) + // writeFully(client.randSeed) + // }.readBytes().md5()) + // }.recoverCatching { + // client.tryDecryptOrNull(encryptA1) { it }!! + // }.getOrElse { + // encryptA1.soutv("ENCRYPT A1") + // client.soutv("CLIENT") + // // exitProcess(1) + // // error("Failed to decrypt A1") + // encryptA1 + // } + + t116(client.miscBitMap, client.subSigMap) + //t116(0x08F7FF7C, 0x00010400) + + //t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap) + //t100(appId, 1, client.appClientVersion, client.ssoVersion, mainSigMap = 1048768) + t100(appId, 2, client.appClientVersion, client.ssoVersion, client.mainSigMap) + + t107(0) + // t108(client.ksid) // 第一次 exchange 没有 108 + t144(client) + t142(client.apkId) + t145(client.device.guid) + + val noPicSig = + client.wLoginSigInfo.noPicSig ?: error("Internal error: doing exchange emp 15 while noPicSig=null") + t16a(noPicSig) + + t154(0) + 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) + + // new + t400( + g = client.G, + uin = client.uin, + guid = client.device.guid, + dpwd = client.dpwd, + appId = 1, + subAppId = 16, + randomSeed = client.randSeed + ) + + t187(client.device.macAddress) + t188(client.device.androidId) + t194(client.device.imsiMd5) + t202(client.device.wifiBSSID, client.device.wifiSSID) + t516() + + t521() // new + t525(client.loginExtraData) // new + //t544() // new + } + + // } + } +} + +internal fun get_mpasswd(): String { + var var5: String + run label41@{ + val var6: ByteArray = SecureRandom.getSeed(16) + var var0 = 0 + var var4 = "" + while (true) { + var5 = var4 + if (var0 >= var6.size) { + return var5 } + val var3: Boolean = Random().nextBoolean() + val var2: Int = abs(var6[var0] % 26) + + val var1: Byte = if (var3) { + 97 + } else { + 65 + } + var4 += ((var1 + var2).toChar()).toString() + + ++var0 } } -} \ No newline at end of file +} diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt index 26bb2e5a6..f0a331cfd 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt @@ -9,18 +9,14 @@ 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 -> + client: QQAndroidClient + ) = 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 @@ -28,7 +24,7 @@ internal object WtLogin20 : WtLoginExt { t8(2052) t104(client.t104) t116(client.miscBitMap, client.subSigMap) - t401((client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5()) + t401(client.G) // (client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5() } } } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt index ff67f7f84..ca786f998 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt @@ -9,11 +9,8 @@ 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 @@ -32,6 +29,7 @@ internal object WtLogin9 : WtLoginExt { t18(appId, client.appClientVersion, client.uin) t1(client.uin, client.device.ipAddress) + t106(appId, client) /* // from GetStWithPasswd @@ -98,13 +96,7 @@ internal object WtLogin9 : WtLoginExt { t516() t521() - t525(buildPacket { - t536(buildPacket { - //com.tencent.loginsecsdk.ProtocolDet#packExtraData - writeByte(1) // const - writeByte(0) // data count - }.readBytes()) - }) + t525() // this.build().debugPrint("傻逼") // ignored t318 because not logging in by QR 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 b81b62fc8..6aeefefb1 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,11 +12,12 @@ 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.EMPTY_BYTE_ARRAY 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.crypto.TEA import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray import net.mamoe.mirai.utils.* @@ -97,15 +98,22 @@ internal interface WtLoginExt { // so as not to register to global extension /** * login extra data + * + * oicq/wlogin_sdk/request/oicq_request.java:1445 */ 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() - ) + discardExact(1) + repeat(readByte().toInt()) { + loginExtraData.add( + LoginExtraData( // args are to correct order + uin = readLong(), + ip = readBytes(readByte().toInt() and 0xff), + time = readInt(), // correct + version = readInt() + ) + ) + } } /** @@ -180,5 +188,23 @@ internal interface WtLoginExt { // so as not to register to global extension } } + fun QQAndroidClient.analyzeTlv106(t106: ByteArray) { + val tgtgtKey = decodeA1(t106) { + discardExact(51) + readBytes(16) + } + this.tgtgtKey = tgtgtKey + } + fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) +} + +internal inline fun QQAndroidClient.decodeA1(a1: ByteArray, block: ByteReadPacket.() -> R): R { + val key = (account.passwordMd5 + ByteArray(4) + uin.toInt().toByteArray()).md5() + val v = TEA.decrypt(a1, key) + return v.toReadPacket().withUse(block) +} + +internal fun ByteArray?.orEmpty(size: Int = 0): ByteArray { + return this ?: if (size == 0) EMPTY_BYTE_ARRAY else ByteArray(size) } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt b/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt index 9035fcf7e..d9a77c527 100644 --- a/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt +++ b/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.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. @@ -34,8 +34,8 @@ internal class MiraiProtocolInternal( init { protocols[MiraiProtocol.ANDROID_PHONE] = MiraiProtocolInternal( "com.tencent.mobileqq", - 537066419, - "8.4.18", + 537066978, + "8.5.5", "6.0.0.2454", 184024956, 0x10400,