diff --git a/README.md b/README.md index 6398add83..5a4b22a2a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 项目处于开发阶段,学生无法每日大量更新。 项目还有很多未完善的地方, 欢迎任何的代码贡献, 或是 issue. 部分协议来自网络上开源项目 -一切开发旨在学习,请勿用于非法用途 +**一切开发旨在学习,请勿用于非法用途** ## 抢先体验 核心框架结构已经开发完毕,一些核心功能也测试完成。 diff --git a/mirai-core/build.gradle b/mirai-core/build.gradle index 850918dde..5233e1db8 100644 --- a/mirai-core/build.gradle +++ b/mirai-core/build.gradle @@ -17,6 +17,8 @@ kotlin { } } jvmMain { + apply plugin: 'java' + dependencies { implementation rootProject.ext.kotlinJvm implementation rootProject.ext.reflect @@ -28,6 +30,7 @@ kotlin { implementation 'org.jsoup:jsoup:1.12.1' implementation 'org.ini4j:ini4j:0.5.2' + implementation project(":mirai-protocol-timpc") } } jvmTest { diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.java b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.java deleted file mode 100644 index c84411b2a..000000000 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.java +++ /dev/null @@ -1,174 +0,0 @@ -package net.mamoe.mirai.message; - -/** - * @author LamGC - * @author Him188moe - */ -public enum FaceID { - unknown(0xff), - - Face_jingya(0), - Face_piezui(1), - Face_se(2), - Face_fadai(3), - Face_deyi(4), - Face_liulei(5), - Face_haixiu(6), - Face_bizui(7), - Face_shui(8), - Face_daku(9), - Face_ganga(10), - Face_fanu(11), - Face_tiaopi(12), - Face_ciya(13), - Face_weixiao(14), - Face_nanguo(15), - Face_ku(16), - Face_zhuakuang(18), - Face_tu(19), - Face_touxiao(20), - Face_keai(21), - Face_baiyan(22), - Face_aoman(23), - Face_ji_e(24), - Face_kun(25), - Face_jingkong(26), - Face_liuhan(27), - Face_hanxiao(28), - Face_dabing(29), - Face_fendou(30), - Face_zhouma(31), - Face_yiwen(32), - Face_yun(34), - Face_zhemo(35), - Face_shuai(36), - Face_kulou(37), - Face_qiaoda(38), - Face_zaijian(39), - Face_fadou(41), - Face_aiqing(42), - Face_tiaotiao(43), - Face_zhutou(46), - Face_yongbao(49), - Face_dan_gao(53), - Face_shandian(54), - Face_zhadan(55), - Face_dao(56), - Face_zuqiu(57), - Face_bianbian(59), - Face_kafei(60), - Face_fan(61), - Face_meigui(63), - Face_diaoxie(64), - Face_aixin(66), - Face_xinsui(67), - Face_liwu(69), - Face_taiyang(74), - Face_yueliang(75), - Face_qiang(76), - Face_ruo(77), - Face_woshou(78), - Face_shengli(79), - Face_feiwen(85), - Face_naohuo(86), - Face_xigua(89), - Face_lenghan(96), - Face_cahan(97), - Face_koubi(98), - Face_guzhang(99), - Face_qiudale(100), - Face_huaixiao(101), - Face_zuohengheng(102), - Face_youhengheng(103), - Face_haqian(104), - Face_bishi(105), - Face_weiqu(106), - Face_kuaikule(107), - Face_yinxian(108), - Face_qinqin(109), - Face_xia(110), - Face_kelian(111), - Face_caidao(112), - Face_pijiu(113), - Face_lanqiu(114), - Face_pingpang(115), - Face_shiai(116), - Face_piaochong(117), - Face_baoquan(118), - Face_gouyin(119), - Face_quantou(120), - Face_chajin(121), - Face_aini(122), - Face_bu(123), - Face_hao(124), - Face_zhuanquan(125), - Face_ketou(126), - Face_huitou(127), - Face_tiaosheng(128), - Face_huishou(129), - Face_jidong(130), - Face_jiewu(131), - Face_xianwen(132), - Face_zuotaiji(133), - Face_youtaiji(134), - Face_shuangxi(136), - Face_bianpao(137), - Face_denglong(138), - Face_facai(139), - Face_K_ge(140), - Face_gouwu(141), - Face_youjian(142), - Face_shuai_qi(143), - Face_hecai(144), - Face_qidao(145), - Face_baojin(146), - Face_bangbangtang(147), - Face_he_nai(148), - Face_xiamian(149), - Face_xiangjiao(150), - Face_feiji(151), - Face_kaiche(152), - Face_gaotiezuochetou(153), - Face_chexiang(154), - Face_gaotieyouchetou(155), - Face_duoyun(156), - Face_xiayu(157), - Face_chaopiao(158), - Face_xiongmao(159), - Face_dengpao(160), - Face_fengche(161), - Face_naozhong(162), - Face_dasan(163), - Face_caiqiu(164), - Face_zuanjie(165), - Face_shafa(166), - Face_zhijin(167), - Face_yao(168), - Face_shouqiang(169), - Face_qingwa(170), - - // TODO: 2019/9/1 添加更多表情 - - ; - - private final int id; - - FaceID(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public static FaceID ofId(int id) { - for (FaceID value : FaceID.values()) { - if (value.id == id) { - return value; - } - } - return FaceID.unknown; - } - - -} diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt new file mode 100644 index 000000000..7cf822298 --- /dev/null +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt @@ -0,0 +1,167 @@ +package net.mamoe.mirai.message + +/** + * @author LamGC + * @author Him188moe + */ +@Suppress("EnumEntryName", "unused", "SpellCheckingInspection") +enum class FaceID constructor(val id: Int) { + unknown(0xff), + // TODO: 2019/9/1 添加更多表情 + jingya(0), + piezui(1), + se(2), + fadai(3), + deyi(4), + liulei(5), + haixiu(6), + bizui(7), + shui(8), + daku(9), + ganga(10), + fanu(11), + tiaopi(12), + ciya(13), + weixiao(14), + nanguo(15), + ku(16), + zhuakuang(18), + tu(19), + touxiao(20), + keai(21), + baiyan(22), + aoman(23), + ji_e(24), + kun(25), + jingkong(26), + liuhan(27), + hanxiao(28), + dabing(29), + fendou(30), + zhouma(31), + yiwen(32), + yun(34), + zhemo(35), + shuai(36), + kulou(37), + qiaoda(38), + zaijian(39), + fadou(41), + aiqing(42), + tiaotiao(43), + zhutou(46), + yongbao(49), + dan_gao(53), + shandian(54), + zhadan(55), + dao(56), + zuqiu(57), + bianbian(59), + kafei(60), + fan(61), + meigui(63), + diaoxie(64), + aixin(66), + xinsui(67), + liwu(69), + taiyang(74), + yueliang(75), + qiang(76), + ruo(77), + woshou(78), + shengli(79), + feiwen(85), + naohuo(86), + xigua(89), + lenghan(96), + cahan(97), + koubi(98), + guzhang(99), + qiudale(100), + huaixiao(101), + zuohengheng(102), + youhengheng(103), + haqian(104), + bishi(105), + weiqu(106), + kuaikule(107), + yinxian(108), + qinqin(109), + xia(110), + kelian(111), + caidao(112), + pijiu(113), + lanqiu(114), + pingpang(115), + shiai(116), + piaochong(117), + baoquan(118), + gouyin(119), + quantou(120), + chajin(121), + aini(122), + bu(123), + hao(124), + zhuanquan(125), + ketou(126), + huitou(127), + tiaosheng(128), + huishou(129), + jidong(130), + jiewu(131), + xianwen(132), + zuotaiji(133), + youtaiji(134), + shuangxi(136), + bianpao(137), + denglong(138), + facai(139), + K_ge(140), + gouwu(141), + youjian(142), + shuai_qi(143), + hecai(144), + qidao(145), + baojin(146), + bangbangtang(147), + he_nai(148), + xiamian(149), + xiangjiao(150), + feiji(151), + kaiche(152), + gaotiezuochetou(153), + chexiang(154), + gaotieyouchetou(155), + duoyun(156), + xiayu(157), + chaopiao(158), + xiongmao(159), + dengpao(160), + fengche(161), + naozhong(162), + dasan(163), + caiqiu(164), + zuanjie(165), + shafa(166), + zhijin(167), + yao(168), + shouqiang(169), + qingwa(170); + + override fun toString(): String { + return "$name($id)" + } + + companion object { + + fun ofId(id: Int): FaceID { + for (value in values()) { + if (value.id == id) { + return value + } + } + return unknown + } + } + +} diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Face.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Face.kt index 2d251de68..0b5eecf33 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Face.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Face.kt @@ -20,7 +20,11 @@ class Face(val id: FaceID) : Message() { override val type: MessageKey = Key override fun toStringImpl(): String { - return String.format("[face%d]", id.id) + return "[face${id.id}]" + } + + override fun toObjectString(): String { + return "Face[$id]" } override fun toByteArray(): ByteArray = dataEncode { section -> @@ -48,7 +52,7 @@ class Face(val id: FaceID) : Message() { override operator fun contains(sub: String): Boolean = false - internal object PacketHelper { + object PacketHelper { fun ofByteArray(data: ByteArray): Face = dataDecode(data) { //00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0 //00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Image.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Image.kt index 79edadf32..459349e7b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Image.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/Image.kt @@ -22,7 +22,11 @@ open class Image(val imageId: String) : Message() { override val type: MessageKey = Key override fun toStringImpl(): String { - return imageId + return "[$imageId]" + } + + override fun toObjectString(): String { + return "Image[$imageId]" } override fun toByteArray(): ByteArray = dataEncode { section -> @@ -55,7 +59,7 @@ open class Image(val imageId: String) : Message() { override operator fun contains(sub: String): Boolean = false //No string can be contained in a image - internal object PacketHelper { + object PacketHelper { @JvmStatic fun ofByteArray0x06(data: ByteArray): Image = dataDecode(data) { it.skip(1) diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/MessageChain.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/MessageChain.kt index 2943513de..8e7be7c4b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/MessageChain.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/MessageChain.kt @@ -2,7 +2,12 @@ package net.mamoe.mirai.message.defaults import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.MessageKey +import net.mamoe.mirai.network.protocol.tim.packet.readLVByteArray +import net.mamoe.mirai.network.protocol.tim.packet.readNBytes +import net.mamoe.mirai.utils.dataDecode import net.mamoe.mirai.utils.dataEncode +import net.mamoe.mirai.utils.toUHexString +import java.io.DataInputStream import java.util.* import java.util.stream.Collectors import java.util.stream.Stream @@ -95,4 +100,69 @@ class MessageChain : Message { operator fun component1(): Message = this.list[0] operator fun component2(): Message = this.list[1] operator fun component3(): Message = this.list[2] + + object PacketHelper { + @JvmStatic + fun ofByteArray(byteArray: ByteArray): MessageChain = dataDecode(byteArray) { + it.readMessageChain() + } + } } + +fun DataInputStream.readMessage(): Message? { + val messageType = this.readByte().toInt() + val sectionLength = this.readShort().toLong()//sectionLength: short + val sectionData = this.readNBytes(sectionLength) + return when (messageType) { + 0x01 -> PlainText.PacketHelper.ofByteArray(sectionData) + 0x02 -> Face.PacketHelper.ofByteArray(sectionData) + 0x03 -> Image.PacketHelper.ofByteArray0x03(sectionData) + 0x06 -> Image.PacketHelper.ofByteArray0x06(sectionData) + + + 0x19 -> {//长文本 + val value = readLVByteArray() + //todo 未知压缩算法 + PlainText(String(value)) + + // PlainText(String(GZip.uncompress( value))) + } + + + 0x14 -> {//长文本 + val value = readLVByteArray() + println(value.size) + println(value.toUHexString()) + //todo 未知压缩算法 + this.skip(7)//几个TLV + return PlainText(String(value)) + } + + 0x0E -> { + //null + null + } + + else -> { + println("未知的messageType=0x${messageType.toByte().toUHexString()}") + println("后文=${this.readAllBytes().toUHexString()}") + null + } + } + +} + +fun DataInputStream.readMessageChain(): MessageChain { + val chain = MessageChain() + var got: Message? = null + do { + if (got != null) { + chain.concat(got) + } + if (this.available() == 0) { + return chain + } + got = this.readMessage() + } while (got != null) + return chain +} \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/PlainText.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/PlainText.kt index c5bb3e759..86748f93e 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/PlainText.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/defaults/PlainText.kt @@ -38,7 +38,7 @@ class PlainText(private val text: String) : Message() { override operator fun contains(sub: String): Boolean = this.toString().contains(sub) - internal object PacketHelper { + object PacketHelper { @JvmStatic fun ofByteArray(data: ByteArray): PlainText = dataDecode(data) { it.skip(1) diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/TIMProtocol.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/TIMProtocol.kt index 6d02ff12f..84c23956c 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/TIMProtocol.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/TIMProtocol.kt @@ -39,6 +39,7 @@ object TIMProtocol { */ 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 + // 02 00 00 00 01 2E 01 00 00 69 35 /** * 0825data1 */ @@ -105,6 +106,7 @@ object TIMProtocol { * length=15 */ const val messageConst1 = "00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91" + // TIM最新 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 private val hexToByteArrayCacheMap: MutableMap = mutableMapOf() diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ServerEvent.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ServerEvent.kt index fd3a7dcfa..16f9dd36c 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ServerEvent.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ServerEvent.kt @@ -2,11 +2,8 @@ package net.mamoe.mirai.network.protocol.tim.packet -import net.mamoe.mirai.message.Message -import net.mamoe.mirai.message.defaults.Face -import net.mamoe.mirai.message.defaults.Image import net.mamoe.mirai.message.defaults.MessageChain -import net.mamoe.mirai.message.defaults.PlainText +import net.mamoe.mirai.message.defaults.readMessageChain import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.utils.dataDecode import net.mamoe.mirai.utils.hexToBytes @@ -49,7 +46,7 @@ abstract class ServerEventPacket(input: DataInputStream, val packetId: ByteArray @PacketId("00 17") class Encrypted(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): Raw = Raw(decryptBy(sessionKey), packetId).setId(this.idHex) + fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey), packetId).setId(this.idHex) } } @@ -135,7 +132,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, this.input.goto(108) this.input.readLVByteArray() input.skip(2)//2个0x00 - message = input.readSections() + message = input.readMessageChain() val map = input.readTLVMap(true) if (map.containsKey(18)) { @@ -262,7 +259,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray input.goto(93 + l1) input.readLVByteArray()//font input.skip(2)//2个0x00 - message = input.readSections() + message = input.readMessageChain() val map: Map = input.readTLVMap(true).withDefault { byteArrayOf() } println(map.getValue(18)) @@ -278,64 +275,6 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray } } -private fun DataInputStream.readSection(): Message? { - val messageType = this.readByte().toInt() - val sectionLength = this.readShort().toLong()//sectionLength: short - val sectionData = this.readNBytes(sectionLength) - return when (messageType) { - 0x01 -> PlainText.PacketHelper.ofByteArray(sectionData) - 0x02 -> Face.PacketHelper.ofByteArray(sectionData) - 0x03 -> Image.PacketHelper.ofByteArray0x03(sectionData) - 0x06 -> Image.PacketHelper.ofByteArray0x06(sectionData) - - - 0x19 -> {//长文本 - val value = readLVByteArray() - //todo 未知压缩算法 - PlainText(String(value)) - - // PlainText(String(GZip.uncompress( value))) - } - - - 0x14 -> {//长文本 - val value = readLVByteArray() - println(value.size) - println(value.toUHexString()) - //todo 未知压缩算法 - this.skip(7)//几个TLV - return PlainText(String(value)) - } - - 0x0E -> { - //null - null - } - - else -> { - println("未知的messageType=0x${messageType.toByte().toUHexString()}") - println("后文=${this.readAllBytes().toUHexString()}") - null - } - } - -} - -private fun DataInputStream.readSections(): MessageChain { - val chain = MessageChain() - var got: Message? = null - do { - if (got != null) { - chain.concat(got) - } - if (this.available() == 0) { - return chain - } - got = this.readSection() - } while (got != null) - return chain -} - /* 牛逼 (10404 diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt index d54e1330b..fbfcc095e 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt @@ -34,11 +34,22 @@ class ClientSendFriendMessagePacket( writeRandom(2) writeTime() 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 + "00 00 00 00") + + //消息过多要分包发送 + //如果只有一个 + writeByte(0x01) + writeByte(0)//第几个包 + writeByte(0) + //如果大于一个, + //writeByte(0x02)//数量 + //writeByte(0)//第几个包 + //writeByte(0x91)//why? + + writeHex("00 01 4D 53 47 00 00 00 00 00") writeTime() writeRandom(4) - writeHex("00 00 00 00 09 00 86") + writeHex("00 00 00 00 09 00 86")//TIM最新 0C 00 86 writeHex(TIMProtocol.messageConst1)//... 85 E9 BB 91 writeZero(2) @@ -56,9 +67,5 @@ class ClientSendFriendMessagePacket( } } - -fun main() { - -} @PacketId("00 CD") class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/image/UploadGroupImage.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/image/UploadGroupImage.kt index c92a4ffd7..7e8d19c5f 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/image/UploadGroupImage.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/image/UploadGroupImage.kt @@ -37,18 +37,19 @@ class ClientTryGetImageIDPacket( writeZero(2) - writeHex("5E") + writeHex("5B")//原5E writeHex("08") writeHex("01 12 03 98 01 01 10 01") writeHex("1A") - writeHex("5A") + writeHex("57")//原5A writeHex("08") - writeUVarInt(groupNumberOrQQNumber) + writeUVarInt(groupNumberOrQQNumber)//FB D2 D8 94 + writeByte(0x02) writeHex("10") - writeUVarInt(botNumber) + writeUVarInt(botNumber)//A2 FF 8C F0 writeHex("18 00") @@ -57,10 +58,13 @@ class ClientTryGetImageIDPacket( write(md5(byteArray)) writeHex("28") - writeUVarInt(byteArray.size.toUInt()) + writeUVarInt(byteArray.size.toUInt())//E2 0D writeHex("32") writeHex("1A") + //28 00 5A 00 53 00 41 00 58 00 40 00 57 00 4B 00 52 00 4A 00 5A 00 31 00 7E 00 38 01 48 01 50 38 58 34 60 04 6A 05 32 36 39 33 33 70 00 78 03 80 01 00 + + writeHex("37 00 4D 00 32 00 25 00 4C 00 31 00 56 00 32 00 7B 00 39 00 30 00 29 00 52 00") writeHex("38 01") diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt index ddfc61010..4a45533c9 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt @@ -84,6 +84,11 @@ fun dataDecode(byteArray: ByteArray, t: (DataInputStream) -> R): R = byteArr fun ByteArray.decode(t: (DataInputStream) -> R): R = this.dataInputStream().let(t) +fun ByteArray.decryptBy(key: ByteArray): ByteArray = TEA.decrypt(this, key) + +fun ByteArray.decryptBy(key: String): ByteArray = TEA.decrypt(this, key) + + fun DataInputStream.skip(n: Number) { this.skip(n.toLong()) } diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Varint.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Varint.kt index 289749a18..ddf9c6818 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Varint.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Varint.kt @@ -69,7 +69,6 @@ fun DataOutputStream.writeVarInt(signedInt: Int) { this.writeUVarInt(encodeZigZag32(signedInt)) } - @Throws(IOException::class) fun DataOutputStream.writeUVarInt(uint: UInt) { return writeUVarInt(uint.toLong()) diff --git a/mirai-debug/build.gradle b/mirai-debug/build.gradle index c1ae647ab..98933ff78 100644 --- a/mirai-debug/build.gradle +++ b/mirai-debug/build.gradle @@ -3,11 +3,12 @@ apply plugin: "java" dependencies { implementation project(':mirai-core') - compile 'com.google.protobuf:protobuf-java:3.5.0' compile files('./lib/jpcap.jar') - compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2' - compile 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50' + compile rootProject.ext.coroutineCommon + compile rootProject.ext.kotlinJvm + + compile group: 'com.google.protobuf', name: 'protobuf-java', version: rootProject.ext.protobuf_version } tasks.withType(JavaCompile) { diff --git a/mirai-debug/src/main/java/HexComparator.java b/mirai-debug/src/main/java/HexComparator.java deleted file mode 100644 index c669ca932..000000000 --- a/mirai-debug/src/main/java/HexComparator.java +++ /dev/null @@ -1,373 +0,0 @@ -import kotlin.ranges.IntRange; - -import net.mamoe.mirai.network.protocol.tim.TIMProtocol; -import net.mamoe.mirai.network.protocol.tim.packet.ClientPacketKt; -import net.mamoe.mirai.utils.UtilsKt; - -import java.awt.*; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Scanner; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; - -/** - * This could be used to check packet encoding.. - * but better to run under UNIX - * - * @author NaturalHG - */ -public class HexComparator { - - /** - * a string result - */ - - private static final String RED = "\033[31m"; - - private static final String GREEN = "\033[33m"; - - private static final String UNKNOWN = "\033[30m"; - - private static final String BLUE = "\033[34m"; - - public static final List consts = new LinkedList<>() {{ - add(new HexReader("90 5E 39 DF 00 02 76 E4 B8 DD 00")); - }}; - - private static class ConstMatcher { - private static final List CONST_FIELDS = new LinkedList<>() {{ - List.of(TIMProtocol.class).forEach(aClass -> Arrays.stream(aClass.getDeclaredFields()).peek(this::add).forEach(Field::trySetAccessible)); - List.of(TestConsts.class).forEach(aClass -> Arrays.stream(aClass.getDeclaredFields()).peek(this::add).forEach(Field::trySetAccessible)); - }}; - - @SuppressWarnings({"unused", "NonAsciiCharacters"}) - private static class TestConsts { - private static final String NIU_BI = UtilsKt.toUHexString("牛逼".getBytes(), " "); - private static final String _1994701021 = ClientPacketKt.toUHexString(1994701021, " "); - private static final String _1040400290 = ClientPacketKt.toUHexString(1040400290, " "); - private static final String _580266363 = ClientPacketKt.toUHexString(580266363, " "); - - private static final String _1040400290_ = "3E 03 3F A2"; - private static final String _1994701021_ = "76 E4 B8 DD"; - private static final String _jiahua_ = "B1 89 BE 09"; - private static final String _Him188moe_ = UtilsKt.toUHexString("Him188moe".getBytes(), " "); - private static final String 发图片 = UtilsKt.toUHexString("发图片".getBytes(), " "); - private static final String 群 = UtilsKt.toUHexString("发图片".getBytes(), " "); - - private static final String SINGLE_PLAIN_MESSAGE_HEAD = "00 00 01 00 09 01"; - - private static final String MESSAGE_TAIL_10404 = "0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00".replace(" ", " "); - //private static final String MESSAGE_TAIL2_10404 ="".replace(" ", " "); - - } - - private final List matches = new LinkedList<>(); - - private ConstMatcher(String hex) { - CONST_FIELDS.forEach(field -> { - for (IntRange match : match(hex, field)) { - matches.add(new Match(match, field.getName())); - } - }); - } - - private String getMatchedConstName(int hexNumber) { - for (Match match : this.matches) { - if (match.range.contains(hexNumber)) { - return match.constName; - } - } - return null; - } - - private static List match(String hex, Field field) { - final String constValue; - try { - constValue = ((String) field.get(null)).trim(); - if (constValue.length() / 3 <= 3) {//Minimum numbers of const hex bytes - return new LinkedList<>(); - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (ClassCastException ignored) { - return new LinkedList<>(); - } - return new LinkedList<>() {{ - int index = -1; - while ((index = hex.indexOf(constValue, index + 1)) != -1) { - add(new IntRange(index / 3, (index + constValue.length()) / 3)); - } - }}; - } - - private static class Match { - private IntRange range; - private String constName; - - Match(IntRange range,String constName){ - this.range = range; - this.constName = constName; - } - } - } - - private static void buildConstNameChain(int length, ConstMatcher constMatcher, StringBuilder constNameBuilder) { - //System.out.println(constMatcher.matches); - for (int i = 0; i < length; i++) { - constNameBuilder.append(" "); - String match = constMatcher.getMatchedConstName(i / 4); - if (match != null) { - int appendedNameLength = match.length(); - constNameBuilder.append(match); - while (match.equals(constMatcher.getMatchedConstName(i++ / 4))) { - if (appendedNameLength-- < 0) { - constNameBuilder.append(" "); - } - } - - constNameBuilder.append(" ".repeat(match.length() % 4)); - } - } - } - - private static String compare(String hex1s, String hex2s) { - StringBuilder builder = new StringBuilder(); - - String[] hex1 = hex1s.trim().replace("\n", "").split(" "); - String[] hex2 = hex2s.trim().replace("\n", "").split(" "); - ConstMatcher constMatcher1 = new ConstMatcher(hex1s); - ConstMatcher constMatcher2 = new ConstMatcher(hex2s); - - if (hex1.length == hex2.length) { - builder.append(GREEN).append("长度一致:").append(hex1.length); - } else { - builder.append(RED).append("长度不一致").append(hex1.length).append("/").append(hex2.length); - } - - - StringBuilder numberLine = new StringBuilder(); - StringBuilder hex1ConstName = new StringBuilder(); - StringBuilder hex1b = new StringBuilder(); - StringBuilder hex2b = new StringBuilder(); - StringBuilder hex2ConstName = new StringBuilder(); - int dif = 0; - - int length = Math.max(hex1.length, hex2.length) * 4; - buildConstNameChain(length, constMatcher1, hex1ConstName); - buildConstNameChain(length, constMatcher2, hex2ConstName); - - - for (int i = 0; i < Math.max(hex1.length, hex2.length); ++i) { - String h1 = null; - String h2 = null; - boolean isDif = false; - if (hex1.length <= i) { - h1 = RED + "__"; - isDif = true; - } else { - String matchedConstName = constMatcher1.getMatchedConstName(i); - if (matchedConstName != null) { - h1 = BLUE + hex1[i]; - } - } - if (hex2.length <= i) { - h2 = RED + "__"; - isDif = true; - } else { - String matchedConstName = constMatcher2.getMatchedConstName(i); - if (matchedConstName != null) { - h2 = BLUE + hex2[i]; - } - } - - if (h1 == null && h2 == null) { - h1 = hex1[i]; - h2 = hex2[i]; - if (h1.equals(h2)) { - h1 = GREEN + h1; - h2 = GREEN + h2; - } else { - h1 = RED + h1; - h2 = RED + h2; - isDif = true; - } - } else { - if (h1 == null) { - h1 = RED + hex1[i]; - } - if (h2 == null) { - h2 = RED + hex2[i]; - } - } - - numberLine.append(UNKNOWN).append(getFixedNumber(i)).append(" "); - hex1b.append(" ").append(h1).append(" "); - hex2b.append(" ").append(h2).append(" "); - if (isDif) { - ++dif; - } - - //doConstReplacement(hex1b); - //doConstReplacement(hex2b); - } - - return (builder.append(" ").append(dif).append(" 个不同").append("\n") - .append(numberLine).append("\n") - .append(hex1ConstName).append("\n") - .append(hex1b).append("\n") - .append(hex2b).append("\n") - .append(hex2ConstName).append("\n") - ) - .toString(); - - - } - - - private static void doConstReplacement(StringBuilder builder) { - String mirror = builder.toString(); - HexReader hexs = new HexReader(mirror); - for (AtomicInteger i = new AtomicInteger(0); i.get() < builder.length(); i.addAndGet(1)) { - hexs.setTo(i.get()); - consts.forEach(a -> { - hexs.setTo(i.get()); - List posToPlaceColor = new LinkedList<>(); - AtomicBoolean is = new AtomicBoolean(false); - - a.readFully((c, d) -> { - if (c.equals(hexs.readHex())) { - posToPlaceColor.add(d); - } else { - is.set(false); - } - }); - - if (is.get()) { - AtomicInteger adder = new AtomicInteger(); - posToPlaceColor.forEach(e -> { - builder.insert(e + adder.getAndAdd(BLUE.length()), BLUE); - }); - } - }); - } - } - - private static String getFixedNumber(int number) { - if (number < 10) { - return "00" + number; - } - if (number < 100) { - return "0" + number; - } - return String.valueOf(number); - } - - private static String getClipboardString() { - Transferable trans = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); - if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) { - try { - return (String) trans.getTransferData(DataFlavor.stringFlavor); - } catch (Exception e) { - e.printStackTrace(); - } - } - - return null; - } - - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - while (true) { - System.out.println("Hex1: "); - var hex1 = scanner.nextLine(); - System.out.println("Hex2: "); - var hex2 = scanner.nextLine(); - System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - System.out.println(HexComparator.compare(hex1, hex2)); - System.out.println(); - } -/* - System.out.println(HexComparator.compare( - //mirai - - "2A 22 96 29 7B 00 40 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 EC 21 40 06 18 89 54 BC Protocol.messageConst1 00 00 01 00 0A 01 00 07 E7 89 9B E9 80 BC 21\n" - , - //e - "2A 22 96 29 7B 00 3F 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 5D 6B 8E 1A FE 39 0B FC Protocol.messageConst1 00 00 01 00 0A 01 00 07 6D 65 73 73 61 67 65" - )); - - - /* - System.out.println(HexComparator.compare( - //e - "90 5E 39 DF 00 02 76 E4 B8 DD 00 00 04 53 00 00 00 01 00 00 15 85 00 00 01 55 35 05 8E C9 BA 16 D0 01 63 5B 59 4B 59 52 31 01 B9 00 00 00 00 00 00 00 00 00 00 00 00 00 7B 7B 7B 7B 00 00 00 00 00 00 00 00 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B AA BB CC DD EE FF AA BB CC", - //mirai - "6F 0B DF 92 00 02 76 E4 B8 DD 00 00 04 53 00 00 00 01 00 00 15 85 00 00 01 55 35 05 8E C9 BA 16 D0 01 63 5B 59 4B 59 52 31 01 B9 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E9 E9 E9 00 00 00 00 00 00 00 00 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B AA BB CC DD EE FF AA BB CC\n\n\n" - ));*/ - } -} - -class HexReader { - private String s; - private int pos = 0; - private int lastHaxPos = 0; - - - public HexReader(String s) { - this.s = s; - } - - public String readHex() { - boolean isStr = false; - String next = ""; - for (; pos < s.length() - 2; ++pos) { - - char s1 = ' '; - if (pos != 0) { - s1 = this.s.charAt(0); - } - char s2 = this.s.charAt(pos + 1); - char s3 = this.s.charAt(pos + 2); - char s4 = ' '; - if (this.s.length() != (this.pos + 3)) { - s4 = this.s.charAt(pos + 3); - } - if ( - Character.isSpaceChar(s1) && Character.isSpaceChar(s4) - && - (Character.isDigit(s2) || Character.isAlphabetic(s2)) - && - (Character.isDigit(s3) || Character.isAlphabetic(s3)) - ) { - this.pos += 2; - this.lastHaxPos = this.pos + 1; - return String.valueOf(s2) + s3; - } - } - return ""; - } - - public void readFully(BiConsumer processor) { - this.reset(); - String nextHax = this.readHex(); - while (!nextHax.equals(" ")) { - processor.accept(nextHax, this.lastHaxPos); - nextHax = this.readHex(); - } - } - - public void setTo(int pos) { - this.pos = pos; - } - - public void reset() { - this.pos = 0; - } -} - diff --git a/mirai-debug/src/main/java/HexComparator.kt b/mirai-debug/src/main/java/HexComparator.kt new file mode 100644 index 000000000..0a373c319 --- /dev/null +++ b/mirai-debug/src/main/java/HexComparator.kt @@ -0,0 +1,291 @@ +@file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant") + +import net.mamoe.mirai.network.protocol.tim.TIMProtocol +import net.mamoe.mirai.network.protocol.tim.packet.toUHexString +import net.mamoe.mirai.utils.toUHexString +import java.awt.Toolkit +import java.awt.datatransfer.DataFlavor +import java.lang.reflect.Field +import java.util.* +import kotlin.math.max + +/** + * Hex 比较器, 并着色已知常量 + * + * This could be used to check packet encoding.. + * but better to run under UNIX + * + * @author NaturalHG + * @author Him188moe + */ +object HexComparator { + + private val RED = "\u001b[31m" + + private val GREEN = "\u001b[33m" + + private val UNKNOWN = "\u001b[30m" + + private val BLUE = "\u001b[34m" + + + private val clipboardString: String? + get() { + val trans = Toolkit.getDefaultToolkit().systemClipboard.getContents(null) + if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) { + try { + return trans.getTransferData(DataFlavor.stringFlavor) as String + } catch (e: Exception) { + e.printStackTrace() + } + + } + + return null + } + + class ConstMatcher constructor(hex: String) { + + private val matches = LinkedList() + + object TestConsts { + val NIU_BI = "牛逼".toByteArray().toUHexString() + val _1994701021 = 1994701021.toUHexString(" ") + val _1040400290 = 1040400290.toUHexString(" ") + val _580266363 = 580266363.toUHexString(" ") + + val _1040400290_ = "3E 03 3F A2" + val _1994701021_ = "76 E4 B8 DD" + val _jiahua_ = "B1 89 BE 09" + val _Him188moe_ = "Him188moe".toByteArray().toUHexString() + val 发图片 = "发图片".toByteArray().toUHexString() + val 群 = "群".toByteArray().toUHexString() + + val SINGLE_PLAIN_MESSAGE_HEAD = "00 00 01 00 09 01" + + val MESSAGE_TAIL_10404 = "0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00" + .replace(" ", " ") + } + + @Suppress("SpellCheckingInspection") + object PacketIds { + val heartbeat = "00 58" + val friendmsg = "00 CD" + } + + init { + CONST_FIELDS.forEach { field -> + for (match in match(hex, field)) { + matches.add(Match(match, field.name)) + } + } + } + + fun getMatchedConstName(hexNumber: Int): String? { + for (match in this.matches) { + if (match.range.contains(hexNumber)) { + return match.constName + } + } + return null + } + + private class Match internal constructor(val range: IntRange, val constName: String) + + companion object { + private val CONST_FIELDS: List = listOf( + TestConsts::class.java, + TIMProtocol::class.java, + PacketIds::class.java + ).map { it.declaredFields }.flatMap { fields -> + fields.map { field -> + field.trySetAccessible() + field + } + } + } + + + private fun match(hex: String, field: Field): List { + val constValue: String + try { + constValue = (field.get(null) as String).trim { it <= ' ' } + if (constValue.length / 3 <= 3) {//Minimum numbers of const hex bytes + return LinkedList() + } + } catch (e: IllegalAccessException) { + throw RuntimeException(e) + } catch (ignored: ClassCastException) { + return LinkedList() + } + + return object : LinkedList() { + init { + var index = -1 + index = hex.indexOf(constValue, index + 1) + while (index != -1) { + add(IntRange(index / 3, (index + constValue.length) / 3)) + + index = hex.indexOf(constValue, index + 1) + } + } + } + } + } + + private fun buildConstNameChain(length: Int, constMatcher: ConstMatcher, constNameBuilder: StringBuilder) { + //System.out.println(constMatcher.matches); + var i = 0 + while (i < length) { + constNameBuilder.append(" ") + val match = constMatcher.getMatchedConstName(i / 4) + if (match != null) { + var appendedNameLength = match.length + constNameBuilder.append(match) + while (match == constMatcher.getMatchedConstName(i++ / 4)) { + if (appendedNameLength-- < 0) { + constNameBuilder.append(" ") + } + } + + constNameBuilder.append(" ".repeat(match.length % 4)) + } + i++ + } + } + + fun compare(hex1s: String, hex2s: String): String { + val builder = StringBuilder() + + val hex1 = hex1s.trim { it <= ' ' }.replace("\n", "").split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val hex2 = hex2s.trim { it <= ' ' }.replace("\n", "").split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val constMatcher1 = ConstMatcher(hex1s) + val constMatcher2 = ConstMatcher(hex2s) + + if (hex1.size == hex2.size) { + builder.append(GREEN).append("长度一致:").append(hex1.size) + } else { + builder.append(RED).append("长度不一致").append(hex1.size).append("/").append(hex2.size) + } + + + val numberLine = StringBuilder() + val hex1ConstName = StringBuilder() + val hex1b = StringBuilder() + val hex2b = StringBuilder() + val hex2ConstName = StringBuilder() + var dif = 0 + + val length = max(hex1.size, hex2.size) * 4 + buildConstNameChain(length, constMatcher1, hex1ConstName) + buildConstNameChain(length, constMatcher2, hex2ConstName) + + + for (i in 0 until max(hex1.size, hex2.size)) { + var h1: String? = null + var h2: String? = null + var isDif = false + if (hex1.size <= i) { + h1 = RED + "__" + isDif = true + } else { + val matchedConstName = constMatcher1.getMatchedConstName(i) + if (matchedConstName != null) { + h1 = BLUE + hex1[i] + } + } + if (hex2.size <= i) { + h2 = RED + "__" + isDif = true + } else { + val matchedConstName = constMatcher2.getMatchedConstName(i) + if (matchedConstName != null) { + h2 = BLUE + hex2[i] + } + } + + if (h1 == null && h2 == null) { + h1 = hex1[i] + h2 = hex2[i] + if (h1 == h2) { + h1 = GREEN + h1 + h2 = GREEN + h2 + } else { + h1 = RED + h1 + h2 = RED + h2 + isDif = true + } + } else { + if (h1 == null) { + h1 = RED + hex1[i] + } + if (h2 == null) { + h2 = RED + hex2[i] + } + } + + numberLine.append(UNKNOWN).append(getFixedNumber(i)).append(" ") + hex1b.append(" ").append(h1).append(" ") + hex2b.append(" ").append(h2).append(" ") + if (isDif) { + ++dif + } + + //doConstReplacement(hex1b); + //doConstReplacement(hex2b); + } + + return builder.append(" ").append(dif).append(" 个不同").append("\n") + .append(numberLine).append("\n") + .append(hex1ConstName).append("\n") + .append(hex1b).append("\n") + .append(hex2b).append("\n") + .append(hex2ConstName).append("\n") + .toString() + + + } + + + private fun getFixedNumber(number: Int): String { + if (number < 10) { + return "00$number" + } + return if (number < 100) { + "0$number" + } else number.toString() + } + +} + +fun main() { + val scanner = Scanner(System.`in`) + while (true) { + println("Hex1: ") + val hex1 = scanner.nextLine() + println("Hex2: ") + val hex2 = scanner.nextLine() + println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") + println(HexComparator.compare(hex1, hex2)) + println() + } + /* + System.out.println(HexComparator.compare( + //mirai + + "2A 22 96 29 7B 00 40 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 EC 21 40 06 18 89 54 BC Protocol.messageConst1 00 00 01 00 0A 01 00 07 E7 89 9B E9 80 BC 21\n" + , + //e + "2A 22 96 29 7B 00 3F 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 5D 6B 8E 1A FE 39 0B FC Protocol.messageConst1 00 00 01 00 0A 01 00 07 6D 65 73 73 61 67 65" + )); + */ + + + /* + System.out.println(HexComparator.compare( + //e + "90 5E 39 DF 00 02 76 E4 B8 DD 00 00 04 53 00 00 00 01 00 00 15 85 00 00 01 55 35 05 8E C9 BA 16 D0 01 63 5B 59 4B 59 52 31 01 B9 00 00 00 00 00 00 00 00 00 00 00 00 00 7B 7B 7B 7B 00 00 00 00 00 00 00 00 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B AA BB CC DD EE FF AA BB CC", + //mirai + "6F 0B DF 92 00 02 76 E4 B8 DD 00 00 04 53 00 00 00 01 00 00 15 85 00 00 01 55 35 05 8E C9 BA 16 D0 01 63 5B 59 4B 59 52 31 01 B9 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E9 E9 E9 00 00 00 00 00 00 00 00 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B AA BB CC DD EE FF AA BB CC\n\n\n" + ));*/ +} \ No newline at end of file diff --git a/mirai-debug/src/main/java/PacketDebuger.kt b/mirai-debug/src/main/java/PacketDebuger.kt index 5e8cb61c8..e631947d2 100644 --- a/mirai-debug/src/main/java/PacketDebuger.kt +++ b/mirai-debug/src/main/java/PacketDebuger.kt @@ -3,14 +3,16 @@ import jpcap.JpcapCaptor import jpcap.packet.IPPacket import jpcap.packet.UDPPacket +import net.mamoe.mirai.message.defaults.readMessageChain import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.packet.ServerEventPacket import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket +import net.mamoe.mirai.network.protocol.tim.packet.UnknownServerEventPacket +import net.mamoe.mirai.network.protocol.tim.packet.UnknownServerPacket import net.mamoe.mirai.utils.* -import java.io.DataInputStream /** - * 模拟登录并抓取到 session key + * 抓包分析器 * * @author Him188moe */ @@ -59,8 +61,8 @@ object Main { dataReceived(pk.data) } else { try { - println("size = " + pk.data.size) dataSent(pk.data) + println() } catch (e: Exception) { e.printStackTrace() } @@ -74,24 +76,47 @@ object Main { /** - * 从 TIM 内存中读取. + * 可从 TIM 内存中读取 * * 方法: - * 在 Common.dll 中搜索 + * 1. x32dbg 附加 TIM + * 2. `符号` 中找到 common.dll + * 3. 搜索函数 `oi_symmetry_encrypt2` (TEA 加密函数) + * 4. 双击跳转 + * 5. 断点并在TIM发送消息以触发 + * 6. 运行到 `mov eax,dword ptr ss:[ebp+10]` + * 7. 从 eax 开始的 16个 bytes 便是 `sessionKey` */ - const val sessionKey: String = "70 BD 1E 12 20 C1 25 12 A0 F8 4F 0D C0 A0 97 0E" + val sessionKey: ByteArray = "48 C0 11 42 2D FD 8F 36 6E BA BF FD D3 AA B7 AE".hexToBytes() fun dataReceived(data: ByteArray) { + //println("--------------") + //println("接收数据包") + //println("raw packet = " + data.toUHexString()) packetReceived(ServerPacket.ofByteArray(data)) } fun packetReceived(packet: ServerPacket) { when (packet) { 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") + } + + is ServerEventPacket.Raw -> packetReceived(packet.distribute()) + + is UnknownServerEventPacket -> { + println("--------------") + println("未知事件ID=" + packet.packetId.toUHexString()) + println("未知事件: " + packet.input.readAllBytes().toUHexString()) + } + + is ServerEventPacket -> { + println("事件") + println(packet) + } + + is UnknownServerPacket -> { + //ignore } else -> { @@ -99,47 +124,45 @@ object Main { } } + fun dataSent(rawPacket: ByteArray) = rawPacket.cutTail(1).decode { packet -> + println("---------------------------") + packet.skip(3)//head + val idHex = packet.readNBytes(4).toUHexString() + println("发出包ID = $idHex") + packet.skip(TIMProtocol.fixVer2.hexToBytes().size + 1 + 5 - 3 + 1) + + val encryptedBody = packet.readAllBytes() + println("body = ${encryptedBody.toUHexString()}") + + encryptedBody.decode { data -> - fun dataSent(data: ByteArray) { - data.cutTail(1).decode { base -> - base.skip(3) - val idHex = base.readNBytes(4).toUHexString() - println("发出包$idHex") when (idHex.substring(0, 5)) { "00 CD" -> { - println("好友消息发出: ") - dataDecode(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()) - } + println("好友消息") + + val raw = data.readAllBytes() + println("解密前数据: " + raw.toUHexString()) + val messageData = raw.decryptBy(sessionKey) + println("解密结果: " + messageData.toUHexString()) + println("尝试解消息") + messageData.decode { + it.skip( + 4 + 4 + 12 + 2 + 4 + 4 + 16 + 2 + 2 + 4 + 2 + 16 + 4 + 4 + 7 + 15 + 2 + + 1 + ) + val chain = it.readMessageChain() + println(chain.toObjectString()) } } + + "03 88" -> { + println("上传图片-获取图片ID") + data.skip(8) + val body = data.readAllBytes().decryptBy(sessionKey) + println(body.toUHexString()) + } } } + } - - private fun ByteArray.decryptBy(key: ByteArray): ByteArray = TEA.decrypt(this, key) - - private fun ByteArray.decryptBy(key: String): ByteArray = TEA.decrypt(this, key) - - - private fun DataInputStream.skipHex(uHex: String) { - this.skip(uHex.hexToBytes().size.toLong()) - } -} - -/* -00 19 -tim的 publicKey = 02 F4 07 37 2D F1 82 1D 45 E8 30 14 41 74 AF E3 03 AB 29 D7 82 D9 E2 E5 89 -00 00 -tim的 key0836=70 BE 41 20 3A FA 05 B2 2D 66 2E 29 33 55 99 7E -552 -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 - */ - +} \ No newline at end of file