[core] decode SsoEstablishShareKey

This commit is contained in:
StageGuard 2023-06-17 10:29:17 +08:00 committed by Karlatemp
parent ff96dce35d
commit 1dacce61cd
No known key found for this signature in database
GPG Key ID: BA173CA2B9956C59
6 changed files with 106 additions and 36 deletions

View File

@ -18,6 +18,7 @@ import net.mamoe.mirai.internal.network.components.PacketCodecException.Kind.*
import net.mamoe.mirai.internal.network.handler.selector.NetworkException import net.mamoe.mirai.internal.network.handler.selector.NetworkException
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.network.protocol.packet.sso.PREFIX_TRPC_SSO
import net.mamoe.mirai.internal.utils.crypto.Ecdh import net.mamoe.mirai.internal.utils.crypto.Ecdh
import net.mamoe.mirai.internal.utils.crypto.TEA import net.mamoe.mirai.internal.utils.crypto.TEA
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
@ -95,6 +96,17 @@ internal class PacketCodecException(
// } // }
} }
internal enum class IncomingPacketType(val value: Int) {
Login(0x0A),
Simple(0x0B),
Unknown(-1)
;
companion object {
internal fun of(value: Int) = enumValues<IncomingPacketType>().find { it.value == value } ?: Unknown
}
}
internal class PacketCodecImpl : PacketCodec { internal class PacketCodecImpl : PacketCodec {
override fun decodeRaw( override fun decodeRaw(
@ -102,15 +114,15 @@ internal class PacketCodecImpl : PacketCodec {
input: ByteReadPacket input: ByteReadPacket
): RawIncomingPacket = input.run { ): RawIncomingPacket = input.run {
// packet type // packet type
val type = readInt() val packetType = IncomingPacketType.of(readInt())
PacketLogger.verbose { "开始处理一个包" } PacketLogger.verbose { "开始处理一个包" }
val encryptMethod = readByte().toInt() val encryptMethod = PacketEncryptType.of(readByte().toInt())
val flag3 = readByte().toInt() val flag3 = readByte().toInt()
val flag3Exception = if (flag3 != 0) { val flag3Exception = if (flag3 != 0) {
PacketCodecException( PacketCodecException(
"Illegal flag3. Expected 0, whereas got $flag3. packet type=$type, encrypt method=$encryptMethod. ", "Illegal flag3. Expected 0, whereas got $flag3. packet type=$packetType, encrypt method=$encryptMethod. ",
kind = PROTOCOL_UPDATED kind = PROTOCOL_UPDATED
) )
} else null } else null
@ -122,10 +134,8 @@ internal class PacketCodecImpl : PacketCodec {
val raw = try { val raw = try {
when (encryptMethod) { when (encryptMethod) {
// empty key PacketEncryptType.Empty -> TEA.decrypt(buffer, DECRYPTER_16_ZERO, size)
2 -> TEA.decrypt(buffer, DECRYPTER_16_ZERO, size) PacketEncryptType.D2 -> {
// d2 key
1 -> {
TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse { TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse {
throw PacketCodecException( throw PacketCodecException(
"Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal", "Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal",
@ -133,17 +143,18 @@ internal class PacketCodecImpl : PacketCodec {
) )
}, size) }, size)
} }
// no encrypt
0 -> buffer PacketEncryptType.NoEncrypt -> buffer
else -> throw PacketCodecException("Unknown encrypt type=$encryptMethod", PROTOCOL_UPDATED) else -> throw PacketCodecException("Unknown encrypt type=$encryptMethod", PROTOCOL_UPDATED)
}.let { decryptedData -> }.let { decryptedData ->
when (type) { when (packetType) {
// login IncomingPacketType.Login -> parseSsoFrame(client, decryptedData)
0x0A -> parseSsoFrame(client, decryptedData) IncomingPacketType.Simple -> parseSsoFrame(
// simple client,
0x0B -> parseSsoFrame(client, decryptedData) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样. decryptedData
) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
else -> throw PacketCodecException( else -> throw PacketCodecException(
"unknown packet type: ${type.toByte().toUHexString()}", "unknown packet type: ${packetType.value.toUHexString()}",
PROTOCOL_UPDATED PROTOCOL_UPDATED
) )
} }
@ -163,25 +174,39 @@ internal class PacketCodecImpl : PacketCodec {
"which may means protocol is updated.", "which may means protocol is updated.",
flag3Exception flag3Exception
) )
} else if (raw.commandName.startsWith(PREFIX_TRPC_SSO)) {
PacketLogger.verbose { "received a trpc native packet: ${raw.commandName}" }
} else { } else {
throw flag3Exception throw flag3Exception
} }
} }
when (encryptMethod) { when (encryptMethod) {
0, 1 -> RawIncomingPacket(raw.commandName, raw.sequenceId, raw.body.readBytes()) PacketEncryptType.NoEncrypt,
2 -> RawIncomingPacket( PacketEncryptType.D2 -> RawIncomingPacket(
raw.commandName, raw.commandName,
raw.sequenceId, raw.sequenceId,
raw.body.withUse { raw.body.readBytes()
try {
parseOicqResponse(client, raw.commandName)
} catch (e: Throwable) {
throw PacketCodecException(e, PacketCodecException.Kind.OTHER)
}
}
) )
PacketEncryptType.Empty -> {
RawIncomingPacket(
raw.commandName,
raw.sequenceId,
raw.body.withUse {
if (raw.commandName.startsWith(PREFIX_TRPC_SSO)) {
readBytes()
} else {
try {
parseOicqResponse(client, raw.commandName)
} catch (e: Throwable) {
throw PacketCodecException(e, PacketCodecException.Kind.OTHER)
}
}
}
)
}
else -> error("unreachable") else -> error("unreachable")
} }
} }

View File

@ -16,7 +16,7 @@ import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.components.EcdhInitialPublicKeyUpdater import net.mamoe.mirai.internal.network.components.EcdhInitialPublicKeyUpdater
import net.mamoe.mirai.internal.network.protocol.data.proto.SSOReserveField import net.mamoe.mirai.internal.network.protocol.data.proto.SSOReserveField
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin import net.mamoe.mirai.internal.network.protocol.packet.sso.SsoEstablishShareKey
import net.mamoe.mirai.internal.spi.EncryptService import net.mamoe.mirai.internal.spi.EncryptService
import net.mamoe.mirai.internal.spi.EncryptServiceContext import net.mamoe.mirai.internal.spi.EncryptServiceContext
import net.mamoe.mirai.internal.utils.io.encryptAndWrite import net.mamoe.mirai.internal.utils.io.encryptAndWrite
@ -75,25 +75,34 @@ internal class IncomingPacket private constructor(
} }
} }
internal enum class PacketEncryptType { internal enum class PacketEncryptType(val value: Int) {
NoEncrypt { // 0x00 NoEncrypt(0x00) { // 0x00
override fun defaultKey(client: QQAndroidClient): ByteArray = NO_ENCRYPT override fun defaultKey(client: QQAndroidClient): ByteArray = NO_ENCRYPT
}, },
D2 { //0x01 D2(0x01) { //0x01
override fun defaultKey(client: QQAndroidClient): ByteArray { override fun defaultKey(client: QQAndroidClient): ByteArray {
return client.wLoginSigInfo.d2Key return client.wLoginSigInfo.d2Key
} }
}, },
Empty { // 16 zeros,// 0x02 Empty(0x02) { // 16 zeros,// 0x02
override fun defaultKey(client: QQAndroidClient): ByteArray { override fun defaultKey(client: QQAndroidClient): ByteArray {
return KEY_16_ZEROS return KEY_16_ZEROS
} }
}, },
Unknown(-1) {
override fun defaultKey(client: QQAndroidClient): ByteArray {
error("unreachable")
}
}
; ;
inline val codec: Byte get() = ordinal.toByte() inline val codec: Byte get() = ordinal.toByte()
abstract fun defaultKey(client: QQAndroidClient): ByteArray abstract fun defaultKey(client: QQAndroidClient): ByteArray
companion object {
internal fun of(value: Int) = enumValues<PacketEncryptType>().find { it.value == value } ?: Unknown
}
} }
@ -251,9 +260,9 @@ internal fun createChannelProxy(client: QQAndroidClient): EncryptService.Channel
uin: Long, uin: Long,
data: ByteArray data: ByteArray
): EncryptService.ChannelResult? { ): EncryptService.ChannelResult? {
if (commandName == "trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey") { if (commandName == SsoEstablishShareKey.commandName) {
val packet = client.bot.network.sendAndExpect<Packet>( val packet = client.bot.network.sendAndExpect(
WtLogin.Login.buildLoginOutgoingPacket( SsoEstablishShareKey.buildLoginOutgoingPacket(
client = client, client = client,
encryptMethod = PacketEncryptType.Empty, encryptMethod = PacketEncryptType.Empty,
uin = uin.toString(), uin = uin.toString(),
@ -268,7 +277,7 @@ internal fun createChannelProxy(client: QQAndroidClient): EncryptService.Channel
) )
} }
) )
TODO("parse packet to ChannelResult") return EncryptService.ChannelResult(SsoEstablishShareKey.commandName, packet.data)
} }
return null return null
} }

