Debugging updates

This commit is contained in:
Him188 2019-10-07 12:44:44 +08:00
parent 8e97b47a8c
commit 6d58cb1880
14 changed files with 133 additions and 56 deletions

View File

@ -18,7 +18,7 @@
1. Clone
2. Import as Maven project
3. Run demo[Demo 1 Main](mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt#L16)
3. Run demo[Demo 1 Main](mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt#L22)
### 事件 Hook
#### Java:
@ -38,7 +38,7 @@ FriendMessageEvent.subscribeAlways{
![](.github/68f8fec9.png)
发送图片已经完成,但我们还在开发上传图片至服务器。
现在你可以通过发送一张图片给机器人账号,再让机器人账号发送这张图片。你可以查看 [Image.kt](mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Image.kt#L20-L93)
现在你可以通过发送一张图片给机器人账号,再让机器人账号发送这张图片。你可以查看 [Image.kt](mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Image.kt#L20-L93)
## 语言使用说明
我们使用 Kotlin但我们也会保留对 Java 和 Java开发者的支持。

View File

@ -23,6 +23,7 @@ kotlin {
implementation rootProject.ext.coroutine
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.4.0'
implementation 'org.yaml:snakeyaml:1.18'
implementation 'org.jsoup:jsoup:1.12.1'
implementation 'org.ini4j:ini4j:0.5.2'

View File

@ -39,6 +39,7 @@ class PlainText(private val text: String) : Message() {
override operator fun contains(sub: String): Boolean = this.toString().contains(sub)
internal object PacketHelper {
@JvmStatic
fun ofByteArray(data: ByteArray): PlainText = lazyDecode(data) {
it.skip(1)
PlainText(it.readLVString())

View File

@ -4,11 +4,13 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.ListeningStatus
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.bot.BotLoginSucceedEvent
import net.mamoe.mirai.event.events.network.BeforePacketSendEvent
import net.mamoe.mirai.event.events.network.PacketSentEvent
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
import net.mamoe.mirai.event.subscribe
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.NetworkScope
@ -62,7 +64,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
//if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
// loginInternal(ipQueue)//超时或未知, 重试连接下一个服务器
//} else {
state
state
// }
}
}
@ -146,7 +148,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
restartSocket()
}
internal var loginResult: CompletableDeferred<LoginState>? = null
internal lateinit var loginResult: CompletableDeferred<LoginState>
@Synchronized
private fun restartSocket() {
@ -172,10 +174,6 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
}
}
/**
* Start network and touch the server
*/
internal suspend fun touch(serverAddress: String): LoginState {
bot.info("Connecting server: $serverAddress")
restartSocket()
@ -189,10 +187,24 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
//bot.waitForPacket(ServerTouchResponsePacket::class, timeoutMillis) {
// loginResult?.complete(LoginState.TIMEOUT)
//}
var received = false
ServerPacketReceivedEvent.subscribe {
if (it.packet is ServerTouchResponsePacket && it.bot === bot) {
received = true
ListeningStatus.STOPPED
} else
ListeningStatus.LISTENING
}
NetworkScope.launch {
delay(2000)
if (!received) {
loginResult.complete(LoginState.TIMEOUT)
}
}
sendPacket(ClientTouchPacket(bot.account.qqNumber, serverIP))
return withContext(Dispatchers.IO) {
loginResult!!.await()
loginResult.await()
}
}
@ -227,11 +239,10 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
override fun close() {
this.socket?.close()
if (this.loginResult != null) {
if (!this.loginResult!!.isCompleted) {
this.loginResult!!.cancel(CancellationException("socket closed"))
if (this::loginResult.isInitialized) {
if (!this.loginResult.isCompleted && !this.loginResult.isCancelled) {
this.loginResult.cancel(CancellationException("socket closed"))
}
this.loginResult = null
}
}
@ -282,7 +293,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
}
is ServerLoginResponseFailedPacket -> {
socket.loginResult?.complete(packet.loginState)
socket.loginResult.complete(packet.loginState)
bot.close()
return
}
@ -375,7 +386,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
socket.sendPacket(ClientHeartbeatPacket(bot.account.qqNumber, sessionKey))
}
socket.loginResult!!.complete(LoginState.SUCCESS)
socket.loginResult.complete(LoginState.SUCCESS)
loginHandler.changeOnlineStatus(ClientLoginStatus.ONLINE)
}

View File

@ -40,6 +40,7 @@ object TIMProtocol {
* _fixVer
*/
const val fixVer2 = "02 00 00 00 01 01 01 00 00 68 20"
// 02 38 03 00 CD 48 68 3E 03 3F A2 02 00 00 00
/**
* 0825data1
*/
@ -81,7 +82,8 @@ object TIMProtocol {
/**
* 没有任何地方写入了这个 key
*/
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"//16
//const val shareKey = "5B 6C 91 55 D9 92 F5 A7 99 85 37 76 3D 0F 08 B7"//16
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"//16//original
const val key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"

View File

@ -7,10 +7,10 @@ interface Packet
internal object PacketNameFormatter {
@JvmSynthetic
@JvmStatic
private var longestNameLength: Int = 43
@JvmSynthetic
@JvmStatic
fun adjustName(name: String): String {
if (name.length > longestNameLength) {
longestNameLength = name.length

View File

@ -2,10 +2,12 @@
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.event.subscribeWhileTrue
import net.mamoe.mirai.network.protocol.tim.packet.PacketNameFormatter.adjustName
import net.mamoe.mirai.network.protocol.tim.packet.action.ServerCanAddFriendResponsePacket
@ -13,7 +15,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendFriendMessag
import net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.*
import java.io.DataInputStream
import java.io.EOFException
@ -84,9 +85,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
return ServerLoginResponseFailedPacket(when (bytes.size) {
135 -> LoginState.UNKNOWN//账号已经在另一台电脑登录??
63, 319, 351 -> LoginState.WRONG_PASSWORD//63不是密码错误, 应该是登录过频繁
319, 351 -> LoginState.WRONG_PASSWORD
//135 -> LoginState.RETYPE_PASSWORD
279 -> LoginState.BLOCKED
63, 279 -> LoginState.BLOCKED
263 -> LoginState.UNKNOWN_QQ_NUMBER
551, 487 -> LoginState.DEVICE_LOCK
359 -> LoginState.TAKEN_BACK
@ -306,8 +307,8 @@ fun <N : Number> DataInputStream.readShortAt(position: N): Short {
return this.readShort()
}
@JvmSynthetic
//添加@JvmSynthetic 导致 idea 无法检查这个文件的错误
//@JvmSynthetic
fun DataInputStream.gotoWhere(matcher: UByteArray): DataInputStream {
return this.gotoWhere(matcher.toByteArray())
}

View File

@ -26,14 +26,16 @@ class ClientSendFriendMessagePacket(
writeQQ(botQQ)
writeQQ(targetQQ)
writeHex("00 00 00 08 00 01 00 04 00 00 00 00")
writeHex("37 0F")
writeHex("37 0F")//TIM最新: 38 03
writeQQ(botQQ)
writeQQ(targetQQ)
write(md5(lazyEncode { md5Key -> md5Key.writeQQ(targetQQ); md5Key.write(sessionKey) }))
writeHex("00 0B")
writeRandom(2)
writeTime()
writeHex("00 00 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00")
writeHex("00 00" +
"00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00")
//01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00
writeTime()
writeRandom(4)
writeHex("00 00 00 00 09 00 86")
@ -54,5 +56,9 @@ class ClientSendFriendMessagePacket(
}
}
fun main() {
}
@PacketId("00 CD")
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input)

View File

@ -0,0 +1,42 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package net.mamoe.mirai.utils
import com.sun.jna.Library
import com.sun.jna.Native
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import sun.misc.Unsafe
/**
* @author Him188moe
*/
object ECDH : IECDH by Native.load("ecdhdll64", IECDH::class.java)
interface IECDH : Library {
//fun encrypt(publicKey: UByteArray, shaKey: UByteArray): UByteArray
fun encrypt(publicKey: Long, shaKey: Long): Long
}
fun main() {
//
// ECDH.encrypt(TIMProtocol.publicKey.hexToUBytes(), TIMProtocol.key0836.hexToUBytes())
val unsafe = Unsafe::class.java.getDeclaredField("theUnsafe").also { it.trySetAccessible() }.get(null) as Unsafe
val publicKeyAddress = unsafe.allocateMemory(25)
TIMProtocol.publicKey.hexToUBytes().forEachIndexed { index, value ->
unsafe.setMemory(publicKeyAddress + index, 1, value.toByte())
}
val key0836Address = unsafe.allocateMemory(16)
TIMProtocol.key0836.hexToUBytes().forEachIndexed { index, value ->
unsafe.setMemory(key0836Address + index, 1, value.toByte())
}
val encrypt = ECDH.encrypt(publicKeyAddress, key0836Address)
//
val bytes = mutableListOf<Byte>()
repeat(16) {
bytes += unsafe.getByte(encrypt + it)
}
println(bytes.toByteArray().toUHexString())
}

View File

@ -162,7 +162,7 @@ object TEA {
}
fun decrypt(cipherText: ByteArray, offset: Int, len: Int): ByteArray? {
require(!(len % 8 != 0 || len < 16)) { "must len % 8 == 0 && len >= 16" }
require(!(len % 8 != 0 || len < 16)) { "data must len % 8 == 0 && len >= 16" }
mIV = decode(cipherText, offset)
mIndexPos = (mIV[0] and 7).toInt()
var plen = len - mIndexPos - 10

View File

@ -15,7 +15,6 @@ import java.util.zip.CRC32
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import javax.imageio.ImageIO
import kotlin.jvm.JvmSynthetic
/**
@ -23,7 +22,7 @@ import kotlin.jvm.JvmSynthetic
* @author NaturalHG
*/
@JvmSynthetic
//@JvmSynthetic
fun ByteArray.toHexString(): String = toHexString(" ")
fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(separator) {
@ -38,11 +37,11 @@ fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(s
fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator)
@JvmSynthetic
//@JvmSynthetic
fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString()
@JvmSynthetic
//@JvmSynthetic
fun UByteArray.toUHexString(separator: String = " "): String {
return this.joinToString(separator) {
var ret = it.toString(16).toUpperCase()
@ -54,7 +53,7 @@ fun UByteArray.toUHexString(separator: String = " "): String {
}
@JvmSynthetic
//@JvmSynthetic
fun UByteArray.toUHexString(): String = this.toUHexString(" ")
@ -79,10 +78,8 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream())
open fun toUByteArray(): UByteArray = (out as ByteArrayOutputStream).toByteArray().toUByteArray()
}
@JvmSynthetic
fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().also(t).toByteArray()
@JvmSynthetic
fun <T> lazyDecode(byteArray: ByteArray, t: (DataInputStream) -> T): T = byteArray.dataInputStream().let(t)
fun DataInputStream.skip(n: Number) {
@ -95,7 +92,6 @@ fun getRandomByteArray(length: Int): ByteArray {
return bytes.toByteArray()
}
@JvmSynthetic
operator fun File.plus(child: String): File = File(this, child)
private const val GTK_BASE_VALUE: Int = 5381

View File

@ -15,7 +15,7 @@ import java.io.DataInputStream
* @author Him188moe
*/
object Main {
const val localIp = "192.168.3.10"
const val localIp = "192.168.3."
@JvmStatic
fun main(args: Array<String>) {
@ -41,7 +41,7 @@ object Main {
val caplen = 4096
val promiscCheck = true
jpcap = JpcapCaptor.openDevice(devices[1], caplen, promiscCheck, 50)
jpcap = JpcapCaptor.openDevice(devices[0], caplen, promiscCheck, 50)
/*----------第二步抓包-----------------*/
@ -55,10 +55,11 @@ object Main {
continue
}
if (localIp == pk.dst_ip.hostAddress) {//接受
if (localIp in pk.dst_ip.hostAddress) {//接受
dataReceived(pk.data)
} else {
try {
println("size = " + pk.data.size)
dataSent(pk.data)
} catch (e: Exception) {
e.printStackTrace()
@ -116,13 +117,21 @@ object Main {
println("Got sessionKey=" + sessionKey.toUHexString())
}
is ServerEventPacket.Raw.Encrypted -> {
val sessionKey = "8B 45 10 0F 10 00 66 0F 38 00 05 20 39 18 64 0F".hexToBytes()
println("! ServerEventPacket.Raw.Encrypted")
packetReceived(packet.decrypt(sessionKey))
println("! decrypt succeed")
}
else -> {
}
}
}
@Volatile
private var debugStarted = false
private var debugStarted = true
private const val qq: Int = 1994701021
private const val password: String = "xiaoqqq"
@ -141,14 +150,29 @@ object Main {
lazyDecode(data.cutTail(1)) {
it.skip(3)
val idHex = it.readNBytes(4).toUHexString()
println("qq=" + it.readUInt())
println(idHex)
println("发出包$idHex")
when (idHex.substring(0, 5)) {
"08 25" -> {
debugStarted = true
println("Detected touch, debug start!!")
}
"00 CD" -> {
println("好友消息发出: ")
val sessionKey = "70 BD 1E 12 20 C1 25 12 A0 F8 4F 0D C0 A0 97 0E".hexToBytes()
lazyDecode(data) {
//it.readShort()
//println(it.readUInt())
println(it.readNBytes(TIMProtocol.fixVer2.hexToBytes().size + 1 + 5 - 3 + 1).toUHexString())
it.readAllBytes().let {
println("解密")
println(it.size)
println(it.toUHexString())
println(it.decryptBy(sessionKey).toUHexString())
}
}
}
"08 36" -> {
println(data.toUHexString())
println("tim的 passwordSubmissionKey1 = " + it.readNBytes(TIMProtocol.passwordSubmissionTLV1.hexToBytes().size).toUHexString())
@ -200,9 +224,9 @@ object Main {
val shareKeyFromCS = "60 42 3B 51 C3 B1 F6 0F 67 E8 9C 00 F0 A7 BD A3"
fun main() {
val data = "76 AF AE 95 EB 89 BE B5 1C 83 D2 87 23 3B 5A 3B 6B 4C 78 AD F9 93 86 CA 13 D7 86 B5 0C D1 84 FB 2B ED 59 26 42 3B E0 6F 1A 91 A5 98 91 20 25 3F 6D C0 F6 FC 27 3D F8 34 EA 50 95 8C 2A BB 22 73 BD 76 60 2A 6B 68 51 07 4A 2F 37 6D 97 42 51 C5 14 47 96 3A A9 6B 8F 66 F8 D4 F4 52 22 13 D5 CC 9F B1 B4 06 BC 4B 35 B6 CF D8 CB 70 0F 0C E6 AA D9 12 E9 A2 C7 7F D8 24 7E 1B 2D 97 67 DA 34 0A FD 8E 44 D3 58 50 0D F0 0A 20 08 0A 46 28 68 0A 06 17 36 84 94 2C 97 2A 22 32 7B 01 67 3F E4 90 71 88 B2 F9 7B 7B AC 1A 00 CD 54 4A D7 AE 71 68 B3 FB E5 F3 94 9A C2 A1 C3 CA A5 4E AB 2C B0 78 AD EE 63 3F E6 24 6E AC 31 A5 00 F4 DB C7 4B 65 44 7B 92 87 30 7D 73 B3 21 81 C8 99 33 06 65 28 0C 98 56 EF 41 DC 64 79 55 69 AD B7 F4 A4 CF 4A 28 4B 3B E3 5A 2B C1 72 20 95 D9 8E 9F 1E A5 DE 9A DD 39 0B BE 76 A8 BE 95 9D 7C C2 C5 A8 3A D3 76 B6 D4 ED 15 34 5D 3C 8E 96 C6 93 64 78 A1 89 78 DA F8 17 E5 96 75 5F B6 97 FC 41 18 A4 54 67 BA 3B ED 97 27 B7 E3 90 81 1E DC 8D 17 25 46 2D 08 0D BB 95 D0 CB C8 9B 78 36 2D 70 E3 C6 4C 21 E9 C0 02 69 3B C5 F7 91 6B 62 D8 E4 10 F0 01 5B 7F 1A 3E 9F 1A D4 D3 A9 2B 4A C2 BD 6D 8B B0 0A AE A4 E9 72 71 F4 39 28 CE 18 42 ED FD BB 61 08 B1 95 93 8E F6 29 D7 B6 CB 15 2A AA AF A7 81 AD DF 3B D5 3F 47 29 AB 61 0C 86 48 82 93 AE 8C 2C 32 CC 83 83 68 08 C6 9D 10 81 82 BA 92 24 0E ED 71 B1 83 E1 08 D0 01 BB DF E2 26 D0 20 DF 8C 95 E1 A6 42 C2 A2 E7 85 00 E6 AA 54 A8 0C 5D BB 8D 46 37 AD 47 88 38 B9 D7 3B 48 13 13 81 3B A5 05 4D 32 24 A4 CE 08 73 6D 89 FD 6D CC F5 AB 8B 6A 39 4B 9D 30 33 73 F1 01 7F E4 43 03 72 44 67 3A 24 28 40 51 2B EB 48 EB F9 05 A9 3C 20 EB 4D B7 45 56 D3 4E BD A0 B5 40 65 D1 16 57 73 A4 81 B1 A6 8C 3F 68 28 AA EB 83".hexToBytes()
println(TEA.decrypt(data, TIMProtocol.shareKey.hexToBytes()))
val data = "2C 3C 4A 0D 14 D3 C4 8D FA 99 58 02 87 04 47 66 F9 F9 4F DF B8 01 1E C6 2A 52 3E 83 B0 96 4C 1C 3C D0 1C A0 D6 58 3C D0 2B 6B 33 1E 37 0A 6E C3 49 CE 57 B0 70 41 88 C1 3B A3 61 72 5E 3C 65 EC B1 2E EC 25 0E 1B 66 7A C4 28 F7 1D 53 15 56 99 BB 18 90 ED E6 13 97 19 FE 42 DB D1 16 E3 21 77 6E 90 B8 E2 5A 6D C3 AE FF 5C 63 98 AE 42 B0 AB 96 0B 08 D8 DA E0 D3 BD 17 E4 7B 76 1C 16 17 DC".hexToBytes()
println(TEA.decrypt(data, "9A 45 7B D4 54 EF 7C E7 86 F5 20 EF 27 BE CF C1".hexToBytes()).toUHexString())
//succeed
}

View File

@ -20,9 +20,9 @@ import kotlin.system.exitProcess
* @author Him188moe
*/
suspend fun main() {
val bot = Bot(BotAccount(
qqNumber = 1683921395,
password = "bb22222"
val bot = Bot(BotAccount(//填写你的账号
qqNumber = 1994701021,
password = "xiaoqqq"
), Console())
bot.login().let {

View File

@ -5,11 +5,4 @@ include(':mirai-api')
include(':mirai-demos:mirai-demo-1')
include(':mirai-demos')
include(':mirai-debug')
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1')
def kotlinVersion = "1.3.50"
def coroutinesVersion = "1.3.0-M2"
def kotlinStandardLib = "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
def kotlinCoroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1')