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
This commit is contained in:
Him188 2021-01-26 19:32:27 +08:00 committed by GitHub
parent cc87f6833f
commit 7a6e930fc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 552 additions and 284 deletions

View File

@ -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 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 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 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 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 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; 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 getDevice ()[B
public final fun getDisplay ()[B public final fun getDisplay ()[B
public final fun getFingerprint ()[B public final fun getFingerprint ()[B
public final fun getGuid ()[B
public final fun getImei ()Ljava/lang/String; public final fun getImei ()Ljava/lang/String;
public final fun getImsiMd5 ()[B public final fun getImsiMd5 ()[B
public final fun getIpAddress ()[B public final fun getIpAddress ()[B

View File

@ -37,6 +37,16 @@ public annotation class LowLevelApi
*/ */
@LowLevelApi @LowLevelApi
public interface LowLevelApiAccessor { public interface LowLevelApiAccessor {
/**
* 主动刷新 keys, SKey, PSKey .
*
* 通常 mirai 会自动刷新, 不需要手动刷新.
*
* @since 2.2
*/
@MiraiExperimentalApi
public suspend fun refreshKeys(bot: Bot)
/** /**
* 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]). * 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
* *

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.utils
import kotlinx.io.core.toByteArray import kotlinx.io.core.toByteArray
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoNumber
@ -42,6 +43,10 @@ public class DeviceInfo(
public val androidId: ByteArray get() = display public val androidId: ByteArray get() = display
public val ipAddress: ByteArray get() = byteArrayOf(192.toByte(), 168.toByte(), 1, 123) public val ipAddress: ByteArray get() = byteArrayOf(192.toByte(), 168.toByte(), 1, 123)
@Transient
@MiraiInternalApi
public val guid: ByteArray = generateGuid(androidId, macAddress)
@Serializable @Serializable
public class Version( public class Version(
public val incremental: ByteArray = "5891938".toByteArray(), public val incremental: ByteArray = "5891938".toByteArray(),
@ -127,6 +132,14 @@ public fun DeviceInfo.generateDeviceInfoData(): ByteArray {
) )
} }
/**
* Defaults "%4;7t>;28<fc.5*6".toByteArray()
*/
@Suppress("RemoveRedundantQualifierName") // bug
private fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray =
(androidId + macAddress).md5()
/* /*
fun DeviceInfo.toOidb0x769DeviceInfo() : Oidb0x769.DeviceInfo = Oidb0x769.DeviceInfo( fun DeviceInfo.toOidb0x769DeviceInfo() : Oidb0x769.DeviceInfo = Oidb0x769.DeviceInfo(
brand = brand.encodeToString(), brand = brand.encodeToString(),

View File

@ -142,6 +142,10 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
} }
} }
override suspend fun refreshKeys(bot: Bot) {
bot.asQQAndroidBot().network.refreshKeys()
}
override suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean) { override suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean) {
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
check(event.responded.compareAndSet(false, true)) { check(event.responded.compareAndSet(false, true)) {

View File

@ -23,8 +23,11 @@ import net.mamoe.mirai.internal.contact.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.checkIsGroupImpl import net.mamoe.mirai.internal.contact.checkIsGroupImpl
import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.message.* import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
import net.mamoe.mirai.internal.network.protocol.packet.chat.* import net.mamoe.mirai.internal.network.protocol.packet.chat.*
import net.mamoe.mirai.internal.network.useNextServers import net.mamoe.mirai.internal.network.useNextServers
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
@ -118,6 +121,14 @@ internal class QQAndroidBot constructor(
} }
suspend inline fun <E : Packet> OutgoingPacketWithRespType<E>.sendAndExpect(
timeoutMillis: Long = 5000,
retry: Int = 2
): E = network.run { sendAndExpect(timeoutMillis, retry) }
suspend inline fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E =
network.run { sendAndExpect(timeoutMillis, retry) }
/** /**
* 获取 获取群公告 所需的 bkn 参数 * 获取 获取群公告 所需的 bkn 参数
* */ * */

View File

@ -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
}

View File

@ -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.Heartbeat
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc 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
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.WtLogin2
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin20 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.WtLogin9
@ -55,6 +56,8 @@ import java.util.concurrent.ConcurrentLinkedQueue
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.time.minutes
import kotlin.time.seconds
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bot: QQAndroidBot) : BotNetworkHandler() { internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bot: QQAndroidBot) : BotNetworkHandler() {
@ -252,8 +255,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
is WtLogin.Login.LoginPacketResponse.DeviceLockLogin -> { is WtLogin.Login.LoginPacketResponse.DeviceLockLogin -> {
response = WtLogin20( response = WtLogin20(
bot.client, bot.client
response.t402
).sendAndExpect() ).sendAndExpect()
continue@mainloop continue@mainloop
} }
@ -279,6 +281,25 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
bot.otherClientsLock.withLock { bot.otherClientsLock.withLock {
updateOtherClientsList() 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() { private suspend fun registerClientOnline() {
@ -778,7 +799,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
return retryCatchingExceptions( return retryCatchingExceptions(
retry + 1, 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 -> withPacketListener(commandName, sequenceId) { listener ->
return withTimeout(timeoutMillis) { // may throw CancellationException return withTimeout(timeoutMillis) { // may throw CancellationException

View File

@ -15,8 +15,10 @@ import kotlinx.atomicfu.AtomicBoolean
import kotlinx.atomicfu.AtomicInt import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.String import kotlinx.io.core.String
import kotlinx.io.core.toByteArray import kotlinx.io.core.toByteArray
import kotlinx.io.core.writeFully
import net.mamoe.mirai.data.OnlineStatus import net.mamoe.mirai.data.OnlineStatus
import net.mamoe.mirai.internal.BotAccount import net.mamoe.mirai.internal.BotAccount
import net.mamoe.mirai.internal.QQAndroidBot 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.data.jce.FileStoragePushFSSvcList
import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY 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.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.MiraiProtocolInternal
import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.NetworkType
import net.mamoe.mirai.internal.utils.crypto.ECDH import net.mamoe.mirai.internal.utils.crypto.ECDH
@ -31,14 +34,8 @@ import net.mamoe.mirai.utils.*
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import kotlin.random.Random import kotlin.random.Random
internal val DeviceInfo.guid: ByteArray get() = generateGuid(androidId, macAddress)
/** internal val DEFAULT_GUID = "%4;7t>;28<fc.5*6".toByteArray()
* Defaults "%4;7t>;28<fc.5*6".toByteArray()
*/
@Suppress("RemoveRedundantQualifierName") // bug
private fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray =
(androidId + macAddress).md5()
/** /**
* 生成长度为 [length], 元素为随机 `0..255` [ByteArray] * 生成长度为 [length], 元素为随机 `0..255` [ByteArray]
@ -89,8 +86,9 @@ internal open class QQAndroidClient(
val bot: QQAndroidBot by bot.unsafeWeakRef() val bot: QQAndroidBot by bot.unsafeWeakRef()
internal var tgtgtKey: ByteArray = generateTgtgtKey(device.guid) internal var tgtgtKey: ByteArray = (account.passwordMd5 + ByteArray(4) + uin.toInt().toByteArray()).md5()
internal var randomKey: ByteArray = getRandomByteArray(16) internal val randomKey: ByteArray = getRandomByteArray(16)
internal val miscBitMap: Int = protocol.miscBitMap // 184024956 // 也可能是 150470524 ? internal val miscBitMap: Int = protocol.miscBitMap // 184024956 // 也可能是 150470524 ?
internal val mainSigMap: Int = protocol.mainSigMap internal val mainSigMap: Int = protocol.mainSigMap
@ -142,7 +140,7 @@ internal open class QQAndroidClient(
internal fun nextHighwayDataTransSequenceIdForApplyUp(): Int = highwayDataTransSequenceIdForApplyUp.getAndAdd(2) internal fun nextHighwayDataTransSequenceIdForApplyUp(): Int = highwayDataTransSequenceIdForApplyUp.getAndAdd(2)
val appClientVersion: Int = 0 val appClientVersion: Int = 0
val ssoVersion: Int = 13 val ssoVersion: Int = 15
var networkType: NetworkType = NetworkType.WIFI var networkType: NetworkType = NetworkType.WIFI
@ -260,21 +258,27 @@ internal open class QQAndroidClient(
/** /**
* t537 * t537
*/ */
var loginExtraData: LoginExtraData? = null var loginExtraData: MutableSet<LoginExtraData> = CopyOnWriteArraySet()
lateinit var wFastLoginInfo: WFastLoginInfo lateinit var wFastLoginInfo: WFastLoginInfo
var reserveUinInfo: ReserveUinInfo? = null var reserveUinInfo: ReserveUinInfo? = null
lateinit var wLoginSigInfo: WLoginSigInfo lateinit var wLoginSigInfo: WLoginSigInfo
val wLoginSigInfoInitialized get() = ::wLoginSigInfo.isInitialized
/** var G: ByteArray = device.guid // sigInfo[2]
* from tlvMap119 var dpwd: ByteArray = get_mpasswd().toByteArray()
*/ var randSeed: ByteArray = EMPTY_BYTE_ARRAY // t403
var tlv16a: ByteArray? = null
var tlv113: ByteArray? = null
var t402: ByteArray? = null
lateinit var qrPushSig: ByteArray lateinit var qrPushSig: ByteArray
lateinit var mainDisplayName: ByteArray lateinit var mainDisplayName: ByteArray
var transportSequenceId = 1 var transportSequenceId = 1
var lastT106Full: ByteArray? = null
lateinit var t104: ByteArray lateinit var t104: ByteArray
/** /**
@ -284,6 +288,16 @@ internal open class QQAndroidClient(
val bdhSession: CompletableDeferred<BdhSession> = CompletableDeferred() val bdhSession: CompletableDeferred<BdhSession> = CompletableDeferred()
} }
internal fun BytePacketBuilder.writeLoginExtraData(loginExtraData: LoginExtraData) {
loginExtraData.run {
writeLong(uin)
writeByte(ip.size.toByte())
writeFully(ip)
writeInt(time)
writeInt(version)
}
}
internal class BdhSession( internal class BdhSession(
val sigSession: ByteArray, val sigSession: ByteArray,
val sessionKey: ByteArray, val sessionKey: ByteArray,

View File

@ -45,17 +45,13 @@ internal class WFastLoginInfo(
internal class WLoginSimpleInfo( internal class WLoginSimpleInfo(
val uin: Long, // uin 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 imgType: ByteArray,
val imgFormat: ByteArray, val imgFormat: ByteArray,
val imgUrl: ByteArray, val imgUrl: ByteArray,
val mainDisplayName: ByteArray val mainDisplayName: ByteArray
) { ) {
override fun toString(): String { 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] * WARNING, please check [QQAndroidClient.tlv16a]
*/ */
val noPicSig: ByteArray?, // sigInfo[1] val noPicSig: ByteArray?, // sigInfo[1]
val G: ByteArray, // sigInfo[2]
val dpwd: ByteArray,
val randSeed: ByteArray,
val simpleInfo: WLoginSimpleInfo, val simpleInfo: WLoginSimpleInfo,
val appPri: Long, var appPri: Long,
val a2ExpiryTime: Long, var a2ExpiryTime: Long,
val loginBitmap: Long, var loginBitmap: Long,
val tgt: ByteArray, var tgt: ByteArray,
val a2CreationTime: Long, var a2CreationTime: Long,
val tgtKey: ByteArray, var tgtKey: ByteArray,
val userStSig: UserStSig, var userStSig: UserStSig,
/** /**
* TransEmpPacket 加密使用 * TransEmpPacket 加密使用
*/ */
val userStKey: ByteArray, var userStKey: ByteArray,
val userStWebSig: UserStWebSig, var userStWebSig: UserStWebSig,
val userA5: UserA5, var userA5: UserA5,
val userA8: UserA8, var userA8: UserA8,
val lsKey: LSKey, var lsKey: LSKey,
val sKey: SKey, var sKey: SKey,
val userSig64: UserSig64, var userSig64: UserSig64,
val openId: ByteArray, var openId: ByteArray,
val openKey: OpenKey, var openKey: OpenKey,
val vKey: VKey, var vKey: VKey,
val accessToken: AccessToken, var accessToken: AccessToken,
val d2: D2, var d2: D2,
val d2Key: ByteArray, var d2Key: ByteArray,
val sid: Sid, var sid: Sid,
val aqSig: AqSig, var aqSig: AqSig,
val psKeyMap: PSKeyMap, var psKeyMap: PSKeyMap,
val pt4TokenMap: Pt4TokenMap, var pt4TokenMap: Pt4TokenMap,
val superKey: ByteArray, var superKey: ByteArray,
val payToken: ByteArray, var payToken: ByteArray,
val pf: ByteArray, var pf: ByteArray,
val pfKey: ByteArray, var pfKey: ByteArray,
val da2: ByteArray, var da2: ByteArray,
// val pt4Token: ByteArray, // val pt4Token: ByteArray,
val wtSessionTicket: WtSessionTicket, var wtSessionTicket: WtSessionTicket,
val wtSessionTicketKey: ByteArray, var wtSessionTicketKey: ByteArray,
val deviceToken: ByteArray var deviceToken: ByteArray
) { ) {
override fun toString(): String { 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()})"
} }
} }

View File

@ -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 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 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. * 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) : internal class EncryptMethodSessionKeyLoginState2(override val sessionKey: ByteArray) :
EncryptMethodSessionKey { EncryptMethodSessionKey {
override val currentLoginState: Int get() = 2 override val currentLoginState: Int get() = 2

View File

@ -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) * 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 <R : Packet?> OutgoingPacketFactory<R>.buildLoginOutgoingPacket(
client: QQAndroidClient, client: QQAndroidClient,
bodyType: Byte, bodyType: Byte,
extraData: ByteArray = EMPTY_BYTE_ARRAY, extraData: ByteArray = EMPTY_BYTE_ARRAY,
@ -143,10 +143,10 @@ internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
commandName: String = this.commandName, commandName: String = this.commandName,
key: ByteArray = KEY_16_ZEROS, key: ByteArray = KEY_16_ZEROS,
body: BytePacketBuilder.(sequenceId: Int) -> Unit body: BytePacketBuilder.(sequenceId: Int) -> Unit
): OutgoingPacket { ): OutgoingPacketWithRespType<R> {
val sequenceId: Int = client.nextSsoSequenceId() val sequenceId: Int = client.nextSsoSequenceId()
return OutgoingPacket(name, commandName, sequenceId, buildPacket { return OutgoingPacketWithRespType(name, commandName, sequenceId, buildPacket {
writeIntLVPacket(lengthOffset = { it + 4 }) { writeIntLVPacket(lengthOffset = { it + 4 }) {
writeInt(0x00_00_00_0A) writeInt(0x00_00_00_0A)
writeByte(bodyType) writeByte(bodyType)

View File

@ -394,7 +394,8 @@ internal object KnownPacketFactories {
this.discardExact(1) this.discardExact(1)
val packet = when (encryptionMethod) { val packet = when (encryptionMethod) {
4 -> { 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 = val peerShareKey =
bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey()) bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey())
@ -402,19 +403,26 @@ internal object KnownPacketFactories {
packetFactory.decode(bot, data) 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 -> { 0 -> {
val data = if (bot.client.loginState == 0) { val data = if (bot.client.loginState == 0) {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") val size = (this.remaining - 1).toInt()
ByteArrayPool.useInstance(this.remaining.toInt()) { byteArrayBuffer -> val byteArrayBuffer = this.readBytes(size)
val size = (this.remaining - 1).toInt()
this.readFully(byteArrayBuffer, 0, size)
runCatching { runCatching {
TEA.decrypt(byteArrayBuffer, bot.client.ecdh.keyPair.initialShareKey, size) TEA.decrypt(byteArrayBuffer, bot.client.ecdh.keyPair.initialShareKey, size)
}.getOrElse { }.getOrElse {
TEA.decrypt(byteArrayBuffer, bot.client.randomKey, size) TEA.decrypt(byteArrayBuffer, bot.client.randomKey, size)
}.toReadPacket() }.toReadPacket()
}
} else { } else {
TEA.decrypt(this, bot.client.randomKey, 0, (this.remaining - 1).toInt()) TEA.decrypt(this, bot.client.randomKey, 0, (this.remaining - 1).toInt())
} }

View File

@ -11,24 +11,39 @@
package net.mamoe.mirai.internal.network.protocol.packet package net.mamoe.mirai.internal.network.protocol.packet
import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.*
import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.internal.network.LoginExtraData
import kotlinx.io.core.toByteArray
import kotlinx.io.core.writeFully
import net.mamoe.mirai.internal.network.QQAndroidClient 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.protocol.LoginType
import net.mamoe.mirai.internal.network.writeLoginExtraData
import net.mamoe.mirai.internal.utils.GuidSource import net.mamoe.mirai.internal.utils.GuidSource
import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.NetworkType
import net.mamoe.mirai.internal.utils.guidFlag import net.mamoe.mirai.internal.utils.guidFlag
import net.mamoe.mirai.internal.utils.io.* import net.mamoe.mirai.internal.utils.io.*
import net.mamoe.mirai.utils.currentTimeMillis import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.generateDeviceInfoData
import net.mamoe.mirai.utils.md5
import net.mamoe.mirai.utils.toByteArray
import kotlin.random.Random 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 * 显式表示一个 [ByteArray] 是一个 tlv body
*/ */
@ -41,7 +56,7 @@ internal fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
writeShort(1) // _ip_ver writeShort(1) // _ip_ver
writeInt(Random.nextInt()) writeInt(Random.nextInt())
writeInt(uin.toInt()) writeInt(uin.toInt())
writeInt(currentTimeMillis().toInt()) writeInt(currentTimeSeconds().toInt())
writeFully(ip) writeFully(ip)
writeShort(0) writeShort(0)
} shouldEqualsTo 20 } shouldEqualsTo 20
@ -76,7 +91,7 @@ internal fun BytePacketBuilder.t18(
writeShort(0x18) writeShort(0x18)
writeShortLVPacket { writeShortLVPacket {
writeShort(1) //_ping_version writeShort(1) //_ping_version
writeInt(1536) //_sso_version writeInt(0x00_00_06_00) //_sso_version=1536
writeInt(appId.toInt()) writeInt(appId.toInt())
writeInt(appClientVersion) writeInt(appClientVersion)
writeInt(uin.toInt()) writeInt(uin.toInt())
@ -106,6 +121,9 @@ internal fun BytePacketBuilder.t106(
) )
} }
/**
* A1
*/
internal fun BytePacketBuilder.t106( internal fun BytePacketBuilder.t106(
appId: Long = 16L, appId: Long = 16L,
subAppId: Long, subAppId: Long,
@ -143,7 +161,7 @@ internal fun BytePacketBuilder.t106(
writeLong(uin) writeLong(uin)
} }
writeInt(currentTimeMillis().toInt()) writeInt(currentTimeSeconds().toInt())
writeFully(ByteArray(4)) // ip // no need to write actual ip writeFully(ByteArray(4)) // ip // no need to write actual ip
writeByte(isSavePassword.toByte()) writeByte(isSavePassword.toByte())
writeFully(passwordMd5) writeFully(passwordMd5)
@ -607,6 +625,11 @@ internal fun BytePacketBuilder.t185() {
} }
internal fun BytePacketBuilder.t400( internal fun BytePacketBuilder.t400(
/**
* if (var1[2] != null && var1[2].length > 0) {
this._G = (byte[])var1[2].clone();
}
*/
g: ByteArray, // 用于加密这个 tlv g: ByteArray, // 用于加密这个 tlv
uin: Long, uin: Long,
guid: ByteArray, guid: ByteArray,
@ -617,15 +640,14 @@ internal fun BytePacketBuilder.t400(
) { ) {
writeShort(0x400) writeShort(0x400)
writeShortLVPacket { writeShortLVPacket {
writeByte(1) // version
writeLong(uin)
encryptAndWrite(g) { encryptAndWrite(g) {
writeByte(1) // version
writeLong(uin)
writeFully(guid) writeFully(guid)
writeFully(dpwd) writeFully(dpwd)
writeInt(appId.toInt()) writeInt(appId.toInt())
writeInt(subAppId.toInt()) writeInt(subAppId.toInt())
writeLong(currentTimeMillis()) writeInt(currentTimeSeconds().toInt())
writeFully(randomSeed) writeFully(randomSeed)
} }
} }
@ -757,8 +779,38 @@ internal fun BytePacketBuilder.t536( // 1334
} }
} }
internal fun BytePacketBuilder.t536( // 1334
loginExtraData: Collection<LoginExtraData>
) {
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( internal fun BytePacketBuilder.t525(
t536: ByteReadPacket loginExtraData: Collection<LoginExtraData>,
) {
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) writeShort(0x525)
writeShortLVPacket { 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( internal fun BytePacketBuilder.t318(
tgtQR: ByteArray // unknown tgtQR: ByteArray // unknown
) { ) {

View File

@ -27,7 +27,6 @@ import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.getRandomByteArray 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.jce.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769 import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769
import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline

File diff suppressed because one or more lines are too long

View File

@ -10,9 +10,12 @@
package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin
import net.mamoe.mirai.internal.network.QQAndroidClient 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.*
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin 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 { internal object WtLogin15 : WtLoginExt {
private const val subCommand = 15.toShort() private const val subCommand = 15.toShort()
@ -21,34 +24,134 @@ internal object WtLogin15 : WtLoginExt {
operator fun invoke( operator fun invoke(
client: QQAndroidClient, client: QQAndroidClient,
) = WtLogin.ExchangeEmp.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ) = WtLogin.ExchangeEmp.buildOutgoingUniPacket(client, bodyType = 2, key = ByteArray(16)) { sequenceId ->
writeSsoPacket(client, client.subAppId, WtLogin.ExchangeEmp.commandName, sequenceId = sequenceId) { // writeSsoPacket(client, client.subAppId, WtLogin.ExchangeEmp.commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeOicqRequestPacket(
writeShort(subCommand) // subCommand client,
writeShort(21) 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) // 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 " +
t106(appId, client) // //"01 06 " +
t116(client.miscBitMap, client.subSigMap) // //client.wLoginSigInfo.encryptA1!!.size.toShort().toUHexString() + " " + client.wLoginSigInfo.encryptA1!!.toUHexString() + " "+
t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap) // "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 " +
t107(0) // "01 16 00 0E 00 0A F7 FF 7C 00 01 " +
t142(client.apkId) // "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 " +
t144(client) // "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 " +
t145(client.device.guid) // "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" +
t16a(client.tlv16a ?: byteArrayOf()) // new // " 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 " +
t154(sequenceId) // "").hexToBytes())
t141(client.device.simInfo, client.networkType, client.device.apn) // return@writeOicqRequestPacket
t8(2052)
t511() t18(appId, uin = client.uin)
t147(appId, client.apkVersionName, client.apkSignatureMd5) t1(client.uin, ByteArray(4))
t177(buildTime = client.buildTime, buildVersion = client.sdkVersion)
t187(client.device.macAddress) // t106(client = client)
t188(client.device.androidId) writeShort(0x106)
t194(client.device.imsiMd5) val encryptA1 = client.wLoginSigInfo.encryptA1!!
t202(client.device.wifiBSSID, client.device.wifiSSID) writeShortLVByteArray(encryptA1)
t516() // 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
} }
} }
} }

View File

@ -9,18 +9,14 @@
package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin 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.QQAndroidClient
import net.mamoe.mirai.internal.network.guid
import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.network.protocol.packet.*
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
import net.mamoe.mirai.utils.md5
internal object WtLogin20 : WtLoginExt { internal object WtLogin20 : WtLoginExt {
operator fun invoke( operator fun invoke(
client: QQAndroidClient, client: QQAndroidClient
t402: ByteArray ) = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(20) // subCommand writeShort(20) // subCommand
@ -28,7 +24,7 @@ internal object WtLogin20 : WtLoginExt {
t8(2052) t8(2052)
t104(client.t104) t104(client.t104)
t116(client.miscBitMap, client.subSigMap) t116(client.miscBitMap, client.subSigMap)
t401((client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5()) t401(client.G) // (client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5()
} }
} }
} }

View File

@ -9,11 +9,8 @@
package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin 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 kotlinx.io.core.toByteArray
import net.mamoe.mirai.internal.network.QQAndroidClient 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.*
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
@ -32,6 +29,7 @@ internal object WtLogin9 : WtLoginExt {
t18(appId, client.appClientVersion, client.uin) t18(appId, client.appClientVersion, client.uin)
t1(client.uin, client.device.ipAddress) t1(client.uin, client.device.ipAddress)
t106(appId, client) t106(appId, client)
/* // from GetStWithPasswd /* // from GetStWithPasswd
@ -98,13 +96,7 @@ internal object WtLogin9 : WtLoginExt {
t516() t516()
t521() t521()
t525(buildPacket { t525()
t536(buildPacket {
//com.tencent.loginsecsdk.ProtocolDet#packExtraData
writeByte(1) // const
writeByte(0) // data count
}.readBytes())
})
// this.build().debugPrint("傻逼") // this.build().debugPrint("傻逼")
// ignored t318 because not logging in by QR // ignored t318 because not logging in by QR

View File

@ -12,11 +12,12 @@ package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.internal.network.LoginExtraData import net.mamoe.mirai.internal.network.LoginExtraData
import net.mamoe.mirai.internal.network.QQAndroidClient 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.Tlv
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin 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.protocol.packet.t145
import net.mamoe.mirai.internal.network.readUShortLVByteArray 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.internal.utils.io.writeShortLVByteArray
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
@ -97,15 +98,22 @@ internal interface WtLoginExt { // so as not to register to global extension
/** /**
* login extra data * login extra data
*
* oicq/wlogin_sdk/request/oicq_request.java:1445
*/ */
fun QQAndroidClient.analysisTlv537(t537: ByteArray) = t537.read { fun QQAndroidClient.analysisTlv537(t537: ByteArray) = t537.read {
//discardExact(2) //discardExact(2)
loginExtraData = LoginExtraData( // args are to correct order discardExact(1)
uin = readUInt().toLong(), repeat(readByte().toInt()) {
ip = readBytes(readByte().toInt() and 0xff), loginExtraData.add(
time = readInt(), // correct LoginExtraData( // args are to correct order
version = readInt() 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()) fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray())
} }
internal inline fun <R> 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)
}

View File

@ -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 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 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. * 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 { init {
protocols[MiraiProtocol.ANDROID_PHONE] = MiraiProtocolInternal( protocols[MiraiProtocol.ANDROID_PHONE] = MiraiProtocolInternal(
"com.tencent.mobileqq", "com.tencent.mobileqq",
537066419, 537066978,
"8.4.18", "8.5.5",
"6.0.0.2454", "6.0.0.2454",
184024956, 184024956,
0x10400, 0x10400,