This commit is contained in:
Him188 2020-01-29 15:46:05 +08:00
parent e2f59416f0
commit 2f2a8666b9
10 changed files with 128 additions and 54 deletions

View File

@ -177,7 +177,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
/**
* 处理从服务器接收过来的包. 这些包可能是粘在一起的, 也可能是不完整的. 将会自动处理
* 处理从服务器接收过来的包. 这些包可能是粘在一起的, 也可能是不完整的. 将会自动处理.
* 处理后的包会调用 [parsePacketAsync]
*/
@UseExperimental(ExperimentalCoroutinesApi::class)
internal fun processPacket(rawInput: ByteReadPacket): Unit = rawInput.debugPrint("Received").let { input: ByteReadPacket ->

View File

@ -53,7 +53,7 @@ internal open class QQAndroidClient(
"tgtgtKey" to tgtgtKey,
"tgtKey" to wLoginSigInfo.tgtKey,
"deviceToken" to wLoginSigInfo.deviceToken,
"shareKeyCalculatedByConstPubKey" to ecdh.keyPair.shareKey
"shareKeyCalculatedByConstPubKey" to ecdh.keyPair.initialShareKey
//"t108" to wLoginSigInfo.t1,
//"t10c" to t10c,
//"t163" to t163

View File

@ -95,6 +95,6 @@ internal interface EncryptMethodECDH : EncryptMethod {
})
// encryptAndWrite("26 33 BA EC 86 EB 79 E6 BC E0 20 06 5E A9 56 6C".hexToBytes(), body)
encryptAndWrite(ecdh.keyPair.shareKey, body)
encryptAndWrite(ecdh.keyPair.initialShareKey, body)
}
}

View File

