PacketDebuger.kt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. @file:Suppress("EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_UNSIGNED_LITERALS")
  2. import PacketDebugger.dataSent
  3. import PacketDebugger.qq
  4. import PacketDebugger.sessionKey
  5. import kotlinx.coroutines.*
  6. import kotlinx.io.core.*
  7. import net.mamoe.mirai.Bot
  8. import net.mamoe.mirai.network.BotNetworkHandler
  9. import net.mamoe.mirai.network.BotSession
  10. import net.mamoe.mirai.network.protocol.tim.TIMProtocol
  11. import net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocketAdapter
  12. import net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
  13. import net.mamoe.mirai.network.protocol.tim.packet.*
  14. import net.mamoe.mirai.network.protocol.tim.packet.login.CaptchaKey
  15. import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
  16. import net.mamoe.mirai.network.protocol.tim.packet.login.ShareKey
  17. import net.mamoe.mirai.network.protocol.tim.packet.login.TouchKey
  18. import net.mamoe.mirai.utils.BotConfiguration
  19. import net.mamoe.mirai.utils.DecryptionFailedException
  20. import net.mamoe.mirai.utils.decryptBy
  21. import net.mamoe.mirai.utils.io.*
  22. import org.pcap4j.core.BpfProgram.BpfCompileMode
  23. import org.pcap4j.core.PacketListener
  24. import org.pcap4j.core.PcapNetworkInterface
  25. import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode
  26. import org.pcap4j.core.Pcaps
  27. import java.util.concurrent.Executors
  28. import kotlin.concurrent.thread
  29. import kotlin.coroutines.CoroutineContext
  30. import kotlin.io.use
  31. /**
  32. * 避免 print 重叠. 单线程处理足够调试
  33. */
  34. val DISPATCHER = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
  35. private fun listenDevice(localIp: String, device: PcapNetworkInterface) {
  36. val sender = device.openLive(65536, PromiscuousMode.PROMISCUOUS, 10)
  37. thread {
  38. sender.setFilter("src $localIp && udp port 8000", BpfCompileMode.OPTIMIZE)
  39. try {
  40. sender.loop(Int.MAX_VALUE, PacketListener {
  41. runBlocking {
  42. withContext(DISPATCHER) {
  43. try {
  44. dataSent(it.rawData.drop(42).toByteArray())
  45. } catch (e: Throwable) {
  46. e.printStackTrace()
  47. }
  48. }
  49. }
  50. })
  51. } catch (e: Throwable) {
  52. e.printStackTrace()
  53. }
  54. }
  55. val receiver = device.openLive(65536, PromiscuousMode.PROMISCUOUS, 10)
  56. thread {
  57. receiver.setFilter("dst $localIp && udp port 8000", BpfCompileMode.OPTIMIZE)
  58. try {
  59. receiver.loop(Int.MAX_VALUE, PacketListener {
  60. runBlocking {
  61. withContext(DISPATCHER) {
  62. try {
  63. PacketDebugger.dataReceived(it.rawData.drop(42).toByteArray())
  64. } catch (e: Throwable) {
  65. e.printStackTrace()
  66. }
  67. }
  68. }
  69. })
  70. } catch (e: Throwable) {
  71. e.printStackTrace()
  72. }
  73. }
  74. }
  75. fun main() {
  76. /*
  77. check(System.getProperty("os.arch") == "x86") { "Jpcap can only work with x86 JDK" }
  78. JpcapCaptor.getDeviceList().forEach {
  79. println(it)
  80. }
  81. JpcapCaptor.openDevice(JpcapCaptor.getDeviceList()[0], 65535, true, 1000).loopPacket(Int.MAX_VALUE) {
  82. println(it)
  83. }*/
  84. val localIp = Pcaps.findAllDevs()[0].addresses[0].address.hostAddress
  85. println("localIp = $localIp")
  86. Pcaps.findAllDevs().forEach {
  87. listenDevice(localIp, it)
  88. }
  89. println("Ready perfectly")
  90. }
  91. /**
  92. * 抓包分析器.
  93. * 设置好 [sessionKey], 和 [qq] 后运行即可开始抓包和自动解密
  94. *
  95. * @author Him188moe
  96. */
  97. object PacketDebugger {
  98. /**
  99. * 会话密匙, 用于解密数据.
  100. * 在一次登录中会话密匙不会改变.
  101. *
  102. * 从 TIM 内存中读取, windows 方法:
  103. * 1. x32dbg 附加 TIM
  104. * 2. `符号` 中找到 common.dll
  105. * 3. 搜索函数 `oi_symmetry_encrypt2` (TEA 加密函数)
  106. * 4. 双击跳转
  107. * 5. 设置断点
  108. * 6. 在 TIM 发送一条消息触发断点
  109. * 7. 运行完 `mov eax,dword ptr ss:[ebp+10]`
  110. * 8. 查看内存, `eax` 到 `eax+10` 的 16 字节就是 `sessionKey`
  111. */
  112. val sessionKey: SessionKey = SessionKey("10 52 CD 27 0B A5 9C 74 1B A2 15 7E 41 19 0C 7B".hexToBytes())
  113. const val qq: UInt = 761025446u
  114. val IgnoredPacketIdList: List<PacketId> = listOf(
  115. KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE,
  116. KnownPacketId.CHANGE_ONLINE_STATUS,
  117. KnownPacketId.HEARTBEAT
  118. )
  119. suspend fun dataReceived(data: ByteArray) {
  120. //println("raw = " + data.toUHexString())
  121. data.read {
  122. discardExact(3)
  123. val id = matchPacketId(readUShort())
  124. val sequenceId = readUShort()
  125. if (id == KnownPacketId.HEARTBEAT || readUInt() != qq)
  126. return@read
  127. if (IgnoredPacketIdList.contains(id)) {
  128. return
  129. }
  130. println("--------------")
  131. discardExact(3)//0x00 0x00 0x00. 但更可能是应该 discard 8
  132. println(
  133. "接收包id=$id, " +
  134. "\nsequence=${sequenceId.toUHexString()}"
  135. )
  136. // val remaining = this.readRemainingBytes().cutTail(1)
  137. try {
  138. val packet = use {
  139. with(id.factory) {
  140. provideDecrypter(id.factory)
  141. .decrypt([email protected]().let { ByteReadPacket(it, 0, it.size - 1) })
  142. .let {
  143. it.debugPrint(" 解密body", it.remaining)
  144. }
  145. .decode(id, sequenceId, DebugNetworkHandler)
  146. }
  147. }
  148. handlePacket(id, sequenceId, packet, id.factory)
  149. } catch (e: DecryptionFailedException) {
  150. // println("密文body=" + remaining.toUHexString())
  151. println(" 解密body=解密失败")
  152. }
  153. }
  154. }
  155. internal fun ByteReadPacket.debugPrint(name: String = "", length: Long = this.remaining): ByteReadPacket {
  156. val bytes = this.readBytes(length.toInt())
  157. println("$name=" + bytes.toUHexString())
  158. return bytes.toReadPacket()
  159. }
  160. /**
  161. * 提供解密密匙. 无需修改
  162. */
  163. @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
  164. internal fun <D : Decrypter> provideDecrypter(factory: PacketFactory<*, D>): D =
  165. when (factory.decrypterType) {
  166. TouchKey -> TouchKey
  167. CaptchaKey -> CaptchaKey
  168. ShareKey -> ShareKey
  169. NoDecrypter -> NoDecrypter
  170. SessionKey -> sessionKey
  171. else -> error("No decrypter is found")
  172. } as? D ?: error("Internal error: could not cast decrypter which is found for factory to class Decrypter")
  173. /**
  174. * 处理一个包
  175. */
  176. @Suppress("UNUSED_PARAMETER")
  177. fun <TPacket : Packet> handlePacket(
  178. id: PacketId,
  179. sequenceId: UShort,
  180. packet: TPacket,
  181. factory: PacketFactory<TPacket, *>
  182. ) {
  183. println(" 解析body=$packet")
  184. return
  185. }
  186. fun dataSent(rawPacket: ByteArray) = rawPacket.cutTail(1).read {
  187. // 02 38 03
  188. // 03 52 78 9F
  189. // 3E 03 3F A2 04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00
  190. // 02 38 03
  191. // 01 BD 63 D6
  192. // 3E 03 3F A2 02 00 00 00 01 2E 01 00 00 69 35
  193. discardExact(3)//head
  194. val id = matchPacketId(readUShort())
  195. val sequence = readUShort().toUHexString()
  196. if (IgnoredPacketIdList.contains(id)) {
  197. return
  198. }
  199. if (readUInt() != qq) {
  200. return@read
  201. }
  202. println("---------------------------")
  203. println("发出包ID = $id")
  204. println("sequence = $sequence")
  205. println(
  206. " fixVer2=" + when (val flag = readByte().toInt()) {
  207. 2 -> byteArrayOf(2) + readBytes(TIMProtocol.fixVer2.size - 1)
  208. 3 -> byteArrayOf(3) + readBytes(TIMProtocol.fixVer2.size - 1 + 4)//4 个0
  209. 4 -> byteArrayOf(4) + readBytes(TIMProtocol.fixVer2.size - 1 + 8)//8 个 0
  210. 0 -> byteArrayOf(0) + readBytes(2)
  211. else -> error("unknown fixVer2 flag=$flag. Remaining =${readBytes().toUHexString()}")
  212. }.toUHexString()
  213. )
  214. //39 27 DC E2 04 00 00 00 00 00 00 00 1E 0E 89 00 00 01 05 0F 05 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 3E 03 3F A2 00 00 00 00 00 00 00 00 00 00 00
  215. val encryptedBody = readRemainingBytes()
  216. try {
  217. println(" 解密body=${encryptedBody.decryptBy(sessionKey.value).toUHexString()}")
  218. } catch (e: DecryptionFailedException) {
  219. println(" 密文=" + encryptedBody.toUHexString())
  220. println(" 解密body=解密失败")
  221. }
  222. encryptedBody.read {
  223. /*
  224. when (idHex.substring(0, 5)) {
  225. "00 CD" -> {
  226. println("好友消息")
  227. val raw = readRemainingBytes()
  228. //println("解密前数据: " + raw.toUHexString())
  229. val messageData = raw.decryptBy(sessionKey.value)
  230. //println("解密结果: " + messageData.toUHexString())
  231. println("尝试解消息")
  232. try {
  233. messageData.read {
  234. discardExact(
  235. 4 + 4 + 12 + 2 + 4 + 4 + 16 + 2 + 2 + 4 + 2 + 16 + 4 + 4 + 7 + 15 + 2
  236. + 1
  237. )
  238. val chain = readMessageChain()
  239. println(chain)
  240. }
  241. } catch (e: Exception) {
  242. println("失败")
  243. }
  244. }*/
  245. /*
  246. "03 88" -> {
  247. println("0388上传图片-获取图片ID")
  248. discardExact(8)
  249. //val body = readRemainingBytes().decryptBy(sessionKey)
  250. //println(body.toUHexString())
  251. }
  252. }*/
  253. }
  254. }
  255. }
  256. internal object DebugNetworkHandler : BotNetworkHandler<DataPacketSocketAdapter>, CoroutineScope {
  257. override val socket: DataPacketSocketAdapter = object : DataPacketSocketAdapter {
  258. override val serverIp: String
  259. get() = ""
  260. override val channel: PlatformDatagramChannel
  261. get() = error("UNSUPPORTED")
  262. override val isOpen: Boolean
  263. get() = true
  264. override suspend fun sendPacket(packet: OutgoingPacket) {
  265. }
  266. override fun close() {
  267. }
  268. override val owner: Bot
  269. get() = bot
  270. }
  271. override val bot: Bot = Bot(qq, "")
  272. override val session = BotSession(bot, sessionKey, socket, this)
  273. override suspend fun login(configuration: BotConfiguration): LoginResult = LoginResult.SUCCESS
  274. override suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*, *>) {
  275. }
  276. override suspend fun sendPacket(packet: OutgoingPacket) {
  277. }
  278. override suspend fun awaitDisconnection() {
  279. }
  280. override val coroutineContext: CoroutineContext
  281. get() = GlobalScope.coroutineContext
  282. }