mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-07 00:20:09 +08:00
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:
parent
cc87f6833f
commit
7a6e930fc7
@ -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
|
||||
|
@ -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]).
|
||||
*
|
||||
|
@ -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<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(
|
||||
brand = brand.encodeToString(),
|
||||
|
@ -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) {
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
check(event.responded.compareAndSet(false, true)) {
|
||||
|
@ -23,8 +23,11 @@ import net.mamoe.mirai.internal.contact.StrangerInfoImpl
|
||||
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
|
||||
import net.mamoe.mirai.internal.contact.uin
|
||||
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.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.useNextServers
|
||||
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 参数
|
||||
* */
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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<fc.5*6".toByteArray()
|
||||
*/
|
||||
@Suppress("RemoveRedundantQualifierName") // bug
|
||||
private fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray =
|
||||
(androidId + macAddress).md5()
|
||||
internal val DEFAULT_GUID = "%4;7t>;28<fc.5*6".toByteArray()
|
||||
|
||||
/**
|
||||
* 生成长度为 [length], 元素为随机 `0..255` 的 [ByteArray]
|
||||
@ -89,8 +86,9 @@ internal open class QQAndroidClient(
|
||||
|
||||
val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
|
||||
internal var tgtgtKey: ByteArray = generateTgtgtKey(device.guid)
|
||||
internal var randomKey: ByteArray = getRandomByteArray(16)
|
||||
internal var tgtgtKey: ByteArray = (account.passwordMd5 + ByteArray(4) + uin.toInt().toByteArray()).md5()
|
||||
internal val randomKey: ByteArray = getRandomByteArray(16)
|
||||
|
||||
|
||||
internal val miscBitMap: Int = protocol.miscBitMap // 184024956 // 也可能是 150470524 ?
|
||||
internal val mainSigMap: Int = protocol.mainSigMap
|
||||
@ -142,7 +140,7 @@ internal open class QQAndroidClient(
|
||||
internal fun nextHighwayDataTransSequenceIdForApplyUp(): Int = highwayDataTransSequenceIdForApplyUp.getAndAdd(2)
|
||||
|
||||
val appClientVersion: Int = 0
|
||||
val ssoVersion: Int = 13
|
||||
val ssoVersion: Int = 15
|
||||
|
||||
var networkType: NetworkType = NetworkType.WIFI
|
||||
|
||||
@ -260,21 +258,27 @@ internal open class QQAndroidClient(
|
||||
/**
|
||||
* t537
|
||||
*/
|
||||
var loginExtraData: LoginExtraData? = null
|
||||
var loginExtraData: MutableSet<LoginExtraData> = 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<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(
|
||||
val sigSession: ByteArray,
|
||||
val sessionKey: ByteArray,
|
||||
|
@ -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()})"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 <R : Packet?> OutgoingPacketFactory<R>.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<R> {
|
||||
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)
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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<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(
|
||||
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)
|
||||
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
|
||||
) {
|
||||
|
@ -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
|
||||
|
File diff suppressed because one or more lines are too long
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 <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)
|
||||
}
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user