Commit 68969669 authored by Him188's avatar Him188

Change into Kotlin

parent 888faaa6
import kotlin.ranges.IntRange; @file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant")
import net.mamoe.mirai.network.protocol.tim.TIMProtocol; import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacketKt; import net.mamoe.mirai.network.protocol.tim.packet.toUHexString
import net.mamoe.mirai.utils.UtilsKt; import net.mamoe.mirai.utils.toUHexString
import java.awt.Toolkit
import java.awt.*; import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.DataFlavor; import java.lang.reflect.Field
import java.awt.datatransfer.Transferable; import java.util.*
import java.lang.reflect.Field; import kotlin.math.max
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;
/** /**
* Hex 比较器, 并着色已知常量
*
* This could be used to check packet encoding.. * This could be used to check packet encoding..
* but better to run under UNIX * but better to run under UNIX
* *
* @author NaturalHG * @author NaturalHG
* @author Him188moe
*/ */
public class HexComparator { object HexComparator {
/** private val RED = "\u001b[31m"
* a string result
*/
private static final String RED = "\033[31m"; private val GREEN = "\u001b[33m"
private static final String GREEN = "\033[33m"; private val UNKNOWN = "\u001b[30m"
private static final String UNKNOWN = "\033[30m"; private val BLUE = "\u001b[34m"
private static final String BLUE = "\033[34m";
public static final List<HexReader> consts = new LinkedList<>() {{ private val clipboardString: String?
add(new HexReader("90 5E 39 DF 00 02 76 E4 B8 DD 00")); 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()
}
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"}) return null
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"; class ConstMatcher constructor(hex: String) {
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 val matches = LinkedList<Match>()
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(" ", " "); object TestConsts {
//private static final String MESSAGE_TAIL2_10404 ="".replace(" ", " "); 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()
private final List<Match> matches = new LinkedList<>(); val SINGLE_PLAIN_MESSAGE_HEAD = "00 00 01 00 09 01"
private ConstMatcher(String hex) { 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"
CONST_FIELDS.forEach(field -> { .replace(" ", " ")
for (IntRange match : match(hex, field)) { }
matches.add(new Match(match, field.getName()));
init {
CONST_FIELDS.forEach { field ->
for (match in match(hex, field)) {
matches.add(Match(match, field.name))
} }
}); }
} }
private String getMatchedConstName(int hexNumber) { fun getMatchedConstName(hexNumber: Int): String? {
for (Match match : this.matches) { for (match in this.matches) {
if (match.range.contains(hexNumber)) { if (match.range.contains(hexNumber)) {
return match.constName; return match.constName
} }
} }
return null; return null
} }
private static List<IntRange> match(String hex, Field field) { private class Match internal constructor(val range: IntRange, val constName: String)
final String constValue;
try { companion object {
constValue = ((String) field.get(null)).trim(); private val CONST_FIELDS: List<Field> = listOf(
if (constValue.length() / 3 <= 3) {//Minimum numbers of const hex bytes TestConsts::class.java,
return new LinkedList<>(); TIMProtocol::class.java
).map { it.declaredFields }.flatMap { fields ->
fields.map { field ->
field.trySetAccessible()
field
} }
} 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){ private fun match(hex: String, field: Field): List<IntRange> {
this.range = range; val constValue: String
this.constName = constName; 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 static void buildConstNameChain(int length, ConstMatcher constMatcher, StringBuilder constNameBuilder) { private fun buildConstNameChain(length: Int, constMatcher: ConstMatcher, constNameBuilder: StringBuilder) {
//System.out.println(constMatcher.matches); //System.out.println(constMatcher.matches);
for (int i = 0; i < length; i++) { var i = 0
constNameBuilder.append(" "); while (i < length) {
String match = constMatcher.getMatchedConstName(i / 4); constNameBuilder.append(" ")
val match = constMatcher.getMatchedConstName(i / 4)
if (match != null) { if (match != null) {
int appendedNameLength = match.length(); var appendedNameLength = match.length
constNameBuilder.append(match); constNameBuilder.append(match)
while (match.equals(constMatcher.getMatchedConstName(i++ / 4))) { while (match == constMatcher.getMatchedConstName(i++ / 4)) {
if (appendedNameLength-- < 0) { if (appendedNameLength-- < 0) {
constNameBuilder.append(" "); constNameBuilder.append(" ")
} }
} }
constNameBuilder.append(" ".repeat(match.length() % 4)); constNameBuilder.append(" ".repeat(match.length % 4))
} }
i++
} }
} }
private static String compare(String hex1s, String hex2s) { fun compare(hex1s: String, hex2s: String): String {
StringBuilder builder = new StringBuilder(); val builder = StringBuilder()
String[] hex1 = hex1s.trim().replace("\n", "").split(" "); val hex1 = hex1s.trim { it <= ' ' }.replace("\n", "").split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
String[] hex2 = hex2s.trim().replace("\n", "").split(" "); val hex2 = hex2s.trim { it <= ' ' }.replace("\n", "").split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
ConstMatcher constMatcher1 = new ConstMatcher(hex1s); val constMatcher1 = ConstMatcher(hex1s)
ConstMatcher constMatcher2 = new ConstMatcher(hex2s); val constMatcher2 = ConstMatcher(hex2s)
if (hex1.length == hex2.length) { if (hex1.size == hex2.size) {
builder.append(GREEN).append("长度一致:").append(hex1.length); builder.append(GREEN).append("长度一致:").append(hex1.size)
} else { } else {
builder.append(RED).append("长度不一致").append(hex1.length).append("/").append(hex2.length); builder.append(RED).append("长度不一致").append(hex1.size).append("/").append(hex2.size)
} }
StringBuilder numberLine = new StringBuilder(); val numberLine = StringBuilder()
StringBuilder hex1ConstName = new StringBuilder(); val hex1ConstName = StringBuilder()
StringBuilder hex1b = new StringBuilder(); val hex1b = StringBuilder()
StringBuilder hex2b = new StringBuilder(); val hex2b = StringBuilder()
StringBuilder hex2ConstName = new StringBuilder(); val hex2ConstName = StringBuilder()
int dif = 0; var dif = 0
int length = Math.max(hex1.length, hex2.length) * 4; val length = max(hex1.size, hex2.size) * 4
buildConstNameChain(length, constMatcher1, hex1ConstName); buildConstNameChain(length, constMatcher1, hex1ConstName)
buildConstNameChain(length, constMatcher2, hex2ConstName); buildConstNameChain(length, constMatcher2, hex2ConstName)
for (int i = 0; i < Math.max(hex1.length, hex2.length); ++i) { for (i in 0 until max(hex1.size, hex2.size)) {
String h1 = null; var h1: String? = null
String h2 = null; var h2: String? = null
boolean isDif = false; var isDif = false
if (hex1.length <= i) { if (hex1.size <= i) {
h1 = RED + "__"; h1 = RED + "__"
isDif = true; isDif = true
} else { } else {
String matchedConstName = constMatcher1.getMatchedConstName(i); val matchedConstName = constMatcher1.getMatchedConstName(i)
if (matchedConstName != null) { if (matchedConstName != null) {
h1 = BLUE + hex1[i]; h1 = BLUE + hex1[i]
} }
} }
if (hex2.length <= i) { if (hex2.size <= i) {
h2 = RED + "__"; h2 = RED + "__"
isDif = true; isDif = true
} else { } else {
String matchedConstName = constMatcher2.getMatchedConstName(i); val matchedConstName = constMatcher2.getMatchedConstName(i)
if (matchedConstName != null) { if (matchedConstName != null) {
h2 = BLUE + hex2[i]; h2 = BLUE + hex2[i]
} }
} }
if (h1 == null && h2 == null) { if (h1 == null && h2 == null) {
h1 = hex1[i]; h1 = hex1[i]
h2 = hex2[i]; h2 = hex2[i]
if (h1.equals(h2)) { if (h1 == h2) {
h1 = GREEN + h1; h1 = GREEN + h1
h2 = GREEN + h2; h2 = GREEN + h2
} else { } else {
h1 = RED + h1; h1 = RED + h1
h2 = RED + h2; h2 = RED + h2
isDif = true; isDif = true
} }
} else { } else {
if (h1 == null) { if (h1 == null) {
h1 = RED + hex1[i]; h1 = RED + hex1[i]
} }
if (h2 == null) { if (h2 == null) {
h2 = RED + hex2[i]; h2 = RED + hex2[i]
} }
} }
numberLine.append(UNKNOWN).append(getFixedNumber(i)).append(" "); numberLine.append(UNKNOWN).append(getFixedNumber(i)).append(" ")
hex1b.append(" ").append(h1).append(" "); hex1b.append(" ").append(h1).append(" ")
hex2b.append(" ").append(h2).append(" "); hex2b.append(" ").append(h2).append(" ")
if (isDif) { if (isDif) {
++dif; ++dif
} }
//doConstReplacement(hex1b); //doConstReplacement(hex1b);
//doConstReplacement(hex2b); //doConstReplacement(hex2b);
} }
return (builder.append(" ").append(dif).append(" 个不同").append("\n") return builder.append(" ").append(dif).append(" 个不同").append("\n")
.append(numberLine).append("\n") .append(numberLine).append("\n")
.append(hex1ConstName).append("\n") .append(hex1ConstName).append("\n")
.append(hex1b).append("\n") .append(hex1b).append("\n")
.append(hex2b).append("\n") .append(hex2b).append("\n")
.append(hex2ConstName).append("\n") .append(hex2ConstName).append("\n")
) .toString()
.toString();
} }
private static void doConstReplacement(StringBuilder builder) { private fun getFixedNumber(number: Int): String {
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) { if (number < 10) {
return "00" + number; return "00$number"
} }
if (number < 100) { return if (number < 100) {
return "0" + number; "0$number"
} } else number.toString()
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 { fun main() {
private String s; val scanner = Scanner(System.`in`)
private int pos = 0; while (true) {
private int lastHaxPos = 0; println("Hex1: ")
val hex1 = scanner.nextLine()
println("Hex2: ")
public HexReader(String s) { val hex2 = scanner.nextLine()
this.s = s; 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()
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;
} }
/*
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"
));
*/
public void reset() {
this.pos = 0;
}
}
/*
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
...@@ -10,7 +10,7 @@ import net.mamoe.mirai.utils.* ...@@ -10,7 +10,7 @@ import net.mamoe.mirai.utils.*
import java.io.DataInputStream import java.io.DataInputStream
/** /**
* 模拟登录并抓取到 session key * 抓包分析器
* *
* @author Him188moe * @author Him188moe
*/ */
...@@ -74,12 +74,18 @@ object Main { ...@@ -74,12 +74,18 @@ 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) {
packetReceived(ServerPacket.ofByteArray(data)) packetReceived(ServerPacket.ofByteArray(data))
...@@ -88,7 +94,6 @@ object Main { ...@@ -88,7 +94,6 @@ object Main {
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") println("! ServerEventPacket.Raw.Encrypted")
packetReceived(packet.decrypt(sessionKey)) packetReceived(packet.decrypt(sessionKey))
println("! decrypt succeed") println("! decrypt succeed")
...@@ -112,22 +117,13 @@ object Main { ...@@ -112,22 +117,13 @@ object Main {
//it.readShort() //it.readShort()
//println(it.readUInt()) //println(it.readUInt())
println(it.readNBytes(TIMProtocol.fixVer2.hexToBytes().size + 1 + 5 - 3 + 1).toUHexString()) println(it.readNBytes(TIMProtocol.fixVer2.hexToBytes().size + 1 + 5 - 3 + 1).toUHexString())
it.readAllBytes().let { val messageData = it.readAllBytes().decryptBy(sessionKey)
println("解密")
println(it.size)
println(it.toUHexString())
println(it.decryptBy(sessionKey).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) { private fun DataInputStream.skipHex(uHex: String) {
this.skip(uHex.hexToBytes().size.toLong()) this.skip(uHex.hexToBytes().size.toLong())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment