mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-02 04:30:25 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
db8315689e
@ -8,7 +8,7 @@
|
|||||||
项目处于开发阶段,学生无法每日大量更新。
|
项目处于开发阶段,学生无法每日大量更新。
|
||||||
项目还有很多未完善的地方, 欢迎任何的代码贡献, 或是 issue.
|
项目还有很多未完善的地方, 欢迎任何的代码贡献, 或是 issue.
|
||||||
部分协议来自网络上开源项目
|
部分协议来自网络上开源项目
|
||||||
一切开发旨在学习,请勿用于非法用途
|
**一切开发旨在学习,请勿用于非法用途**
|
||||||
|
|
||||||
## 抢先体验
|
## 抢先体验
|
||||||
核心框架结构已经开发完毕,一些核心功能也测试完成。
|
核心框架结构已经开发完毕,一些核心功能也测试完成。
|
||||||
|
@ -17,6 +17,8 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
|
apply plugin: 'java'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation rootProject.ext.kotlinJvm
|
implementation rootProject.ext.kotlinJvm
|
||||||
implementation rootProject.ext.reflect
|
implementation rootProject.ext.reflect
|
||||||
@ -28,6 +30,7 @@ kotlin {
|
|||||||
implementation 'org.jsoup:jsoup:1.12.1'
|
implementation 'org.jsoup:jsoup:1.12.1'
|
||||||
implementation 'org.ini4j:ini4j:0.5.2'
|
implementation 'org.ini4j:ini4j:0.5.2'
|
||||||
|
|
||||||
|
implementation project(":mirai-protocol-timpc")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmTest {
|
jvmTest {
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
167
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt
Normal file
167
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,7 +20,11 @@ class Face(val id: FaceID) : Message() {
|
|||||||
override val type: MessageKey = Key
|
override val type: MessageKey = Key
|
||||||
|
|
||||||
override fun toStringImpl(): String {
|
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 ->
|
override fun toByteArray(): ByteArray = dataEncode { section ->
|
||||||
@ -48,7 +52,7 @@ class Face(val id: FaceID) : Message() {
|
|||||||
|
|
||||||
override operator fun contains(sub: String): Boolean = false
|
override operator fun contains(sub: String): Boolean = false
|
||||||
|
|
||||||
internal object PacketHelper {
|
object PacketHelper {
|
||||||
fun ofByteArray(data: ByteArray): Face = dataDecode(data) {
|
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 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
|
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
|
||||||
|
@ -22,7 +22,11 @@ open class Image(val imageId: String) : Message() {
|
|||||||
override val type: MessageKey = Key
|
override val type: MessageKey = Key
|
||||||
|
|
||||||
override fun toStringImpl(): String {
|
override fun toStringImpl(): String {
|
||||||
return imageId
|
return "[$imageId]"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toObjectString(): String {
|
||||||
|
return "Image[$imageId]"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toByteArray(): ByteArray = dataEncode { section ->
|
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
|
override operator fun contains(sub: String): Boolean = false //No string can be contained in a image
|
||||||
|
|
||||||
internal object PacketHelper {
|
object PacketHelper {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun ofByteArray0x06(data: ByteArray): Image = dataDecode(data) {
|
fun ofByteArray0x06(data: ByteArray): Image = dataDecode(data) {
|
||||||
it.skip(1)
|
it.skip(1)
|
||||||
|
@ -2,7 +2,12 @@ package net.mamoe.mirai.message.defaults
|
|||||||
|
|
||||||
import net.mamoe.mirai.message.Message
|
import net.mamoe.mirai.message.Message
|
||||||
import net.mamoe.mirai.message.MessageKey
|
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.dataEncode
|
||||||
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
|
import java.io.DataInputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Collectors
|
import java.util.stream.Collectors
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
@ -95,4 +100,69 @@ class MessageChain : Message {
|
|||||||
operator fun component1(): Message = this.list[0]
|
operator fun component1(): Message = this.list[0]
|
||||||
operator fun component2(): Message = this.list[1]
|
operator fun component2(): Message = this.list[1]
|
||||||
operator fun component3(): Message = this.list[2]
|
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
|
||||||
|
}
|
@ -38,7 +38,7 @@ class PlainText(private val text: String) : Message() {
|
|||||||
|
|
||||||
override operator fun contains(sub: String): Boolean = this.toString().contains(sub)
|
override operator fun contains(sub: String): Boolean = this.toString().contains(sub)
|
||||||
|
|
||||||
internal object PacketHelper {
|
object PacketHelper {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun ofByteArray(data: ByteArray): PlainText = dataDecode(data) {
|
fun ofByteArray(data: ByteArray): PlainText = dataDecode(data) {
|
||||||
it.skip(1)
|
it.skip(1)
|
||||||
|
@ -39,6 +39,7 @@ object TIMProtocol {
|
|||||||
*/
|
*/
|
||||||
const val fixVer2 = "02 00 00 00 01 01 01 00 00 68 20"
|
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 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
|
* 0825data1
|
||||||
*/
|
*/
|
||||||
@ -105,6 +106,7 @@ object TIMProtocol {
|
|||||||
* length=15
|
* length=15
|
||||||
*/
|
*/
|
||||||
const val messageConst1 = "00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
|
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<Int, ByteArray> = mutableMapOf()
|
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()
|
||||||
|
|
||||||
|
@ -2,11 +2,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.network.protocol.tim.packet
|
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.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.network.protocol.tim.TIMProtocol
|
||||||
import net.mamoe.mirai.utils.dataDecode
|
import net.mamoe.mirai.utils.dataDecode
|
||||||
import net.mamoe.mirai.utils.hexToBytes
|
import net.mamoe.mirai.utils.hexToBytes
|
||||||
@ -49,7 +46,7 @@ abstract class ServerEventPacket(input: DataInputStream, val packetId: ByteArray
|
|||||||
|
|
||||||
@PacketId("00 17")
|
@PacketId("00 17")
|
||||||
class Encrypted(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
|
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.goto(108)
|
||||||
this.input.readLVByteArray()
|
this.input.readLVByteArray()
|
||||||
input.skip(2)//2个0x00
|
input.skip(2)//2个0x00
|
||||||
message = input.readSections()
|
message = input.readMessageChain()
|
||||||
|
|
||||||
val map = input.readTLVMap(true)
|
val map = input.readTLVMap(true)
|
||||||
if (map.containsKey(18)) {
|
if (map.containsKey(18)) {
|
||||||
@ -262,7 +259,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
|
|||||||
input.goto(93 + l1)
|
input.goto(93 + l1)
|
||||||
input.readLVByteArray()//font
|
input.readLVByteArray()//font
|
||||||
input.skip(2)//2个0x00
|
input.skip(2)//2个0x00
|
||||||
message = input.readSections()
|
message = input.readMessageChain()
|
||||||
|
|
||||||
val map: Map<Int, ByteArray> = input.readTLVMap(true).withDefault { byteArrayOf() }
|
val map: Map<Int, ByteArray> = input.readTLVMap(true).withDefault { byteArrayOf() }
|
||||||
println(map.getValue(18))
|
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
|
牛逼 (10404
|
||||||
|
@ -34,11 +34,22 @@ class ClientSendFriendMessagePacket(
|
|||||||
writeRandom(2)
|
writeRandom(2)
|
||||||
writeTime()
|
writeTime()
|
||||||
writeHex("00 00" +
|
writeHex("00 00" +
|
||||||
"00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00")
|
"00 00 00 00")
|
||||||
//01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 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()
|
writeTime()
|
||||||
writeRandom(4)
|
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
|
writeHex(TIMProtocol.messageConst1)//... 85 E9 BB 91
|
||||||
writeZero(2)
|
writeZero(2)
|
||||||
|
|
||||||
@ -56,9 +67,5 @@ class ClientSendFriendMessagePacket(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@PacketId("00 CD")
|
@PacketId("00 CD")
|
||||||
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input)
|
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input)
|
@ -37,18 +37,19 @@ class ClientTryGetImageIDPacket(
|
|||||||
|
|
||||||
writeZero(2)
|
writeZero(2)
|
||||||
|
|
||||||
writeHex("5E")
|
writeHex("5B")//原5E
|
||||||
writeHex("08")
|
writeHex("08")
|
||||||
writeHex("01 12 03 98 01 01 10 01")
|
writeHex("01 12 03 98 01 01 10 01")
|
||||||
|
|
||||||
writeHex("1A")
|
writeHex("1A")
|
||||||
writeHex("5A")
|
writeHex("57")//原5A
|
||||||
|
|
||||||
writeHex("08")
|
writeHex("08")
|
||||||
writeUVarInt(groupNumberOrQQNumber)
|
writeUVarInt(groupNumberOrQQNumber)//FB D2 D8 94
|
||||||
|
|
||||||
|
writeByte(0x02)
|
||||||
writeHex("10")
|
writeHex("10")
|
||||||
writeUVarInt(botNumber)
|
writeUVarInt(botNumber)//A2 FF 8C F0
|
||||||
|
|
||||||
writeHex("18 00")
|
writeHex("18 00")
|
||||||
|
|
||||||
@ -57,10 +58,13 @@ class ClientTryGetImageIDPacket(
|
|||||||
write(md5(byteArray))
|
write(md5(byteArray))
|
||||||
|
|
||||||
writeHex("28")
|
writeHex("28")
|
||||||
writeUVarInt(byteArray.size.toUInt())
|
writeUVarInt(byteArray.size.toUInt())//E2 0D
|
||||||
|
|
||||||
writeHex("32")
|
writeHex("32")
|
||||||
writeHex("1A")
|
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("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")
|
writeHex("38 01")
|
||||||
|
@ -84,6 +84,11 @@ fun <R> dataDecode(byteArray: ByteArray, t: (DataInputStream) -> R): R = byteArr
|
|||||||
|
|
||||||
fun <R> ByteArray.decode(t: (DataInputStream) -> R): R = this.dataInputStream().let(t)
|
fun <R> 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) {
|
fun DataInputStream.skip(n: Number) {
|
||||||
this.skip(n.toLong())
|
this.skip(n.toLong())
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,6 @@ fun DataOutputStream.writeVarInt(signedInt: Int) {
|
|||||||
this.writeUVarInt(encodeZigZag32(signedInt))
|
this.writeUVarInt(encodeZigZag32(signedInt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun DataOutputStream.writeUVarInt(uint: UInt) {
|
fun DataOutputStream.writeUVarInt(uint: UInt) {
|
||||||
return writeUVarInt(uint.toLong())
|
return writeUVarInt(uint.toLong())
|
||||||
|
@ -3,11 +3,12 @@ apply plugin: "java"
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':mirai-core')
|
implementation project(':mirai-core')
|
||||||
compile 'com.google.protobuf:protobuf-java:3.5.0'
|
|
||||||
compile files('./lib/jpcap.jar')
|
compile files('./lib/jpcap.jar')
|
||||||
|
|
||||||
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2'
|
compile rootProject.ext.coroutineCommon
|
||||||
compile 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50'
|
compile rootProject.ext.kotlinJvm
|
||||||
|
|
||||||
|
compile group: 'com.google.protobuf', name: 'protobuf-java', version: rootProject.ext.protobuf_version
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
|
@ -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<HexReader> 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<Field> 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<Match> 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<IntRange> 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<Integer> 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<String, Integer> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
291
mirai-debug/src/main/java/HexComparator.kt
Normal file
291
mirai-debug/src/main/java/HexComparator.kt
Normal file
@ -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<Match>()
|
||||||
|
|
||||||
|
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<Field> = 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<IntRange> {
|
||||||
|
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<IntRange>() {
|
||||||
|
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"
|
||||||
|
));*/
|
||||||
|
}
|
@ -3,14 +3,16 @@
|
|||||||
import jpcap.JpcapCaptor
|
import jpcap.JpcapCaptor
|
||||||
import jpcap.packet.IPPacket
|
import jpcap.packet.IPPacket
|
||||||
import jpcap.packet.UDPPacket
|
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.TIMProtocol
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.ServerEventPacket
|
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.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 net.mamoe.mirai.utils.*
|
||||||
import java.io.DataInputStream
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模拟登录并抓取到 session key
|
* 抓包分析器
|
||||||
*
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
@ -59,8 +61,8 @@ object Main {
|
|||||||
dataReceived(pk.data)
|
dataReceived(pk.data)
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
println("size = " + pk.data.size)
|
|
||||||
dataSent(pk.data)
|
dataSent(pk.data)
|
||||||
|
println()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
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) {
|
fun dataReceived(data: ByteArray) {
|
||||||
|
//println("--------------")
|
||||||
|
//println("接收数据包")
|
||||||
|
//println("raw packet = " + data.toUHexString())
|
||||||
packetReceived(ServerPacket.ofByteArray(data))
|
packetReceived(ServerPacket.ofByteArray(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun packetReceived(packet: ServerPacket) {
|
fun packetReceived(packet: ServerPacket) {
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is ServerEventPacket.Raw.Encrypted -> {
|
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))
|
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 -> {
|
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)) {
|
when (idHex.substring(0, 5)) {
|
||||||
"00 CD" -> {
|
"00 CD" -> {
|
||||||
println("好友消息发出: ")
|
println("好友消息")
|
||||||
dataDecode(data) {
|
|
||||||
//it.readShort()
|
val raw = data.readAllBytes()
|
||||||
//println(it.readUInt())
|
println("解密前数据: " + raw.toUHexString())
|
||||||
println(it.readNBytes(TIMProtocol.fixVer2.hexToBytes().size + 1 + 5 - 3 + 1).toUHexString())
|
val messageData = raw.decryptBy(sessionKey)
|
||||||
it.readAllBytes().let {
|
println("解密结果: " + messageData.toUHexString())
|
||||||
println("解密")
|
println("尝试解消息")
|
||||||
println(it.size)
|
messageData.decode {
|
||||||
println(it.toUHexString())
|
it.skip(
|
||||||
println(it.decryptBy(sessionKey).toUHexString())
|
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
|
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user