View File

@ -26,11 +26,11 @@ 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.sso.SsoEstablishShareKey
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.ChangeFriendRemark import net.mamoe.mirai.internal.network.protocol.packet.summarycard.ChangeFriendRemark
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard
import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.DeprecatedSinceMirai
import net.mamoe.mirai.utils.MiraiLoggerWithSwitch import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
import kotlin.jvm.JvmName
internal sealed class PacketFactory<TPacket : Packet?> { internal sealed class PacketFactory<TPacket : Packet?> {
/** /**
@ -132,6 +132,7 @@ internal object KnownPacketFactories {
WtLogin.Login, WtLogin.Login,
WtLogin.ExchangeEmp, WtLogin.ExchangeEmp,
WtLogin.TransEmp, WtLogin.TransEmp,
SsoEstablishShareKey,
StatSvc.Register, StatSvc.Register,
StatSvc.GetOnlineStatus, StatSvc.GetOnlineStatus,
StatSvc.SimpleGet, StatSvc.SimpleGet,

View File

@ -0,0 +1,25 @@
/*
* Copyright 2019-2023 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/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.protocol.packet.sso
import io.ktor.utils.io.core.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
internal object SsoEstablishShareKey : OutgoingPacketFactory<SsoEstablishShareKey.RawData>(
"trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey"
) {
internal class RawData(val data: ByteArray) : Packet
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): RawData {
return RawData(readBytes())
}
}

View File

@ -0,0 +1,12 @@
/*
* Copyright 2019-2023 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/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.protocol.packet.sso
internal const val PREFIX_TRPC_SSO = "trpc.o3"

View File

@ -72,8 +72,6 @@ public interface EncryptService : BaseService {
public class ChannelResult( public class ChannelResult(
public val cmd: String, public val cmd: String,
public val data: ByteArray, public val data: ByteArray,
public val success: Int,
public val callbackId: Long
) )
public interface ChannelProxy { public interface ChannelProxy {