mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 20:39:36 +08:00
Congratulations!! QQAndroid logon successful
This commit is contained in:
parent
434e65111e
commit
0e150cdc2f
@ -11,7 +11,6 @@ import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.RegPushReason
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.SvcReqRegisterPacket
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
@ -39,7 +38,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
println()
|
||||
println()
|
||||
println("Sending ReqRegister")
|
||||
SvcReqRegisterPacket(bot.client, RegPushReason.setOnlineStatus).sendAndExpect<SvcReqRegisterPacket.Response>()
|
||||
SvcReqRegisterPacket(bot.client).sendAndExpect<SvcReqRegisterPacket.Response>()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,6 @@ import kotlinx.atomicfu.AtomicInt
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.data.OnlineStatus
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
@ -21,7 +20,10 @@ import net.mamoe.mirai.utils.cryptor.contentToString
|
||||
import net.mamoe.mirai.utils.cryptor.decryptBy
|
||||
import net.mamoe.mirai.utils.cryptor.initialPublicKey
|
||||
import net.mamoe.mirai.utils.getValue
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.read
|
||||
import net.mamoe.mirai.utils.io.readUShortLVByteArray
|
||||
import net.mamoe.mirai.utils.io.readUShortLVString
|
||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||
|
||||
/*
|
||||
@ -64,16 +66,11 @@ internal open class QQAndroidClient(
|
||||
)
|
||||
}
|
||||
|
||||
internal inline fun <R> tryDecryptOrNull(data: ByteReadPacket, mapper: (ByteArray) -> R): R? {
|
||||
ByteArrayPool.useInstance {
|
||||
data.readAvailable(it)
|
||||
|
||||
keys.forEach { (key, value) ->
|
||||
kotlin.runCatching {
|
||||
return mapper(it.decryptBy(value).also { PacketLogger.verbose("成功使用 $key 解密") })
|
||||
}
|
||||
internal inline fun <R> tryDecryptOrNull(data: ByteArray, size: Int = data.size, mapper: (ByteArray) -> R): R? {
|
||||
keys.forEach { (key, value) ->
|
||||
kotlin.runCatching {
|
||||
return mapper(data.decryptBy(value, size).also { PacketLogger.verbose("成功使用 $key 解密") })
|
||||
}
|
||||
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.mamoe.mirai.qqandroid.network.io
|
||||
|
||||
import kotlinx.io.charsets.Charset
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.ObjectPool
|
||||
import net.mamoe.mirai.utils.io.readString
|
||||
import net.mamoe.mirai.utils.io.toIoBuffer
|
||||
import kotlin.experimental.and
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
inline class JceHead(private val value: Long) {
|
||||
@ -12,17 +12,23 @@ inline class JceHead(private val value: Long) {
|
||||
|
||||
val tag: Int get() = (value ushr 32).toInt()
|
||||
val type: Byte get() = value.toUInt().toByte()
|
||||
|
||||
override fun toString(): String {
|
||||
return "JceHead(tag=$tag, type=$type)"
|
||||
}
|
||||
}
|
||||
|
||||
fun ByteArray.asJceInput(): JceInput = JceInput(this.toIoBuffer())
|
||||
fun ByteArray.asJceInput(charset: Charset = CharsetGBK): JceInput = JceInput(this.toIoBuffer(), charset)
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
class JceInput(
|
||||
@PublishedApi
|
||||
internal val input: IoBuffer,
|
||||
private val charset: Charset = CharsetGBK,
|
||||
private val pool: ObjectPool<IoBuffer> = IoBuffer.Pool
|
||||
) : Closeable {
|
||||
|
||||
constructor(input: Input) : this(IoBuffer.Pool.borrow().also { input.readAvailable(it) })
|
||||
|
||||
override fun close() {
|
||||
@ -36,12 +42,12 @@ class JceInput(
|
||||
internal fun peakHead(): JceHead = input.makeView().readHead()
|
||||
|
||||
private fun IoBuffer.readHead(): JceHead {
|
||||
val var2 = readByte()
|
||||
val type = var2 and 15
|
||||
var tag = (var2.toInt() and 240) shr 4
|
||||
if (tag == 15)
|
||||
tag = readByte().toInt() and 255
|
||||
return JceHead(tag = tag, type = type)
|
||||
val var2 = readUByte()
|
||||
val type = var2 and 15u
|
||||
var tag = var2.toUInt() shr 4
|
||||
if (tag == 15u)
|
||||
tag = readUByte().toUInt()
|
||||
return JceHead(tag = tag.toInt(), type = type.toByte())
|
||||
}
|
||||
|
||||
fun read(default: Byte, tag: Int): Byte = readByteOrNull(tag) ?: default
|
||||
@ -79,7 +85,7 @@ class JceInput(
|
||||
fun readBooleanArray(tag: Int): BooleanArray = readBooleanArrayOrNull(tag) ?: error("cannot find tag $tag")
|
||||
fun <K, V> readMap(defaultKey: K, defaultValue: V, tag: Int): Map<K, V> = readMapOrNull(defaultKey, defaultValue, tag) ?: error("cannot find tag $tag")
|
||||
fun <T> readList(defaultElement: T, tag: Int): List<T> = readListOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
|
||||
fun <T> readSimpleArray(defaultElement: T, tag: Int): Array<T> = readArrayOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
|
||||
inline fun <reified T> readSimpleArray(defaultElement: T, tag: Int): Array<T> = readArrayOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
|
||||
fun <J : JceStruct> readJceStruct(factory: JceStruct.Factory<J>, tag: Int): J = readJceStructOrNull(factory, tag) ?: error("cannot find tag $tag")
|
||||
fun readStringArray(tag: Int): Array<String> = readArrayOrNull("", tag) ?: error("cannot find tag $tag")
|
||||
|
||||
@ -142,7 +148,7 @@ class JceInput(
|
||||
|
||||
fun readByteArrayOrNull(tag: Int): ByteArray? = skipToTagOrNull(tag) {
|
||||
when (it.type.toInt()) {
|
||||
9 -> ByteArray(input.readInt()) { readByte(tag) }
|
||||
9 -> ByteArray(readInt(0)) { readByte(0) }
|
||||
13 -> {
|
||||
val head = readHead()
|
||||
check(head.type.toInt() == 0) { "type mismatch" }
|
||||
@ -154,56 +160,56 @@ class JceInput(
|
||||
|
||||
fun readShortArrayOrNull(tag: Int): ShortArray? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
ShortArray(input.readInt()) { readShort(tag) }
|
||||
ShortArray(readInt(0)) { readShort(0) }
|
||||
}
|
||||
|
||||
fun readDoubleArrayOrNull(tag: Int): DoubleArray? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
DoubleArray(input.readInt()) { readDouble(tag) }
|
||||
DoubleArray(readInt(0)) { readDouble(0) }
|
||||
}
|
||||
|
||||
fun readFloatArrayOrNull(tag: Int): FloatArray? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
FloatArray(input.readInt()) { readFloat(tag) }
|
||||
FloatArray(readInt(0)) { readFloat(0) }
|
||||
}
|
||||
|
||||
fun readIntArrayOrNull(tag: Int): IntArray? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
IntArray(input.readInt()) { readInt(tag) }
|
||||
IntArray(readInt(0)) { readInt(0) }
|
||||
}
|
||||
|
||||
fun readLongArrayOrNull(tag: Int): LongArray? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
LongArray(input.readInt()) { readLong(tag) }
|
||||
LongArray(readInt(0)) { readLong(0) }
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <reified T> readArrayOrNull(tag: Int): Array<T>? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
Array(input.readInt()) { readSimpleObject<T>(tag) }
|
||||
Array(readInt(0)) { readSimpleObject<T>(0) }
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> readArrayOrNull(defaultElement: T, tag: Int): Array<T>? = skipToTagOrNull(tag) {
|
||||
inline fun <reified T> readArrayOrNull(defaultElement: T, tag: Int): Array<T>? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
Array(input.readInt()) { readObject(defaultElement, tag) as Any } as Array<T>
|
||||
Array(readInt(0)) { readObject(defaultElement, 0) }
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <J : JceStruct> readJceStructArrayOrNull(factory: JceStruct.Factory<J>, tag: Int): Array<J>? = skipToTagOrNull(tag) {
|
||||
inline fun <reified J : JceStruct> readJceStructArrayOrNull(factory: JceStruct.Factory<J>, tag: Int): Array<J>? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
Array(input.readInt()) { readObject(factory, tag) as Any } as Array<J>
|
||||
Array(readInt(0)) { readJceStruct(factory, 0) }
|
||||
}
|
||||
|
||||
fun readBooleanArrayOrNull(tag: Int): BooleanArray? = skipToTagOrNull(tag) {
|
||||
require(it.type.toInt() == 9) { "type mismatch" }
|
||||
BooleanArray(input.readInt()) { readBoolean(tag) }
|
||||
BooleanArray(readInt(0)) { readBoolean(0) }
|
||||
}
|
||||
|
||||
fun readStringOrNull(tag: Int): String? = skipToTagOrNull(tag) { head ->
|
||||
return when (head.type.toInt()) {
|
||||
6 -> input.readString(input.readUByte().toInt())
|
||||
7 -> input.readString(input.readUInt().also { require(it.toInt() in 1 until 104857600) { "bad string length: $it" } }.toInt())
|
||||
6 -> input.readString(input.readUByte().toInt(), charset = charset)
|
||||
7 -> input.readString(input.readUInt().toInt().also { require(it in 1 until 104857600) { "bad string length: $it" } }, charset = charset)
|
||||
else -> error("type mismatch: ${head.type}")
|
||||
}
|
||||
}
|
||||
@ -233,7 +239,7 @@ class JceInput(
|
||||
val size = readInt(0)
|
||||
val list = ArrayList<T>(size)
|
||||
repeat(size) {
|
||||
list[it] = readObject(defaultElement, tag)
|
||||
list.add(readObject(defaultElement, 0))
|
||||
}
|
||||
return list
|
||||
}
|
||||
@ -267,9 +273,8 @@ class JceInput(
|
||||
is ByteArray -> readByteArray(tag)
|
||||
is FloatArray -> readByteArray(tag)
|
||||
is DoubleArray -> readDoubleArrayOrNull(tag)
|
||||
is JceStruct.Factory<JceStruct> -> readJceStruct(default, tag) as T
|
||||
is List<*> -> {
|
||||
readList(default[0], tag)
|
||||
readList(default, tag)
|
||||
}
|
||||
is Map<*, *> -> {
|
||||
val entry = default.entries.first()
|
||||
@ -325,19 +330,13 @@ class JceInput(
|
||||
6 -> this.input.discardExact(this.input.readUByte().toInt())
|
||||
7 -> this.input.discardExact(this.input.readInt())
|
||||
8 -> { // map
|
||||
val length = this.readInt(0)
|
||||
var read = 0
|
||||
while (read < length * 2) {
|
||||
repeat(this.readInt(0) * 2) {
|
||||
skipField()
|
||||
++read
|
||||
}
|
||||
}
|
||||
9 -> { // list
|
||||
val length = this.readInt(0)
|
||||
var read = 0
|
||||
while (read < length) {
|
||||
repeat(this.readInt(0)) {
|
||||
skipField()
|
||||
++read
|
||||
}
|
||||
}
|
||||
10 -> this.skipToStructEnd()
|
||||
@ -360,8 +359,10 @@ private inline fun <R> JceInput.skipToTag(tag: Int, block: (JceHead) -> R): R {
|
||||
@PublishedApi
|
||||
internal inline fun <R> JceInput.skipToTagOrNull(tag: Int, block: (JceHead) -> R): R? {
|
||||
while (true) {
|
||||
if (this.input.endOfInput)
|
||||
if (this.input.endOfInput) {
|
||||
println("endOfInput")
|
||||
return null
|
||||
}
|
||||
|
||||
val head = peakHead()
|
||||
if (head.tag > tag) {
|
||||
|
@ -2,6 +2,7 @@ package net.mamoe.mirai.qqandroid.network.io
|
||||
|
||||
import kotlinx.io.charsets.Charset
|
||||
import kotlinx.io.core.*
|
||||
import kotlin.experimental.or
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@PublishedApi
|
||||
@ -45,7 +46,7 @@ class JceOutput(
|
||||
|
||||
fun writeByte(v: Byte, tag: Int) {
|
||||
if (v.toInt() == 0) {
|
||||
writeHead(ZERO_TAG, tag)
|
||||
writeHead(ZERO_TYPE, tag)
|
||||
} else {
|
||||
writeHead(BYTE, tag)
|
||||
output.writeByte(v)
|
||||
@ -147,7 +148,7 @@ class JceOutput(
|
||||
if (v in Byte.MIN_VALUE..Byte.MAX_VALUE) {
|
||||
writeByte(v.toByte(), tag)
|
||||
} else {
|
||||
writeHead(BYTE, tag)
|
||||
writeHead(SHORT, tag)
|
||||
output.writeShort(v)
|
||||
}
|
||||
}
|
||||
@ -266,7 +267,7 @@ class JceOutput(
|
||||
const val STRING4: Int = 7
|
||||
const val STRUCT_BEGIN: Int = 10
|
||||
const val STRUCT_END: Int = 11
|
||||
const val ZERO_TAG: Int = 12
|
||||
const val ZERO_TYPE: Int = 12
|
||||
|
||||
private fun Any?.getClassName(): KClass<out Any> = if (this == null) Unit::class else this::class
|
||||
}
|
||||
@ -278,7 +279,7 @@ class JceOutput(
|
||||
return
|
||||
}
|
||||
if (tag < 256) {
|
||||
this.output.writeByte((type or 0xF0).toByte())
|
||||
this.output.writeByte((type.toByte() or 0xF0.toByte()))
|
||||
this.output.writeByte(tag.toByte())
|
||||
return
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package net.mamoe.mirai.qqandroid.network.io
|
||||
abstract class JceStruct {
|
||||
abstract fun writeTo(builder: JceOutput)
|
||||
|
||||
interface Factory<out T : JceStruct> {
|
||||
interface Factory<out T : JceStruct> {
|
||||
fun newInstanceFrom(input: JceInput): T
|
||||
}
|
||||
}
|
@ -26,12 +26,12 @@ class SvcReqRegister(
|
||||
var lBid: Long = 0L,
|
||||
var lCpId: Long = 0L,
|
||||
var lUin: Long = 0L,
|
||||
var sBuildVer: String? = null,
|
||||
var sChannelNo: String? = null,
|
||||
var sBuildVer: String? = "",
|
||||
var sChannelNo: String? = "",
|
||||
var sOther: String = "",
|
||||
var strDevName: String? = null,
|
||||
var strDevType: String? = null,
|
||||
var strIOSIdfa: String? = null,
|
||||
var strIOSIdfa: String? = "",
|
||||
var strOSVer: String? = null,
|
||||
var strVendorName: String? = null,
|
||||
var strVendorOSName: String? = null,
|
||||
@ -59,7 +59,7 @@ class SvcReqRegister(
|
||||
this.timeStamp = input.readLong(10)
|
||||
this.iOSVersion = input.readLong(11)
|
||||
this.cNetType = input.readByte(12)
|
||||
this.sBuildVer = input.readString(13)
|
||||
this.sBuildVer = input.readStringOrNull(13)
|
||||
this.bRegType = input.readByte(14)
|
||||
this.vecDevParam = input.readByteArrayOrNull(15)
|
||||
this.vecGuid = input.readByteArrayOrNull(16)
|
||||
|
@ -64,7 +64,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
|
||||
* full packet without length
|
||||
*/
|
||||
// do not inline. Exceptions thrown will not be reported correctly
|
||||
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) {
|
||||
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer): Unit {
|
||||
rawInput.readBytes().let {
|
||||
PacketLogger.verbose("开始处理包: ${it.toUHexString()}")
|
||||
it.toReadPacket()
|
||||
@ -89,28 +89,34 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
|
||||
|
||||
//debugPrint("remaining")
|
||||
|
||||
(if (flag2 == 2) {
|
||||
PacketLogger.verbose("SSO, 尝试使用 16 zero 解密.")
|
||||
kotlin.runCatching {
|
||||
decryptBy(DECRYPTER_16_ZERO).also { PacketLogger.verbose("成功使用 16 zero 解密") }
|
||||
ByteArrayPool.useInstance { data ->
|
||||
val size = this.readAvailable(data)
|
||||
|
||||
(if (flag2 == 2) {
|
||||
PacketLogger.verbose("SSO, 尝试使用 16 zero 解密.")
|
||||
kotlin.runCatching {
|
||||
data.decryptBy(DECRYPTER_16_ZERO, size).also { PacketLogger.verbose("成功使用 16 zero 解密") }
|
||||
}
|
||||
} else {
|
||||
PacketLogger.verbose("Uni, 尝试使用 d2Key 解密.")
|
||||
kotlin.runCatching {
|
||||
data.decryptBy(bot.client.wLoginSigInfo.d2Key, size).also { PacketLogger.verbose("成功使用 d2Key 解密") }
|
||||
}
|
||||
}).getOrElse {
|
||||
PacketLogger.verbose("失败, 尝试其他各种key")
|
||||
bot.client.tryDecryptOrNull(data) { it }
|
||||
}?.toReadPacket()?.also { decryptedData ->
|
||||
when (flag1) {
|
||||
0x0A -> parseLoginSsoPacket(bot, decryptedData, consumer)
|
||||
0x0B -> parseUniPacket(bot, decryptedData, consumer)
|
||||
}
|
||||
} ?: inline {
|
||||
PacketLogger.error("任何key都无法解密: ${data.toUHexString()}")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
PacketLogger.verbose("Uni, 尝试使用 d2Key 解密.")
|
||||
kotlin.runCatching {
|
||||
decryptBy(bot.client.wLoginSigInfo.d2Key).also { PacketLogger.verbose("成功使用 d2Key 解密") }
|
||||
}
|
||||
}).getOrElse {
|
||||
PacketLogger.verbose("失败, 尝试其他各种key")
|
||||
bot.client.tryDecryptOrNull(this) { it.toReadPacket() }
|
||||
}?.also { decryptedData ->
|
||||
when(flag1) {
|
||||
0x0A -> parseLoginSsoPacket(bot, decryptedData, consumer)
|
||||
0x0B -> parseUniPacket(bot, decryptedData, consumer)
|
||||
}
|
||||
} ?: inline {
|
||||
PacketLogger.error("任何key都无法解密")
|
||||
return
|
||||
}
|
||||
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +161,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
|
||||
|
||||
if (packetFactory == null) {
|
||||
bot.logger.warning("找不到包 PacketFactory")
|
||||
PacketLogger.verbose("最外层解密后的 body = ${this.readBytes().toUHexString()}")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.login
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
|
||||
import net.mamoe.mirai.qqandroid.network.io.jceMap
|
||||
import net.mamoe.mirai.qqandroid.network.io.jceStruct
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.jce.SvcReqRegister
|
||||
@ -17,7 +15,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacke
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
|
||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
import net.mamoe.mirai.utils.io.debugPrint
|
||||
import net.mamoe.mirai.utils.io.encodeToString
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
@ -64,65 +61,64 @@ internal object SvcReqRegisterPacket : PacketFactory<SvcReqRegisterPacket.Respon
|
||||
sFuncName = "SvcReqRegister"
|
||||
sBuffer = jceMap(
|
||||
0,
|
||||
"SvcReqRegister" to buildJcePacket {
|
||||
writeObject(jceStruct(
|
||||
0,
|
||||
SvcReqRegister(
|
||||
cConnType = 0,
|
||||
lBid = 1 or 2 or 4,
|
||||
lUin = client.uin,
|
||||
iStatus = client.onlineStatus.id,
|
||||
bKikPC = 0, // 是否把 PC 踢下线
|
||||
bKikWeak = 0,
|
||||
timeStamp = currentTimeSeconds, // millis or seconds??
|
||||
iLargeSeq = 1551, // ?
|
||||
bOpenPush = 1,
|
||||
iLocaleID = 2052,
|
||||
bRegType =
|
||||
"SvcReqRegister" to jceStruct(
|
||||
0,
|
||||
SvcReqRegister().apply {
|
||||
cConnType = 0
|
||||
lBid = 1 or 2 or 4
|
||||
lUin = client.uin
|
||||
iStatus = client.onlineStatus.id
|
||||
bKikPC = 0 // 是否把 PC 踢下线
|
||||
bKikWeak = 0
|
||||
timeStamp = 0
|
||||
// timeStamp = currentTimeSeconds // millis or seconds??
|
||||
iLargeSeq = 1551 // ?
|
||||
bOpenPush = 1
|
||||
iLocaleID = 2052
|
||||
bRegType =
|
||||
(if (regPushReason == RegPushReason.appRegister ||
|
||||
regPushReason == RegPushReason.fillRegProxy ||
|
||||
regPushReason == RegPushReason.createDefaultRegInfo ||
|
||||
regPushReason == RegPushReason.setOnlineStatus
|
||||
) 0 else 1).toByte(),
|
||||
bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0,
|
||||
iOSVersion = client.device.version.sdk.toLong(),
|
||||
cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0,
|
||||
vecGuid = client.device.guid,
|
||||
strDevName = client.device.model.encodeToString(),
|
||||
strDevType = client.device.model.encodeToString(),
|
||||
strOSVer = client.device.version.release.encodeToString(),
|
||||
) 0 else 1).toByte()
|
||||
bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0
|
||||
iOSVersion = client.device.version.sdk.toLong()
|
||||
cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0
|
||||
vecGuid = client.device.guid
|
||||
strDevName = client.device.model.encodeToString()
|
||||
strDevType = client.device.model.encodeToString()
|
||||
strOSVer = client.device.version.release.encodeToString()
|
||||
|
||||
uOldSSOIp = 0,
|
||||
uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String ->
|
||||
acc or ((s.toLong() shl (index * 16)))
|
||||
},
|
||||
strVendorName = "MIUI",
|
||||
strVendorOSName = "?ONEPLUS A5000_23_17",
|
||||
// register 时还需要
|
||||
/*
|
||||
var44.uNewSSOIp = field_127445;
|
||||
var44.uOldSSOIp = field_127444;
|
||||
var44.strVendorName = ROMUtil.getRomName();
|
||||
var44.strVendorOSName = ROMUtil.getRomVersion(20);
|
||||
*/
|
||||
bytes_0x769_reqbody = ProtoBuf.dump(
|
||||
Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
|
||||
rpt_config_list = listOf(
|
||||
Oidb0x769.ConfigSeq(
|
||||
type = 46,
|
||||
version = 0
|
||||
),
|
||||
Oidb0x769.ConfigSeq(
|
||||
type = 283,
|
||||
version = 0
|
||||
)
|
||||
uOldSSOIp = 0
|
||||
uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String ->
|
||||
acc or ((s.toLong() shl (index * 16)))
|
||||
}
|
||||
strVendorName = "MIUI"
|
||||
strVendorOSName = "?ONEPLUS A5000_23_17"
|
||||
// register 时还需要
|
||||
/*
|
||||
var44.uNewSSOIp = field_127445;
|
||||
var44.uOldSSOIp = field_127444;
|
||||
var44.strVendorName = ROMUtil.getRomName();
|
||||
var44.strVendorOSName = ROMUtil.getRomVersion(20);
|
||||
*/
|
||||
bytes_0x769_reqbody = ProtoBuf.dump(
|
||||
Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
|
||||
rpt_config_list = listOf(
|
||||
Oidb0x769.ConfigSeq(
|
||||
type = 46,
|
||||
version = 0
|
||||
),
|
||||
Oidb0x769.ConfigSeq(
|
||||
type = 283,
|
||||
version = 0
|
||||
)
|
||||
)
|
||||
),
|
||||
bSetMute = 0
|
||||
)
|
||||
)
|
||||
), 0)
|
||||
}.readBytes()
|
||||
bSetMute = 0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
this.writePacket(this.build().debugPrint("sso body"))
|
||||
|
@ -152,11 +152,51 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) {
|
||||
|
||||
is ByteArray -> {
|
||||
if (this.size == 0) "<Empty ByteArray>"
|
||||
else this.toUHexString()// + " (${this.encodeToString()})"
|
||||
else this.toUHexString()
|
||||
}
|
||||
is UByteArray -> {
|
||||
if (this.size == 0) "<Empty UByteArray>"
|
||||
else this.toUHexString()// + " (${this.encodeToString()})"
|
||||
else this.toUHexString()
|
||||
}
|
||||
is ShortArray -> {
|
||||
if (this.size == 0) "<Empty ShortArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is IntArray -> {
|
||||
if (this.size == 0) "<Empty IntArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is LongArray -> {
|
||||
if (this.size == 0) "<Empty LongArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is FloatArray -> {
|
||||
if (this.size == 0) "<Empty FloatArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is DoubleArray -> {
|
||||
if (this.size == 0) "<Empty DoubleArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is UShortArray -> {
|
||||
if (this.size == 0) "<Empty ShortArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is UIntArray -> {
|
||||
if (this.size == 0) "<Empty IntArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is ULongArray -> {
|
||||
if (this.size == 0) "<Empty LongArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is Array<*> -> {
|
||||
if (this.size == 0) "<Empty Array>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
is BooleanArray -> {
|
||||
if (this.size == 0) "<Empty BooleanArray>"
|
||||
else this.iterator().contentToString()
|
||||
}
|
||||
|
||||
is ProtoMap -> "ProtoMap(size=$size){\n" + this.toStringPrefixed("$prefix${ProtoMap.indent}${ProtoMap.indent}") + "\n$prefix${ProtoMap.indent}}"
|
||||
|
@ -3,6 +3,8 @@
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import kotlinx.io.OutputStream
|
||||
import kotlinx.io.charsets.Charset
|
||||
import kotlinx.io.charsets.Charsets
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.contact.GroupId
|
||||
@ -180,13 +182,13 @@ internal inline fun illegalArgument(message: String? = null): Nothing = error(me
|
||||
fun Map<Int, String>.printTLVMap(name: String = "") =
|
||||
debugPrintln("TLVMap $name= " + this.mapKeys { it.key.toInt().toUShort().toUHexString() })
|
||||
|
||||
fun Input.readString(length: Int): String = String(this.readBytes(length))
|
||||
fun Input.readString(length: Long): String = String(this.readBytes(length.toInt()))
|
||||
fun Input.readString(length: Short): String = String(this.readBytes(length.toInt()))
|
||||
fun Input.readString(length: Int, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length), charset = charset)
|
||||
fun Input.readString(length: Long, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
|
||||
fun Input.readString(length: Short, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
|
||||
@JvmSynthetic
|
||||
fun Input.readString(length: UShort): String = String(this.readBytes(length.toInt()))
|
||||
fun Input.readString(length: UShort, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
|
||||
|
||||
fun Input.readString(length: Byte): String = String(this.readBytes(length.toInt()))
|
||||
fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
|
||||
|
||||
@JvmSynthetic
|
||||
fun Input.readStringUntil(stopSignalExclude: UByte, expectingEOF: Boolean = false): String = readStringUntil(stopSignalExclude.toByte(), expectingEOF)
|
||||
|
@ -353,6 +353,8 @@ public final class JceInputStream {
|
||||
throw new JceDecodeException(var5.getMessage());
|
||||
}
|
||||
|
||||
System.out.println(var1.toString());
|
||||
|
||||
var4 = new JceInputStream$HeadData();
|
||||
this.readHead(var4);
|
||||
if (var4.type != 10) {
|
||||
|
@ -400,18 +400,18 @@ public class JceOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public void writeHead(byte var1, int var2) {
|
||||
byte var3;
|
||||
if (var2 < 15) {
|
||||
var3 = (byte) (var2 << 4 | var1);
|
||||
this.field_80728.put(var3);
|
||||
} else if (var2 < 256) {
|
||||
var3 = (byte) (var1 | 240);
|
||||
this.field_80728.put(var3);
|
||||
this.field_80728.put((byte) var2);
|
||||
} else {
|
||||
throw new JceEncodeException("tag is too large: " + var2);
|
||||
}
|
||||
public void writeHead(byte var1, int tag) {
|
||||
byte var3;
|
||||
if (tag < 15) {
|
||||
var3 = (byte) (tag << 4 | var1);
|
||||
this.field_80728.put(var3);
|
||||
} else if (tag < 256) {
|
||||
var3 = (byte) (var1 | 240);
|
||||
this.field_80728.put(var3);
|
||||
this.field_80728.put((byte) tag);
|
||||
} else {
|
||||
throw new JceEncodeException("tag is too large: " + tag);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeStringByte(String var1, int var2) {
|
||||
|
287
mirai-debug/src/test/kotlin/jceTest/JceInputTest.kt
Normal file
287
mirai-debug/src/test/kotlin/jceTest/JceInputTest.kt
Normal file
@ -0,0 +1,287 @@
|
||||
package jceTest
|
||||
|
||||
import io.ktor.util.InternalAPI
|
||||
import jce.jce.JceInputStream
|
||||
import jceTest.JceOutputTest.TestMiraiStruct
|
||||
import jceTest.JceOutputTest.TestQQStruct
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import net.mamoe.mirai.qqandroid.network.io.JceInput
|
||||
import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
|
||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||
import net.mamoe.mirai.utils.io.toIoBuffer
|
||||
import org.junit.Test
|
||||
|
||||
private infix fun <T> T.shouldEqualTo(another: T) {
|
||||
if (this is Array<*>) {
|
||||
this.contentEquals(another as Array<*>)
|
||||
} else
|
||||
check(this.contentToString() == another.contentToString()) {
|
||||
"""actual: ${this.contentToString()}
|
||||
|required: ${another.contentToString()}
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(InternalAPI::class)
|
||||
private fun <R> ByteArray.qqJce(block: JceInputStream.() -> R): R {
|
||||
return JceInputStream(this).run(block)
|
||||
}
|
||||
|
||||
private fun <R> ByteArray.read(block: JceInput.() -> R): R {
|
||||
return JceInput(this.toIoBuffer()).run(block)
|
||||
}
|
||||
|
||||
private fun ByteReadPacket.check(block: ByteArray.() -> Unit) {
|
||||
this.readBytes().apply(block)
|
||||
}
|
||||
|
||||
internal class JceInputTest {
|
||||
|
||||
@Test
|
||||
fun readByte() = buildJcePacket {
|
||||
writeByte(1, 1)
|
||||
}.check {
|
||||
read {
|
||||
readByte(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(0.toByte(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readDouble() = buildJcePacket {
|
||||
writeDouble(1.0, 1)
|
||||
}.check {
|
||||
read {
|
||||
readDouble(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(0.toDouble(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readFloat() = buildJcePacket {
|
||||
writeFloat(1.0f, 1)
|
||||
}.check {
|
||||
read {
|
||||
readFloat(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(0.toFloat(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readFully() = buildJcePacket {
|
||||
writeFully(byteArrayOf(1, 2, 3), 1)
|
||||
}.check {
|
||||
read {
|
||||
readByteArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(byteArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully() = buildJcePacket {
|
||||
writeFully(shortArrayOf(1, 2, 3), 1)
|
||||
}.check {
|
||||
read {
|
||||
readShortArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(shortArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully1() = buildJcePacket {
|
||||
writeFully(intArrayOf(1, 2, 3), 1)
|
||||
}.check {
|
||||
read {
|
||||
readIntArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(intArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully2() = buildJcePacket {
|
||||
writeFully(longArrayOf(1, 2, 3), 1)
|
||||
}.check {
|
||||
read {
|
||||
readLongArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(longArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully3() = buildJcePacket {
|
||||
writeFully(booleanArrayOf(true, false, true), 1)
|
||||
}.check {
|
||||
read {
|
||||
readBooleanArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(booleanArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully4() = buildJcePacket {
|
||||
writeFully(floatArrayOf(1f, 2f, 3f), 1)
|
||||
}.check {
|
||||
read {
|
||||
readFloatArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(floatArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully5() = buildJcePacket {
|
||||
writeFully(doubleArrayOf(1.0, 2.0, 3.0), 1)
|
||||
}.check {
|
||||
read {
|
||||
readDoubleArray(1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(doubleArrayOf(), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully6() = buildJcePacket {
|
||||
writeFully(arrayOf("sss", "哈哈"), 1)
|
||||
}.check {
|
||||
read {
|
||||
readSimpleArray("", 1)
|
||||
} shouldEqualTo qqJce {
|
||||
read(arrayOf(""), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully7() = buildJcePacket {
|
||||
writeFully(arrayOf("sss", "哈哈"), 1)
|
||||
}.check {
|
||||
read {
|
||||
readArrayOrNull("", 1)!!
|
||||
} shouldEqualTo qqJce {
|
||||
read(arrayOf(""), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteFully8() = buildJcePacket {
|
||||
writeFully(arrayOf(TestMiraiStruct("Haha")), 1)
|
||||
}.check {
|
||||
read {
|
||||
readJceStructArrayOrNull(TestMiraiStruct, 1)!!
|
||||
} shouldEqualTo qqJce {
|
||||
read(arrayOf(TestQQStruct("stub")), 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readInt() = buildJcePacket {
|
||||
writeInt(1, 2)
|
||||
}.check {
|
||||
read {
|
||||
readInt(2)
|
||||
} shouldEqualTo qqJce {
|
||||
read(0, 2, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readLong() = buildJcePacket {
|
||||
writeLong(1, 2)
|
||||
}.check {
|
||||
read {
|
||||
readLong(2)
|
||||
} shouldEqualTo qqJce {
|
||||
read(0L, 2, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readShort() = buildJcePacket {
|
||||
writeShort(1, 2)
|
||||
}.check {
|
||||
read {
|
||||
readShort(2)
|
||||
} shouldEqualTo qqJce {
|
||||
read(0.toShort(), 2, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readBoolean() = buildJcePacket {
|
||||
writeBoolean(true, 2)
|
||||
}.check {
|
||||
read {
|
||||
readBoolean(2)
|
||||
} shouldEqualTo qqJce {
|
||||
read(false, 2, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readString() = buildJcePacket {
|
||||
writeString("嗨", 2)
|
||||
}.check {
|
||||
read {
|
||||
readString(2)
|
||||
} shouldEqualTo qqJce {
|
||||
read("", 2, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readMap() = buildJcePacket {
|
||||
writeMap(mapOf(123.0 to "Hello"), 3)
|
||||
}.check {
|
||||
read {
|
||||
readMap(0.0, "", 3)
|
||||
} shouldEqualTo qqJce {
|
||||
read(mapOf(0.0 to ""), 3, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readCollection() = buildJcePacket {
|
||||
writeCollection(listOf("1", "还"), 3)
|
||||
}.check {
|
||||
repeat(0) {
|
||||
error("fuck kotlin")
|
||||
}
|
||||
read {
|
||||
readList("", 3)
|
||||
} shouldEqualTo qqJce {
|
||||
read(listOf(""), 3, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun readJceStruct() = buildJcePacket {
|
||||
writeJceStruct(TestMiraiStruct("123"), 3)
|
||||
}.check {
|
||||
read {
|
||||
readJceStruct(TestMiraiStruct, 3)
|
||||
} shouldEqualTo qqJce {
|
||||
read(TestQQStruct("stub"), 3, true)!!
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readObject() = buildJcePacket {
|
||||
writeObject(123, 3)
|
||||
}.check {
|
||||
read {
|
||||
readObject(123, 3)
|
||||
} shouldEqualTo qqJce {
|
||||
read(123 as Any, 3, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -221,13 +221,9 @@ internal class JceOutputTest {
|
||||
@Test
|
||||
fun writeCollection() {
|
||||
buildJcePacket {
|
||||
writeMap(mapOf("" to ""), 1)
|
||||
writeMap(mapOf("" to 123), 2)
|
||||
writeMap(mapOf(123.0 to "Hello"), 3)
|
||||
writeCollection(listOf("啊", "333", "1"), 1)
|
||||
} shouldEqualTo qqJce {
|
||||
write(mapOf("" to ""), 1)
|
||||
write(mapOf("" to 123), 2)
|
||||
write(mapOf(123.0 to "Hello"), 3)
|
||||
write(listOf("啊", "333", "1"), 1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +244,8 @@ internal class JceOutputTest {
|
||||
class TestQQStruct(
|
||||
private var message: String
|
||||
) : JceStruct() {
|
||||
constructor() : this("")
|
||||
|
||||
override fun readFrom(var1: JceInputStream) {
|
||||
message = var1.read("", 0, true)
|
||||
}
|
||||
@ -255,6 +253,10 @@ internal class JceOutputTest {
|
||||
override fun writeTo(var1: JceOutputStream) {
|
||||
var1.write(message, 0)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "TestMiraiStruct(message=$message)"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
Loading…
Reference in New Issue
Block a user