Commit 2f1114d8 authored by jiahua.liu's avatar jiahua.liu

Merge branch 'master' of https://github.com/mamoe/mirai

parents 7b124705 9353a5af
......@@ -33,7 +33,7 @@
[Mirai-Console](https://github.com/mamoe/mirai/tree/master/mirai-console) 插件支持, 在终端中启动Mirai并获得机器人服务
## Use as a library
**把 Mirai 作为库内置于您的项目中使用.**
**mirai-core 为独立设计, 可以作为库内置于您的任意 Java/Android 项目中使用.**
Mirai 只上传在 `jcenter`, 因此请确保在 `build.gradle` 添加 `jcenter()` 仓库
```kotlin
repositories{
......@@ -120,4 +120,4 @@ bot.subscribeAlways<MemberPermissionChangedEvent> {
## Acknowledgement
特别感谢 [JetBrains](https://www.jetbrains.com/?from=mirai) 提供的免费 [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai) 等 IDE 授权
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
\ No newline at end of file
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
apply plugin: "com.android.library"
android {
compileSdkVersion(29)
defaultConfig {
minSdkVersion(15)
}
}
\ No newline at end of file
......@@ -3,7 +3,6 @@
plugins {
kotlin("multiplatform")
id("kotlinx-atomicfu")
id("com.android.library")
id("kotlinx-serialization")
`maven-publish`
id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
......@@ -32,15 +31,9 @@ version = rootProject.ext.get("mirai_version")!!.toString()
val isAndroidSDKAvailable: Boolean by project
android {
compileSdkVersion(29)
defaultConfig {
minSdkVersion(15)
}
}
kotlin {
if (isAndroidSDKAvailable) {
apply(from = rootProject.file("gradle/android.gradle"))
android("android") {
publishAllLibraryVariants()
}
......
......@@ -76,7 +76,7 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(
(context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId
}
}.getOrElse { "" }
override val ipAddress: String get() = localIpAddress()
override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
override val androidId: ByteArray get() = Build.ID.toByteArray()
override val apn: ByteArray get() = "wifi".toByteArray()
......
......@@ -2,21 +2,31 @@ package net.mamoe.mirai.qqandroid.network.io
import kotlinx.io.charsets.Charset
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.readRemainingBytes
import kotlin.reflect.KClass
private val CharsetGBK = Charset.forName("GBK")
@PublishedApi
internal val CharsetGBK = Charset.forName("GBK")
fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket {
inline fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket {
return JceOutput(stringCharset).apply(block).build()
}
fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) {
inline fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) {
return this.writePacket(buildJcePacket(stringCharset, block))
}
fun jceStruct(tag: Int, struct: JceStruct): ByteArray{
return buildJcePacket {
writeJceStruct(struct, tag)
}.readBytes()
}
fun <K, V> jceMap(tag: Int, vararg entries: Pair<K, V>): ByteArray {
return buildJcePacket {
writeMap(mapOf(*entries), tag)
}.readBytes()
}
/**
*
* From: com.qq.taf.jce.JceOutputStream
......
package net.mamoe.mirai.qqandroid.network.io
import kotlinx.io.core.BytePacketBuilder
abstract class JceStruct {
abstract fun writeTo(builder: JceOutput)
interface Factory<out T : JceStruct> {
fun newInstanceFrom(input: JceInput): T
}
}
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.jce
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.io.JceOutput
import net.mamoe.mirai.qqandroid.network.io.JceStruct
private val EMPTY_MAP = mapOf<String, String>()
class RequestPacket(
val sBuffer: ByteArray,
val cPacketType: Byte = 0,
val iMessageType: Int = 0,
val iRequestId: Int = 0,
val iTimeout: Int = 3000,
val iVersion: Short = 3,
val context: Map<String, String> = EMPTY_MAP,
val sFuncName: String = "",
val sServantName: String = "",
val status: Map<String, String> = EMPTY_MAP
) : JceStruct() {
class RequestPacket() : JceStruct() {
lateinit var sBuffer: ByteArray
var cPacketType: Byte = 0
var iMessageType: Int = 0
var iRequestId: Int = 0
var iTimeout: Int = 3000
var iVersion: Short = 3
var context: Map<String, String> = EMPTY_MAP
var sFuncName: String = ""
var sServantName: String = ""
var status: Map<String, String> = EMPTY_MAP
constructor(
sBuffer: ByteArray,
cPacketType: Byte = 0,
iMessageType: Int = 0,
iRequestId: Int = 0,
iTimeout: Int = 3000,
iVersion: Short = 3,
context: Map<String, String> = EMPTY_MAP,
sFuncName: String = "",
sServantName: String = "",
status: Map<String, String> = EMPTY_MAP
) : this() {
this.sBuffer = sBuffer
this.cPacketType = cPacketType
this.iMessageType = iMessageType
this.iRequestId = iRequestId
this.iTimeout = iTimeout
this.iVersion = iVersion
this.context = context
this.sFuncName = sFuncName
this.sServantName = sServantName
this.status = status
}
companion object : Factory<RequestPacket> {
override fun newInstanceFrom(input: JceInput): RequestPacket {
val iVersion = input.readShort(1)
val cPacketType = input.readByte(2)
val iMessageType = input.readInt(3)
val iRequestId = input.readInt(4)
val sServantName = input.readString(5)
val sFuncName = input.readString(6)
val sBuffer = input.readByteArray(7)
val iTimeout = input.readInt(8)
val context = input.readMap("", "", 9)
val status = input.readMap("", "", 10)
return RequestPacket(sBuffer, cPacketType, iMessageType, iRequestId, iTimeout, iVersion, context, sFuncName, sServantName, status)
}
}
override fun writeTo(builder: JceOutput) {
builder.write(this.iVersion, 1)
builder.write(this.cPacketType, 2)
......
package net.mamoe.mirai.qqandroid.network.protocol.jce
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.io.JceOutput
import net.mamoe.mirai.qqandroid.network.io.JceStruct
......@@ -25,15 +26,15 @@ class SvcReqRegister(
val lBid: Long = 0L,
val lCpId: Long = 0L,
val lUin: Long = 0L,
val sBuildVer: String? = "",
val sChannelNo: String? = "",
val sBuildVer: String? = null,
val sChannelNo: String? = null,
val sOther: String = "",
val strDevName: String? = "",
val strDevType: String? = "",
val strIOSIdfa: String? = "",
val strOSVer: String? = "",
val strVendorName: String? = "",
val strVendorOSName: String? = "",
val strDevName: String? = null,
val strDevType: String? = null,
val strIOSIdfa: String? = null,
val strOSVer: String? = null,
val strVendorName: String? = null,
val strVendorOSName: String? = null,
val timeStamp: Long = 0L,
val uNewSSOIp: Long = 0L,
val uOldSSOIp: Long = 0L,
......@@ -41,7 +42,12 @@ class SvcReqRegister(
val vecGuid: ByteArray? = null,
val vecServerBuf: ByteArray? = null
) : JceStruct() {
companion object : Factory<RequestPacket> {
override fun newInstanceFrom(input: JceInput): RequestPacket {
TODO("not implemented")
}
}
override fun writeTo(builder: JceOutput) {
builder.write(lUin, 0)
builder.write(lBid, 1)
......
......@@ -5,8 +5,8 @@ import net.mamoe.mirai.qqandroid.network.io.JceOutput
import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
import net.mamoe.mirai.qqandroid.network.io.writeJcePacket
fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket) {
inline fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket.() -> Unit) {
writeJcePacket {
requestPacket.writeTo(this)
RequestPacket().apply(requestPacket).writeTo(this)
}
}
\ No newline at end of file
......@@ -8,8 +8,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId.commandName
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
import net.mamoe.mirai.utils.cryptor.Decrypter
import net.mamoe.mirai.utils.cryptor.DecrypterType
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.SvcReqRegisterPacket
import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.io.*
......@@ -50,7 +49,8 @@ internal typealias PacketConsumer = suspend (packet: Packet, packetId: PacketId,
@UseExperimental(ExperimentalUnsignedTypes::class)
internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
LoginPacket
LoginPacket,
SvcReqRegisterPacket
) {
fun findPacketFactory(commandName: String): PacketFactory<*> = this.first { it.id.commandName == commandName }
......@@ -59,12 +59,12 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
// do not inline. Exceptions thrown will not be reported correctly
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
rawInput.debugPrintIfFail("Incoming packet") {
rawInput.debugIfFail("Incoming packet") {
require(remaining < Int.MAX_VALUE) { "rawInput is too long" }
val expectedLength = readUInt().toInt() - 4
if (expectedLength > 16e7) {
bot.logger.warning("Detect incomplete packet, ignoring.")
return@debugPrintIfFail
return@debugIfFail
}
check(remaining.toInt() == expectedLength) { "Invalid packet length. Expected $expectedLength, got ${rawInput.remaining} Probably packets merged? " }
// login
......@@ -81,13 +81,15 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
}
else -> error("Illegal flag2. Expected 0x02, got $flag2")
}
// 00 00 00 60 00 00 00 0B 02 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 CE 35 53 19 84 A8 1A B8 5B 48 E3 7C D0 A6 BA 58 6A EB CE 50 B9 A0 98 D5 B9 D0 1C 72 E2 86 24 FC 55 44 6C 6E E3 F9 15 6C EC 6C 6B 94 40 F7 B4 45 CF B4 D0 79 84 FE 30 EA 98 84 44 84 02 32 70 DD D7 07 07 72 DE 87 59 AC
0x0B ->
else -> error("Illegal flag1. Expected 0x0A or 0x0B, got $flag1")
}
}
@UseExperimental(ExperimentalUnsignedTypes::class)
private suspend fun parseLoginSsoPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
rawInput.debugPrintIfFail("Login sso packet") {
rawInput.debugIfFail("Login sso packet") {
val commandName: String
val ssoSequenceId: Int
readIoBuffer(readInt() - 4).withUse {
......
......@@ -16,14 +16,15 @@ import kotlin.random.Random
*/
inline class Tlv(val value: ByteArray)
fun BytePacketBuilder.t1(uin: Long, ip: String) {
fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
require(ip.size == 4) { "ip.size must == 4" }
writeShort(0x1)
writeShortLVPacket {
writeShort(1) // _ip_ver
writeInt(Random.nextInt())
writeInt(uin.toInt())
writeTime()
writeFully(ByteArray(4))
writeFully(ip)
writeShort(0)
} shouldEqualsTo 20
}
......
......@@ -10,6 +10,7 @@ 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.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.cryptor.decryptBy
......@@ -27,10 +28,6 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
this._id = PacketId(commandId = 0x0810, commandName = "wtlogin.login")
}
fun hahahaha() {
}
object SubCommand9 {
private const val appId = 16L
private const val subAppId = 537062845L
......@@ -61,8 +58,6 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
LoginType.PASSWORD
)
hahahaha()
/* // from GetStWithPasswd
int mMiscBitmap = this.mMiscBitmap;
if (t.uinDeviceToken) {
......@@ -180,6 +175,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
object Success : LoginPacketResponse()
}
@UseExperimental(MiraiDebugAPI::class)
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse = this.debugPrint("login解析").run {
// 00 09 sub cmd
// 00 type
......@@ -199,6 +195,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() })
tlvMap[0x150]?.let { client.analysisTlv150(it) }
tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
tlvMap[0x161]?.let { client.analysisTlv161(it) }
tlvMap[0x119]?.let { t119Data ->
t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply {
......@@ -207,6 +204,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
// 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00
val tlvMap119 = this.readTLVMap()
println("tlvMap119 KEYS: " + tlvMap119.keys.joinToString { it.contentToString() })
tlvMap119[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
tlvMap119.filterValues { it.size == 16 }.forEach {
println(it.key.toUHexString("") + "=" + it.value.toUHexString())
}
// ???
tlvMap119[0x1c]?.read {
......
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.protocol.jce.RequestPacket
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
import net.mamoe.mirai.qqandroid.network.protocol.jce.writeUniRequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
......@@ -38,73 +37,63 @@ internal object SvcReqRegisterPacket : PacketFactory<SvcReqRegisterPacket.Respon
operator fun invoke(
client: QQAndroidClient,
regPushReason: RegPushReason = RegPushReason.setOnlineStatus
): OutgoingPacket = buildOutgingPacket(client, key = client.wLoginSigInfo.wtSessionTicketKey) {
writeUniRequestPacket(
RequestPacket(
sServantName = "PushService",
sFuncName = "SvcReqRegister",
sBuffer = buildJcePacket {
writeMap(
mapOf(
"SvcReqRegister" to buildJcePacket {
writeJceStruct(
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 = 0,
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(),
): OutgoingPacket = buildOutgingPacket(client, key = client.wLoginSigInfo.d2Key) {
writeUniRequestPacket {
sServantName = "PushService"
sFuncName = "SvcReqRegister"
sBuffer = jceMap(
0,
"SvcReqRegister" to 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 = 0,
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(),
// 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 = 4
),
Oidb0x769.ConfigSeq(
type = 283,
version = 0
)
)
)
),
bSetMute = 0
), 0
// 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 = 4
),
Oidb0x769.ConfigSeq(
type = 283,
version = 0
)
)
}.readBytes()
), 0
)
),
bSetMute = 0
)
}.readBytes()
)
)
)
}
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
......
......@@ -3,10 +3,8 @@ package net.mamoe.mirai.qqandroid.utils
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.io.encodeToString
import net.mamoe.mirai.utils.unsafeWeakRef
abstract class DeviceInfo(
......@@ -42,7 +40,7 @@ abstract class DeviceInfo(
abstract val imsiMd5: ByteArray
abstract val imei: String
abstract val ipAddress: String
abstract val ipAddress: ByteArray
abstract val androidId: ByteArray
......
......@@ -27,7 +27,9 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(
override val imsiMd5: ByteArray
get() = ubyteArrayOf(0xD4u, 0x1Du, 0x8Cu, 0xD9u, 0x8Fu, 0x00u, 0xB2u, 0x04u, 0xE9u, 0x80u, 0x09u, 0x98u, 0xECu, 0xF8u, 0x42u, 0x7Eu).toByteArray()
override val imei: String get() = "858414369211993"
override val ipAddress: String get() = localIpAddress()
@UseExperimental(ExperimentalUnsignedTypes::class)
override val ipAddress: ByteArray
get() = localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
override val androidId: ByteArray get() = "QSR1.190920.001".toByteArray()
override val apn: ByteArray get() = "wifi".toByteArray()
......
......@@ -3,7 +3,6 @@
plugins {
kotlin("multiplatform")
id("kotlinx-atomicfu")
id("com.android.library")
id("kotlinx-serialization")
`maven-publish`
id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
......@@ -32,18 +31,11 @@ version = rootProject.ext.get("mirai_version")!!.toString()
val isAndroidSDKAvailable: Boolean by project
android {
compileSdkVersion(29)
defaultConfig {
minSdkVersion(15)
}
}
kotlin {
if (isAndroidSDKAvailable) {
apply(from = rootProject.file("gradle/android.gradle"))
android("android") {
publishAllLibraryVariants()
project.apply(plugin = "com.android.library")
}
} else {
println(
......
......@@ -10,7 +10,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.data.*
import net.mamoe.mirai.utils.io.debugPrintIfFail
import net.mamoe.mirai.utils.io.debugIfFail
import net.mamoe.mirai.utils.io.readQQ
import net.mamoe.mirai.utils.io.readRemainingBytes
import net.mamoe.mirai.utils.io.toUHexString
......@@ -62,7 +62,7 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl
Unknown0x02DCPacketFlag0x0EMaybeMutePacket(readRemainingBytes())
}
0x11u -> debugPrintIfFail("解析禁言包(0x02DC)时"){ // 猜测这个失败是撤回??
0x11u -> debugIfFail("解析禁言包(0x02DC)时"){ // 猜测这个失败是撤回??
// 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 27 0B 60 E7 11 00 33 08 07 20 E7 C1 AD B8 02 5A 29 08 A6 FE C0 A4 0A 1A 19 08 BC 15 10 C1 95 BC F0 05 18 CA CA 8F DE 04 20 00 28 00 30 A6 FE C0 A4 0A 2A 02 08 00 30 00 38 00
// 失败
......
......@@ -3,7 +3,6 @@
plugins {
kotlin("multiplatform")
id("kotlinx-atomicfu")
id("com.android.library")
id("kotlinx-serialization")
`maven-publish`
id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
......@@ -31,16 +30,9 @@ description = "QQ protocol library"
val isAndroidSDKAvailable: Boolean by project
android {
compileSdkVersion(29)
defaultConfig {
minSdkVersion(15)
}
}
kotlin {
if (isAndroidSDKAvailable) {
project.apply(plugin = "com.android.library")
apply(from = rootProject.file("gradle/android.gradle"))
android("android") {
publishAllLibraryVariants()
}
......
......@@ -5,7 +5,7 @@ import kotlin.reflect.full.allSuperclasses
actual fun Any.contentToStringReflectively(prefix: String): String {
val newPrefix = prefix + ProtoMap.indent
val newPrefix = prefix
return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" +
this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") }
.distinctBy { it.name }
......
......@@ -9,7 +9,7 @@ import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unzip
internal fun IoBuffer.parseMessageFace(): Face {
debugPrintIfFail("Analyzing Face") {
debugIfFail("Analyzing Face") {
discardExact(1)
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
......
......@@ -8,6 +8,7 @@ import kotlin.annotation.AnnotationTarget.*
* 这些 API 可能会在任意时刻更改, 且不会发布任何预警.
* 非常不建议在发行版本中使用这些 API.
*/
@Retention(AnnotationRetention.SOURCE)
@Experimental(level = Experimental.Level.ERROR)
@Target(
CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR,
......@@ -25,12 +26,26 @@ annotation class MiraiInternalAPI(
* 这些 API 不具有稳定性, 且可能会在任意时刻更改.
* 不建议在发行版本中使用这些 API.
*/
@Experimental(level = Experimental.Level.ERROR)
@Retention(AnnotationRetention.SOURCE)
@Experimental(level = Experimental.Level.WARNING)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
annotation class MiraiExperimentalAPI(
val message: String = ""
)
/**
* 标记这个类, 类型, 函数, 属性, 字段, 或构造器为仅供调试阶段使用的.
*
* 这些 API 不具有稳定性, 可能会在任意时刻更改, 并且效率非常低下.
* 非常不建议在发行版本中使用这些 API.
*/
@Retention(AnnotationRetention.SOURCE)
@Experimental(level = Experimental.Level.WARNING)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
annotation class MiraiDebugAPI(
val message: String = ""
)
/**
* 标记这个 API 是自 Mirai 某个版本起才受支持.
*/
......@@ -43,13 +58,13 @@ annotation class SinceMirai(val version: String)
* 包的最后一次修改时间, 和分析时使用的 TIM 版本
*/
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
@Target(FUNCTION, CLASS, PROPERTY)
@Retention(AnnotationRetention.SOURCE)
annotation class PacketVersion(val date: String, val timVersion: String)
/**
* 带有这个注解的 [Packet] 将不会被记录在 log 中.
*/
@Target(AnnotationTarget.CLASS)
@Target(CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class NoLog
\ No newline at end of file
......@@ -163,7 +163,7 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) {
is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() }
is Iterator<*> -> this.asSequence().joinToString(prefix = "[", postfix = "]") { it.contentToString() }
is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() }
is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString() + "=" + it.value.contentToString() }
is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString(prefix) + "=" + it.value.contentToString(prefix) }
else -> {
if (this == null) "null"
else if (this::class.isData) this.toString()
......
......@@ -2,27 +2,32 @@ package net.mamoe.mirai.utils.io
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.withSwitch
import net.mamoe.mirai.utils.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch()
fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this)
@MiraiDebugAPI("Low efficiency.")
fun debugPrintln(any: Any?) = DebugLogger.debug(any)
@MiraiDebugAPI("Low efficiency.")
fun String.debugPrint(name: String): String {
DebugLogger.debug("$name=$this")
return this
}
@MiraiDebugAPI("Low efficiency.")
fun ByteArray.debugPrint(name: String): ByteArray {
DebugLogger.debug(name + "=" + this.toUHexString())
return this
}
@MiraiDebugAPI("Low efficiency.")
fun IoBuffer.debugPrint(name: String): IoBuffer {
ByteArrayPool.useInstance {
val count = this.readAvailable(it)
......@@ -31,6 +36,7 @@ fun IoBuffer.debugPrint(name: String): IoBuffer {
}
}
@MiraiDebugAPI("Low efficiency.")
inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer {
ByteArrayPool.useInstance {
val count = this.readAvailable(it)
......@@ -39,10 +45,12 @@ inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer {
}
}
@MiraiDebugAPI("Low efficiency.")
fun Input.debugDiscardExact(n: Number, name: String = "") {
DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
}
@MiraiDebugAPI("Low efficiency.")
fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
ByteArrayPool.useInstance {
val count = this.readAvailable(it)
......@@ -51,12 +59,24 @@ fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
}
}
inline fun <R> Input.debugPrintIfFail(name: String = "", block: ByteReadPacket.() -> R): R {
/**
* 备份数据, 并在 [block] 失败后执行 [onFail].
*
* 此方法非常低效. 请仅在测试环境使用.
*/
@MiraiDebugAPI("Low efficiency")
@UseExperimental(ExperimentalContracts::class)
inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
callsInPlace(onFail, InvocationKind.UNKNOWN)
}
ByteArrayPool.useInstance {
val count = this.readAvailable(it)
try {
return block(it.toReadPacket(0, count))
} catch (e: Throwable) {
onFail(it.take(count).toByteArray()).readAvailable(it)
DebugLogger.debug("Error in ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count))
throw e
}
......
......@@ -99,6 +99,8 @@ fun UByte.fixToUHex(): String = if (this.toInt() in 0..15) "0${this.toString(16)
/**
* 将无符号 Hex 转为 [ByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
*
* 这个方法很累, 不建议经常使用.
*/
fun String.hexToBytes(): ByteArray =
this.split(" ")
......@@ -110,12 +112,24 @@ fun String.hexToBytes(): ByteArray =
/**
* 每 2 char 为一组, 转换 Hex 为 [ByteArray]
*
* 这个方法很累, 不建议经常使用.
*/
fun String.chunkedHexToBytes(): ByteArray =
this.asSequence().chunked(2).map { (it[0].toString() + it[1]).toUByte(16).toByte() }.toList().toByteArray()
/**
* 删掉全部空格和换行后每 2 char 为一组, 转换 Hex 为 [ByteArray].
*
* 这个方法很累, 不建议经常使用.
*/
fun String.autoHexToBytes(): ByteArray =
this.replace("\n", "").replace(" ", "").asSequence().chunked(2).map { (it[0].toString() + it[1]).toUByte(16).toByte() }.toList().toByteArray()
/**
* 将无符号 Hex 转为 [UByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
*
* 这个方法很累, 不建议经常使用.
*/
fun String.hexToUBytes(): UByteArray =
this.split(" ")
......
......@@ -14,6 +14,8 @@ import kotlin.jvm.JvmSynthetic
*
* Some code from http://wiki.vg/Protocol.
*
* Source project: [Nukkit](http://github.com/nukkit/nukkit)
*
* @author MagicDroidX of Nukkit Project
* @author lmlstarqaq of Nukkit Project
*/
......
......@@ -12,6 +12,7 @@ actual fun Any.contentToStringReflectively(prefix: String): String {
this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") }
.distinctBy { it.name }
.filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" }
.filterNot { it.isEnumConstant }
.joinToStringPrefixed(
prefix = newPrefix
) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment