feat(huya): ✅ XXTEA变体加密侧回环验证+ga全表14方法 (R25.5)
- sub_24D58 正参数=加密/负参数=解密, 44B回环100%还原, key@3DF88确认 - ga JNI表14方法全解 (j209202=取DID! k209202=传输加密入口@0x219d8) - 判断: 捕获密文=ga传输层 (非mfa内部序列化), 需ga IDA导出或运行时状态
This commit is contained in:
@@ -1313,3 +1313,32 @@ dfpReport: tReq = [10B 魔数 57 18 82 cf 66 4b b3 94 01 ee][3988B 加密采
|
||||
- key 槽验证: 3DF88 结构内偏移 / 或 key 由 config 派生
|
||||
- b87_ 全链 harness 直调 (喂真实捕获密文 → inflate → 明文!)
|
||||
- a87_@0x25680 = 加密侧 (sub_27DA4 链) — "纯代码铸造" 的核心
|
||||
|
||||
## §11.56 R25.5: 🎯 XXTEA 变体加密侧回环验证成功 — 3DF88 密钥确认 (2026-08-29)
|
||||
|
||||
### harness 直调 sub_24D58 回环实验
|
||||
- `sub_24D58(buf, +n, key@3DF88)` = **加密** (a2 正参数分支)
|
||||
- `sub_24D58(buf, -n, key@3DF88)` = **解密** (负参数分支)
|
||||
- 44B 已知明文 → ENC → DEC → **100% 还原** (roundtrip match=True!)
|
||||
- **key@0x3DF88 = 真密钥**: d2785ef1-c4c23330-25a9c8ba-3a867e67 (u32 LE)
|
||||
- 三层后端对比: Unicorn2 直跑 XXTEA 无原子指令 ✓ (dynarmic 在 2E250 链 JIT 崩)
|
||||
|
||||
### ga (turingga.so) JNI 表全解 — harness 注册后读表 @0x4d1a0
|
||||
14 方法: a209202(SparseArray,Context,Map,Map,I)@0x&1fcb8 b209202(SparseArray,[B,Map,I)@0x20110
|
||||
c209202(SparseArray,Context)@0x20574 d209202(SparseArray,Context,I)@0x20688 e209202(SparseArray,Context,Map,I)@0x20984
|
||||
f209202(SparseArray,[B,I)@0x20fc4 g209202(SparseArray,Context,Map,I)@0x211e8 h209202(...)@0x21468
|
||||
i209202(SparseArray,Context,Obj,Obj,Obj)@0x21624 **j209202()String@0x21948 (取DID!)**
|
||||
**k209202([B)[B@0x219d8 (字节进出 = 传输加密!)** l209202(InvocationHandler,AtomicReference,ClassLoader)@0x42d50
|
||||
m209202(SparseArray,Context,Map)@0x21bdc n209202(SparseArray,[B,I,String,J)@0x43900
|
||||
- ga JNI_OnLoad @0x1faf0 (dynsym!) — 注册 = JNIEnv vtable-215 + 表@0x4d1a0 + 14计数
|
||||
- ga 传输加密入口 k209202@0x219d8: strncpy@plt + 内部链 (0x19378/0x13eb8×2/0x1c640×2/0x24334/0x19258)
|
||||
|
||||
### 现状判断
|
||||
- **捕获密文 ≠ mfa 的 b87_ 内部序列化** (sub_2E250 全偏移网格拒绝 = key 或格式不匹配)
|
||||
- **捕获密文 = ga 传输层加密** (k209202 家族) — ga 调用返回 -1 = SDK 全局状态未初始化
|
||||
- mfa 侧 a87_@0x25680 = 事件收集序列化 (sub_27DA4: timing-tags + 0x11-version-key + sub_2C76C 收集器) — 非传输加密
|
||||
- 捕获密文 10B 魔数 7cda... = 运行时合成 (无反编译常量)
|
||||
|
||||
### 下一步 (两条关键路径)
|
||||
1. **用户导出的 turingga.so IDA export** → k209202@0x219d8 反编译 → 传输加密链静态全解 (同 mfa 方法论)
|
||||
2. harness 继续喂 ga 运行时状态 (0x48XXX 构建器等价物) → k209202 真调用 (同 b87_ -1 问题)
|
||||
|
||||
@@ -54,7 +54,11 @@ public class TuringProbe {
|
||||
TuringProbe() {
|
||||
com.github.unidbg.arm.backend.BackendFactory backend;
|
||||
try {
|
||||
if (System.getenv().containsKey("TP_UNI2")) {
|
||||
backend = new com.github.unidbg.arm.backend.Unicorn2Factory(true);
|
||||
} else {
|
||||
backend = new com.github.unidbg.arm.backend.DynarmicFactory(false);
|
||||
}
|
||||
} catch (Throwable t) { backend = new com.github.unidbg.arm.backend.Unicorn2Factory(true); }
|
||||
emulator = AndroidEmulatorBuilder.for64Bit()
|
||||
.setProcessName("com.duowan.kiwi")
|
||||
@@ -113,8 +117,38 @@ public class TuringProbe {
|
||||
switch (which) {
|
||||
case "ga": {
|
||||
DalvikModule dm = vm.loadLibrary(new File(base + "turingga.so"), false);
|
||||
com.github.unidbg.Module gm = dm.getModule();
|
||||
// ga 的 GOT 页: LOAD2 vaddr 0x48720 (页0x48000), 补 0x48000-0x48720 间隙
|
||||
try {
|
||||
long ggap = gm.base + 0x48000;
|
||||
emulator.getMemory().mmap2(ggap, 0x720, 7, 0x32, -1, 0);
|
||||
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, ggap).setMemory(0, 0x720, (byte) 0);
|
||||
System.out.println("[TP] ga gap mapped @" + Long.toHexString(ggap));
|
||||
} catch (Throwable t2) { System.out.println("[TP] ga gap EXC " + t2); }
|
||||
// ga 的 JNI_OnLoad 日志桩区 (先试 0xca5c=log 调用)
|
||||
try {
|
||||
for (long stubOf : new long[]{0xca5c}) {
|
||||
com.github.unidbg.pointer.UnidbgPointer lp = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, gm.base + stubOf);
|
||||
lp.setInt(0, 0x52800000);
|
||||
lp.setInt(4, 0xd65f03c0);
|
||||
}
|
||||
System.out.println("[TP] ga log stash ok");
|
||||
} catch (Throwable t3) { System.out.println("[TP] ga stash EXC " + t3); }
|
||||
try {
|
||||
dm.callJNI_OnLoad(emulator);
|
||||
System.out.println("[TP] libturingga JNI_OnLoad OK");
|
||||
} catch (Throwable t4) { System.out.println("[TP] ga onLoad EXC: " + t4); regDump(); }
|
||||
// 读注册表 @0x4d1a0 (14 项)
|
||||
try {
|
||||
for (int i = 0; i < 14; i++) {
|
||||
com.github.unidbg.pointer.UnidbgPointer cell = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, gm.base + 0x4d1a0 + i * 24);
|
||||
long nptr = cell.getLong(0), sptr = cell.getLong(8), fptr = cell.getLong(16);
|
||||
String name = "?", sig = "?";
|
||||
if (nptr != 0) { try { name = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, nptr).getString(0); } catch (Throwable t) {} }
|
||||
if (sptr != 0) { try { sig = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, sptr).getString(0); } catch (Throwable t) {} }
|
||||
System.out.println("[TP] ga-m[" + i + "] " + name + " " + sig + " -> 0x" + Long.toHexString(fptr));
|
||||
}
|
||||
} catch (Throwable t5) { System.out.println("[TP] ga table read EXC " + t5); }
|
||||
break;
|
||||
}
|
||||
case "mfa": {
|
||||
@@ -283,15 +317,24 @@ public class TuringProbe {
|
||||
com.github.unidbg.Module m = dm.getModule();
|
||||
long gapBase = m.base + 0x45000;
|
||||
try { emulator.getMemory().mmap2(gapBase, 0x648, 7, 0x32, -1, 0); } catch (Throwable t) {}
|
||||
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
|
||||
boolean enc = blobPath.startsWith("ENC:");
|
||||
if (enc) blobPath = blobPath.substring(4);
|
||||
// 支持 offset 参数: blobPath 可含 ":off"
|
||||
int off = 0;
|
||||
if (blobPath.contains(":")) {
|
||||
String[] sp = blobPath.split(":");
|
||||
blobPath = sp[0]; off = Integer.parseInt(sp[1]);
|
||||
}
|
||||
byte[] blobAll = new java.io.FileInputStream(blobPath).readAllBytes();
|
||||
byte[] blob = java.util.Arrays.copyOfRange(blobAll, off, blobAll.length);
|
||||
// 密文词数
|
||||
int n = blob.length / 4;
|
||||
com.github.unidbg.pointer.UnidbgPointer vp = emulator.getMemory().mmap(blob.length + 32, 7);
|
||||
for (int i=0;i<blob.length;i++) vp.setByte(i, blob[i]);
|
||||
// key @ 3DF88 (LOAD-1 identity fileoff==vaddr)
|
||||
long keyPtr = m.base + 0x3DF88;
|
||||
System.out.println("[TP] xtest calling sub_24D58 n=" + n + " key@" + Long.toHexString(keyPtr));
|
||||
Number ret = m.callFunction(emulator, 0x24D58, vp.peer, -n, keyPtr);
|
||||
System.out.println("[TP] xtest " + (enc ? "ENC" : "DEC") + " sub_24D58 n=" + n + " key@" + Long.toHexString(keyPtr));
|
||||
Number ret = m.callFunction(emulator, 0x24D58, vp.peer, enc ? n : -n, keyPtr);
|
||||
System.out.println("[TP] xtest ret=" + ret);
|
||||
byte[] out = vp.getByteArray(0, Math.min(blob.length, 64));
|
||||
System.out.println("[TP] xtest out head: " + java.util.HexFormat.of().formatHex(out));
|
||||
@@ -307,10 +350,113 @@ public class TuringProbe {
|
||||
}
|
||||
}
|
||||
|
||||
/** etest: 直调 sub_2E250 完整封套解析 (XXTEA + len 提取) */
|
||||
void etest(String blobPath) {
|
||||
try {
|
||||
DalvikModule dm = vm.loadLibrary(new File("/Users/yml/codes/douyu_login_py/evidence/dfp_live/libturingmfa.so"), false);
|
||||
com.github.unidbg.Module m = dm.getModule();
|
||||
long gapBase = m.base + 0x45000;
|
||||
try { emulator.getMemory().mmap2(gapBase, 0x648, 7, 0x32, -1, 0); } catch (Throwable t) {}
|
||||
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
|
||||
com.github.unidbg.pointer.UnidbgPointer inPtr = emulator.getMemory().mmap(blob.length + 32, 7);
|
||||
for (int i=0;i<blob.length;i++) inPtr.setByte(i, blob[i]);
|
||||
com.github.unidbg.pointer.UnidbgPointer outSlot = emulator.getMemory().mmap(16, 7);
|
||||
com.github.unidbg.pointer.UnidbgPointer lenSlot = emulator.getMemory().mmap(16, 7);
|
||||
long keyPtr = m.base + 0x3DF88;
|
||||
System.out.println("[TP] etest sub_2E250 in=" + blob.length + "B key@" + Long.toHexString(keyPtr));
|
||||
Number ret = m.callFunction(emulator, 0x2E250, inPtr.peer, blob.length, outSlot.peer, lenSlot.peer, keyPtr);
|
||||
System.out.println("[TP] etest ret=" + ret);
|
||||
long outAddr = outSlot.getLong(0);
|
||||
long outLen = lenSlot.getLong(0);
|
||||
System.out.println("[TP] etest out=" + Long.toHexString(outAddr) + " len=" + outLen);
|
||||
if (outAddr != 0 && outLen > 0 && outLen < 200000) {
|
||||
byte[] out = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, outAddr).getByteArray(0, (int) outLen);
|
||||
System.out.println("[TP] etest plain head: " + java.util.HexFormat.of().formatHex(java.util.Arrays.copyOf(out, Math.min(out.length, 64))));
|
||||
// 尝试 inflate (sub_34A00 等同)
|
||||
java.util.zip.Inflater inf = new java.util.zip.Inflater();
|
||||
inf.setInput(out);
|
||||
byte[] buf = new byte[200000];
|
||||
int got = inf.inflate(buf);
|
||||
System.out.println("[TP] etest inflate got=" + got + " head=" + java.util.HexFormat.of().formatHex(java.util.Arrays.copyOf(buf, Math.min(got, 48))));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
System.out.println("[TP] etest EXC " + t);
|
||||
t.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
/** gacall: ga 注册原生直调 — k209202([B)[B @0x219d8 */
|
||||
void gacall(String blobPath) {
|
||||
try {
|
||||
DalvikModule dm = vm.loadLibrary(new File("/Users/yml/codes/douyu_login_py/evidence/dfp_live/turingga.so"), false);
|
||||
com.github.unidbg.Module gm = dm.getModule();
|
||||
try {
|
||||
long ggap = gm.base + 0x48000;
|
||||
emulator.getMemory().mmap2(ggap, 0x720, 7, 0x32, -1, 0);
|
||||
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, ggap).setMemory(0, 0x720, (byte) 0);
|
||||
} catch (Throwable t) {}
|
||||
// 注册 (onLoad 部分成功)
|
||||
try { dm.callJNI_OnLoad(emulator); } catch (Throwable t) { System.out.println("[TP] ga onLoad EXC " + t); }
|
||||
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
|
||||
DvmClass gaCls = vm.resolveClass("com/tencent/turingfd/sdk/ams/ga/TNative$aa");
|
||||
DvmObject<?> byteArg = vm.resolveClass("[B").newObject(blob);
|
||||
System.out.println("[TP] gacall k209202 in=" + blob.length + "B");
|
||||
DvmObject<?> ret = gaCls.callStaticJniMethodObject(emulator, "k209202_2A742FA224BA8A2A", byteArg);
|
||||
if (ret != null && ret.getValue() instanceof byte[]) {
|
||||
byte[] out = (byte[]) ret.getValue();
|
||||
System.out.println("[TP] gacall out=" + out.length + "B head=" + java.util.HexFormat.of().formatHex(java.util.Arrays.copyOf(out, Math.min(out.length, 48))));
|
||||
java.io.FileOutputStream fos = new java.io.FileOutputStream("/tmp/ga_k_out.bin"); fos.write(out); fos.close();
|
||||
} else {
|
||||
System.out.println("[TP] gacall ret=" + ret);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
System.out.println("[TP] gacall EXC " + t);
|
||||
t.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
/** gacall2: 直调 ga k209202@0x219d8 (JNI C 参数 + 对象 hash) */
|
||||
void gacall2(String blobPath) {
|
||||
try {
|
||||
DalvikModule dm = vm.loadLibrary(new File("/Users/yml/codes/douyu_login_py/evidence/dfp_live/turingga.so"), false);
|
||||
com.github.unidbg.Module gm = dm.getModule();
|
||||
try {
|
||||
long ggap = gm.base + 0x48000;
|
||||
emulator.getMemory().mmap2(ggap, 0x720, 7, 0x32, -1, 0);
|
||||
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, ggap).setMemory(0, 0x720, (byte) 0);
|
||||
} catch (Throwable t) {}
|
||||
try { dm.callJNI_OnLoad(emulator); } catch (Throwable t) {}
|
||||
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
|
||||
DvmObject<?> byteObj = vm.resolveClass("[B").newObject(blob);
|
||||
long env = ((com.github.unidbg.pointer.UnidbgPointer) vm.getJNIEnv()).peer;
|
||||
long fn = gm.base + 0x219d8;
|
||||
System.out.println("[TP] gacall2 k209202@" + Long.toHexString(fn) + " in=" + blob.length);
|
||||
Number ret = gm.callFunction(emulator, 0x219d8, env, 0, byteObj.hashCode());
|
||||
System.out.println("[TP] gacall2 ret-hash=" + ret);
|
||||
try {
|
||||
DvmObject<?> out = vm.getObject(ret.intValue());
|
||||
Object v = out.getValue();
|
||||
if (v instanceof byte[]) {
|
||||
byte[] outb = (byte[]) v;
|
||||
System.out.println("[TP] gacall2 out=" + outb.length + "B head=" + java.util.HexFormat.of().formatHex(java.util.Arrays.copyOf(outb, Math.min(outb.length, 48))));
|
||||
java.io.FileOutputStream fos = new java.io.FileOutputStream("/tmp/ga_k_out.bin"); fos.write(outb); fos.close();
|
||||
} else {
|
||||
System.out.println("[TP] gacall2 out-val=" + v);
|
||||
}
|
||||
} catch (Throwable t) { System.out.println("[TP] gacall2 out-EXC " + t); }
|
||||
} catch (Throwable t) {
|
||||
System.out.println("[TP] gacall2 EXC " + t);
|
||||
t.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
TuringProbe p = new TuringProbe();
|
||||
String which = args.length > 0 ? args[0] : "both";
|
||||
p.preResolveClasses();
|
||||
if (which.equals("gacall2")) { p.gacall2(args[1]); return; }
|
||||
if (which.equals("gacall")) { p.gacall(args[1]); return; }
|
||||
if (which.equals("etest")) { p.etest(args[1]); return; }
|
||||
if (which.equals("xtest")) { p.xtest(args[1], args.length > 2 ? args[2] : null); return; }
|
||||
if (which.equals("call")) {
|
||||
p.callEnc(args.length > 1 ? args[1] : "/tmp/dfp_str2.bin");
|
||||
|
||||
Reference in New Issue
Block a user