@ -111,7 +111,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
// 解析外层包装
when (flag1) {
0x0A -> parseSsoFrame(bot, decryptedData)
0x0B -> parseUniFrame(bot, decryptedData)
0x0B -> parseSsoFrame(bot, decryptedData) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
else -> error("unknown flag1: ${flag1.toByte().toUHexString()}")
}
}?.let {
@ -214,7 +214,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
this.discardExact(1) // const = 0
val packet = when (encryptionMethod) {
4 -> { // peer public key, ECDH
var data = this.decryptBy(bot.client.ecdh.keyPair.shareKey, this.readRemaining - 1)
var data = this.decryptBy(bot.client.ecdh.keyPair.initialShareKey, this.readRemaining - 1)
val peerShareKey = bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey())
data = data.decryptBy(peerShareKey)
@ -228,7 +228,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
this.readFully(byteArrayBuffer, 0, size)
runCatching {
byteArrayBuffer.decryptBy(bot.client.ecdh.keyPair.shareKey, size)
byteArrayBuffer.decryptBy(bot.client.ecdh.keyPair.initialShareKey, size)
}.getOrElse {
byteArrayBuffer.decryptBy(bot.client.randomKey, size)
} // 这里实际上应该用 privateKey(另一个random出来的key)

View File

@ -7,9 +7,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import net.mamoe.mirai.utils.cryptor.*
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.io.discardExact
import net.mamoe.mirai.utils.md5
import kotlin.text.toByteArray
// sessionTicket = 55 F7 24 8B 04 4E AA A8 98 E6 77 D2 D6 54 A9 B4 43 91 94 A3 0D DA CF 8F E8 94 E0 F4 A2 6B B4 8B 2B 4F 78 8D 21 EE D4 95 A6 F7 A4 3D B5 87 9B 3D
// sessionTicketKey = B6 9D E4 EC 65 38 64 FD C8 3A D8 33 54 35 0C 73
// randomKey = A4 9A 6A EE 17 5B 7E 3D C0 71 DA 04 1C E1 E4 88
@ -239,13 +239,12 @@ fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed
discardExact(4)
readTLVMap()[0x106]
?.also { DebugLogger.info("找到了 0x106") }
?.decryptBy(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray())
?.decryptBy(md5(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray()))
?.read {
discardExact(2 + 4 * 4 + 8 + 4 + 4 + 1 + 16)
tgtgtKey = readBytes(16)
DebugLogger.info("获取 tgtgtKey=${tgtgtKey.toUHexString()}")
}
DebugLogger.info("tlv map里面没有 0x106")
} ?: DebugLogger.info("找不到 0x106")
} catch (e: Exception) {
e.printStackTrace()
}
@ -269,9 +268,9 @@ fun ByteReadPacket.decodeUni() {
// 00 00 00 5B 10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 01 22 14 DA 6E B1 0B 8C 98 0C A8 0C
println("// 尝试解 Uni")
println("// head")
return
//return
readBytes(readInt() - 4).debugPrint("head").toReadPacket().apply {
val commandName = readString(readInt() - 4).also { println("commandName=$it") }
val commandName = readString(readInt() - 4).also { PacketLogger.warning("commandName=$it") }
println(commandName)
println(" unknown4Bytes=" + readBytes(readInt() - 4).toUHexString())
// 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,7 @@ actual class ECDHKeyPair(
actual val privateKey: ECDHPrivateKey get() = delegate.private
actual val publicKey: ECDHPublicKey get() = delegate.public
actual val shareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
actual val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
}
@Suppress("FunctionName")

View File

@ -8,14 +8,14 @@ import net.mamoe.mirai.utils.coerceAtLeastOrFail
/**
* .
* . QQ Android 中叫做 "Troop"
*
* Group ID Group Number 并不是同一个值.
* - Group Number([Group.id]) 是通常使用的群号码.( QQ 客户端中可见)
* - Group ID([Group.internalId]) 是与调用 API 时使用的 id.( QQ 客户端中不可见)
* @author Him188moe
*/
interface Group : Contact, CoroutineScope/*, Map<UInt, Member>*/ { // TODO: 2019/12/4 在 inline 稳定后实现 Map<UInt, Member>. 目前这样做会导致问题
interface Group : Contact, CoroutineScope/*, Map<UInt, Member>*/ { // TODO: 2020/1/29 实现接口 Map<Long, Memebr>
/**
* 内部 ID. 内部 ID [GroupId] 的映射
*/
@ -86,7 +86,10 @@ fun Long.groupInternalId(): GroupInternalId = GroupInternalId(this)
/**
* 将无符号整数格式的 [Long] 转为 [GroupId].
*
* : Java 中常用 [Long] 来表示 [UInt]
* : Java 中常用 [Long] 来表示 [UInt].
*
* : Kotlin/Java, 有符号的数据类型的二进制最高位为符号标志.
* 如一个 byte, `1000 0000` 最高位为 1, 则为负数.
*/
fun Long.groupId(): GroupId = GroupId(this.coerceAtLeastOrFail(0))

View File

@ -15,23 +15,46 @@ expect class ECDHKeyPair {
val privateKey: ECDHPrivateKey
val publicKey: ECDHPublicKey
val shareKey: ByteArray
/**
* 私匙和固定公匙([initialPublicKey]) 计算得到的 shareKey
*/
val initialShareKey: ByteArray
}
/**
* 椭圆曲线密码, ECDH 加密
*/
expect class ECDH(keyPair: ECDHKeyPair) {
val keyPair: ECDHKeyPair
/**
* [keyPair] 的私匙和 [peerPublicKey] 计算 shareKey
*/
fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray
companion object {
/**
* 由完整的 publicKey ByteArray 得到 [ECDHPublicKey]
*/
fun constructPublicKey(key: ByteArray): ECDHPublicKey
/**
* 生成随机密匙对
*/
fun generateKeyPair(): ECDHKeyPair
/**
* 由一对密匙计算 shareKey
*/
fun calculateShareKey(privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey): ByteArray
}
override fun toString(): String
}
/**
*
*/
@Suppress("FunctionName")
expect fun ECDH(): ECDH

View File

@ -17,7 +17,7 @@ actual class ECDHKeyPair(
actual val privateKey: ECDHPrivateKey get() = delegate.private
actual val publicKey: ECDHPublicKey get() = delegate.public
actual val shareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
actual val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
}
@Suppress("FunctionName")