Daily QQA update: TLVMap enhancement, A1 parsing, OutgoingPacket, Tickets, D2 key for decrypting

This commit is contained in:
Him188 2020-01-16 22:05:44 +08:00
parent 4fb2fd4ba1
commit a75843b678
5 changed files with 112 additions and 18 deletions

View File

@ -181,8 +181,8 @@ class LoginExtraData(
class WLoginSigInfo(
val uin: Long,
val encryptA1: ByteArray, // sigInfo[0]
val noPicSig: ByteArray, // sigInfo[1]
val encryptA1: ByteArray?, // sigInfo[0]
val noPicSig: ByteArray?, // sigInfo[1]
val G: ByteArray, // sigInfo[2]
val dpwd: ByteArray,
val randSeed: ByteArray,

View File

@ -0,0 +1,30 @@
package net.mamoe.mirai.qqandroid.network
class Ticket(
val id: Int,
val data: ByteArray,
val key: ByteArray?,
val creationTime: Long,
val expireTime: Long
) {
companion object {
const val USER_A5 = 0x2
const val AQ_SIG = 0x200000
const val USER_SIG_64 = 0x2000
const val SUPER_KEY = 0x100000
const val OPEN_KEY = 0x4000
const val ACCESS_TOKEN = 0x8000
const val USER_ST_SIG = 0x80
const val USER_A8 = 0x10
const val LS_KEY = 0x200
const val S_KEY = 0x1000
const val V_KEY = 0x20000
const val TGT = 0x40
const val D2 = 0x40000
const val SID = 0x80000
const val USER_ST_WEB_SIG = 0x20
const val PAY_TOKEN = 0x800000
const val PF = 0x1000000
const val DA2 = 0x2000000
}
}

View File

@ -10,7 +10,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.DecrypterByteArray
import net.mamoe.mirai.utils.cryptor.encryptAndWrite
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeIntLVPacket
import net.mamoe.mirai.utils.io.writeQQ
/**
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket].
@ -31,6 +34,45 @@ internal class OutgoingPacket constructor(
private val KEY_16_ZEROS = ByteArray(16)
private val EMPTY_BYTE_ARRAY = ByteArray(0)
/**
* 最外层的包. 结构适用于登录之后的过程.
*
* QQ 中这个被以 JNI 实现:
* 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)
*
* **Packet structure**
* int remaining.length + 4
* int 0x0B
* byte 0x01
* int sequenceId
* byte 0
* int uinAccount.length + 4
* byte[] uinAccount
*
* byte[] body encrypted by 16 zero
*/
/*
* 00 00 02 34 // remaining.length + 4
* 00 00 00 0B
* 01
* 00 01 4E 73 // sequence
* 00
* 00 00 00 0E
* 31 39 39 34 37 30 31 30 32 31
* 18 5D 8F 17 7D 67 71 61 FE DB 30 A4 4D 16 DD 0E 8D 84 0A F2 44 BE FB BB 11 BB B4 AC 79 50 50 9F 4C 99 CC 77 0B AA B6 E0 06 0C F7 91 79 99 57 31 3D EF 38 92 2C C8 81 33 79 83 FF C6 2F BA 18 2A 33 F8 D9 4E CD 62 07 D8 08 B7 1A 1E C7 EB AC AB B4 1E C9 9D A9 15 9C 29 29 2A 99 F6 BB D0 43 65 D6 5E 9C 93 A8 8D 17 08 5B 6A 29 92 58 6A 75 C9 B5 45 B3 0E A5 D3 52 8F 9A A4 88 36 A0 14 3A 21 F2 46 C3 91 66 A3 73 67 6A 3E F7 9D 8E 44 52 87 7B 8A C7 1B E2 D3 98 62 E8 25 30 2A 43 5C 5A B2 C6 45 F5 39 EC 85 81 BF 7D 22 4C E8 01 87 92 48 38 06 6B A0 83 70 0B 51 ED CF 7A FF E2 F2 06 3E A7 95 4E E5 29 23 32 1C FE 79 C6 08 C5 7A 39 B9 AF CD 4F 80 3E 5D 74 4D 0B E1 10 33 8D F0 54 8E 0E 22 96 B4 06 7F 29 01 1E CA 30 35 FD 8A 2E 51 04 20 79 7B 08 DC DF F6 64 21 6B C5 95 34 B3 40 D2 E8 CE BB DC 69 89 75 62 A6 0B 4A 49 9D 90 BA 68 2B BD 8A 50 2D 68 6B 56 40 0C 39 F2 08 20 1B EB A4 A5 20 1D 1F 7E FA 4B B8 2E 58 79 2A 16 54 26 6C C8 44 6C 4F 64 2D 5C 0C 47 2E 90 13 A9 D7 33 4A 51 17 6E 3F 3E 48 AE 39 D8 45 05 2C 0C 3C 9F 92 39 DB 62 B3 BB 64 EE 7E 91 C5 84 92 10 96 D9 F1 13 02 94 00 EA DA 87 7C 85 7B 68 BA 8D A1 AB F5 CD 9C EB 4C CD A0 38 78 43 80 DD E5 1D 28 25 1F F0 25 EF 0D 95 91 0F 21 5D 41 06 00 03 48 77 E0 98 09 3E 04 5A B0 93 63 3B AE 8E 49 0C C2 12 BA DD C3 5A ED FF 68 98 22 C4 5E F6 1E 85 57 15 E8 7E 26 22 E3 70 C2 57 F4 CE 2F CB C4 DC 39 4A 9C FE DE 27 18 D3 36 66 88 92 D7 69 D0 04 8E 93 9B AD E9 2E 5A 2C 91 CD 28 DF BE 62 CF 2C 72 8E FD A9 1F 0E 8E 00 9E 54 28 50 25 0C E7 DC 98 85 C9 B3 59 A8 97 F5 2E 7F 44 4C 43 3C C4 65 E5 AB DB 5B 3C 50 2D 53 B3 EA 74 3C 39 F4 0A 52 31 34 30 F5 E6 82 CD 36 D9
*/
internal inline fun PacketFactory<*, *>.buildOutgingPacket(
client: QQAndroidClient,
subAppId: Long,
extraData: ByteArray = EMPTY_BYTE_ARRAY,
name: String? = null,
id: PacketId = this.id,
ssoExtraData: ByteReadPacket = BRP_STUB,
body: BytePacketBuilder.(sequenceId: Int) -> Unit
){
}
/**
* 最外层的包. 结构适用于登录.
*
@ -158,6 +200,7 @@ private inline fun BytePacketBuilder.writeLoginSsoPacket(
writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
}
/**
* Outermost packet, not for login
*

View File

@ -10,7 +10,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.utils.GuidSource
import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.qqandroid.utils.guidFlag
import net.mamoe.mirai.qqandroid.utils.inline
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.DecrypterByteArray
import net.mamoe.mirai.utils.cryptor.DecrypterType
@ -321,18 +320,27 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, Log
val outPSKeyMap: PSKeyMap = mutableMapOf()
val outPt4TokenMap: Pt4TokenMap = mutableMapOf()
parsePSKeyMapAndPt4TokenMap(tlvMap119[0x512]?: error("Cannot find tlv 0x512, which is pskeyMap and pt4tokenMap"), creationTime, expireTime, outPSKeyMap, outPt4TokenMap)
parsePSKeyMapAndPt4TokenMap(
tlvMap119[0x512] ?: error("Cannot find tlv 0x512, which is pskeyMap and pt4tokenMap"),
creationTime,
expireTime,
outPSKeyMap,
outPt4TokenMap
)
var a1: ByteArray? = null
var noPicSig: ByteArray? = null
tlvMap119[0x531]?.let {
analysisTlv0x531(it){ arg1, arg2 ->
a1 = arg1
noPicSig = arg2
}
}
client.wLoginSigInfo = WLoginSigInfo(
uin = client.uin,
encryptA1 = inline {
val t10c = tlvMap119[0x10c]
val t106 = tlvMap119[0x106]
if (t106 != null && t10c != null) {
t106 + t10c
} else ByteArray(0)
},
noPicSig = tlvMap119.getOrEmpty(0x16a),
encryptA1 = a1,
noPicSig = noPicSig,
G = byteArrayOf(), // defaults {}, from asyncContext._G
dpwd = byteArrayOf(), // defaults {}, from asyncContext._G
randSeed = tlvMap119.getOrEmpty(0x403), // or from asyncContext._t403.get_body_data()
@ -393,6 +401,19 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, Log
return this[key] ?: byteArrayOf()
}
private inline fun analysisTlv0x531(t531: ByteArray, handler: (a1: ByteArray, noPicSig: ByteArray) -> Unit) {
val map = t531.toReadPacket().readTLVMap()
val t106 = map[0x106]
val t16a = map[0x16a]
val t113 = map[0x113]
val t10c = map[0x10c]
if (t106 != null && t16a != null && t113 != null && t10c != null) {
handler(t106 + t10c, t16a)
}
}
/**
* @throws error
*/

View File

@ -162,15 +162,15 @@ fun Input.readFlatTUVarIntMap(expectingEOF: Boolean = false, tagSize: Int = 1):
return map
}
fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 1) =
fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) =
debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys {
when (keyLength) {
1 -> it.key.toInt().toUByte().toUHexString()
2 -> it.key.toInt().toUShort().toUHexString()
4 -> it.key.toInt().toUInt().toUHexString()
1 -> it.key.toUByte().toUHexString()
2 -> it.key.toUShort().toUHexString()
4 -> it.key.toUInt().toUHexString()
else -> illegalArgument("Expecting 1, 2 or 4 for keyLength")
}
})
}.entries.joinToString(prefix = "{", postfix = "}", separator = "\n"))
internal inline fun unsupported(message: String? = null): Nothing = error(message ?: "Unsupported")