feat(huya): 💎 JAVA侧全逆! Cprivate XXTEA+会话密钥架构+Lyra TLV格式 (R30)

- Cprivate.java 加密/解密器全文 (XXTEA+MD5密钥变换+尾字长度)
- 密钥=SecureRandom 16字符[A-Za-z0-9] (guava.c内存明文! RSA封装上送)
- Lyra TLV: (type<<4)|tag, type 0-13 全表
- 捕获密文可用 Cprivate.a(data,会话key) 解密 - key在内存!
- 下一步: frida抓guava.c → 真key解密明文
This commit is contained in:
yml2213
2026-08-29 08:12:10 +08:00
parent 9586eaa8a3
commit 5a138376ca
10 changed files with 193 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
cd /Users/yml/codes/douyu_login_py/tools/frida
CAP_HOOK=hook_b209202_args.js CAP_HOOK_DELAY=8 CAP_WINDOW=150 CAP_MAX_ATTEMPTS=3 CAP_OUT_DIR=/tmp/b2cap python3 run_capture.py 2>&1 | tail -40
+50
View File
@@ -0,0 +1,50 @@
// hook ga 的 b209202@0x20110 + k209202@0x219d8 — 抓 JNI 实参 (moderate - 只有 2 个 attach!)
function pad(h, n){ h=h||'0'; return h.length>=n?h:('0'.repeat(n-h.length)+h); }
setTimeout(function(){
var mod = Process.findModuleByName('libturingga.so');
if (!mod) { console.log('[B2] no ga module'); return; }
var base = mod.base;
var hooks = [
{name:'b209202', off:0x20110, nargs:7},
{name:'k209202', off:0x219d8, nargs:4},
];
hooks.forEach(function(h){
var addr = base.add(h.off);
try {
Interceptor.attach(addr, {
onEnter: function(args){
this.t = Date.now();
var regs = 'a0=' + pad(args[0].toString(16)) + ' a1=' + pad(args[1].toString(16));
for (var i=h.nargs-2; i<h.nargs; i++) regs += ' a' + i + '=' + pad(args[i].toString(16));
console.log('[B2] ' + h.name + '.' + h.off.toString(16) + ' enter ' + regs);
},
onLeave: function(ret){
console.log('[B2] ' + h.name + '.' + h.off.toString(16) + ' leave -> ' + pad(ret.toString(16)) + ' dt=' + (Date.now()-this.t) + 'ms');
}
});
console.log('[B2] hooked ' + h.name + ' @' + h.off.toString(16));
} catch(e) { console.log('[B2] hook ' + h.name + ' FAIL ' + e); }
});
// 监听 DFPService capture (同 hook_str_magic 的过滤)
var ssl = Process.findModuleByName('libssl.so');
if (ssl) {
var f = Module.findExportByName('libssl.so', 'SSL_write');
if (f) {
Interceptor.attach(f, {
onEnter: function(args){
try {
var len = args[2].toInt32();
if (len < 50 || len > 60000) return;
var b = args[1].readByteArray(Math.min(len, 300));
var s = Array.from(new Uint8Array(b)).map(function(x){return ('0'+x.toString(16)).slice(-2)}).join('');
if (s.indexOf('7cda') >= 0 || s.indexOf('dfpReport') >= 0 || s.indexOf('hyudbwebuif') >= 0) {
console.log('[B2] SSL_write len=' + len);
}
} catch(e){}
}
});
console.log('[B2] ssl-write hook ok');
}
}
}, 1000);
+22
View File
@@ -0,0 +1,22 @@
// 轻量 Java hook: Cprivate.b(byte[] data, byte[] key) — 抓明文+密钥 (单点 hook, 不扫内存!)
setTimeout(function(){
try {
Java.perform(function(){
try {
var Cpr = Java.use('com.tencent.turingfd.sdk.ams.ga.Cprivate');
var Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon');
Cpr.b.overload('[B','[B').implementation = function(a, b){
try {
var plainHex = Melon.a(a); // a() = byte[] -> hex? (Melon.a(byte[]) = hex!)
var keyHex = Melon.a(b);
console.log('[CPR] Cprivate.b data-len=' + a.length + ' key-len=' + b.length);
console.log('[CPR] key=' + keyHex);
console.log('[CPR] data-head=' + (plainHex ? plainHex.substring(0, 160) : 'null'));
} catch(e) { console.log('[CPR] dump-exc ' + e); }
return this.b(a, b);
};
console.log('[CPR] hooked Cprivate.b');
} catch(e2) { console.log('[CPR] no Cprivate: ' + e2); }
});
} catch(e) { console.log('[CPR] perm-exc ' + e); }
}, 1500);
@@ -351,6 +351,88 @@ public class TuringProbe {
}
}
/** kchain: 拆分 k209202 链 — 19378对象 + 24334状态 + Java-deflate + 1F918 XTEA(max=16B输入) */
void kchain(String keyPath) {
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 { emulator.getMemory().mmap2(gm.base + 0x48000, 0x720, 7, 0x32, -1, 0); } catch (Throwable t) {}
patchGaImports(gm);
try { dm.callJNI_OnLoad(emulator); } catch (Throwable t) {}
byte[] key = new java.io.FileInputStream(keyPath).readAllBytes();
// 1) sub_19378 对象
Number obj = gm.callFunction(emulator, 0x19378);
System.out.println("[TP] kchain obj=" + obj);
// 2) sub_24334(obj, &ptr, &len)
com.github.unidbg.pointer.UnidbgPointer outSlot = emulator.getMemory().mmap(16, 7);
com.github.unidbg.pointer.UnidbgPointer lenSlot = emulator.getMemory().mmap(16, 7);
Number r2 = gm.callFunction(emulator, 0x24334, obj.longValue(), outSlot.peer, lenSlot.peer);
long statePtr = outSlot.getLong(0); long stateLen = lenSlot.getLong(0);
System.out.println("[TP] kchain 24334 ret=" + r2 + " state=" + stateLen);
if (statePtr != 0 && stateLen > 0 && stateLen < 100000) {
byte[] state = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, statePtr).getByteArray(0, (int) stateLen);
System.out.println("[TP] kchain state head=" + java.util.HexFormat.of().formatHex(java.util.Arrays.copyOf(state, Math.min(state.length, 32))));
// 3) Java deflate (raw ~ sub_2E904)
java.util.zip.Deflater def2 = new java.util.zip.Deflater(6, true);
def2.setInput(state); def2.finish();
byte[] zbuf = new byte[state.length * 2 + 64];
int zn = def2.deflate(zbuf);
byte[] z = java.util.Arrays.copyOf(zbuf, zn);
System.out.println("[TP] kchain deflate " + stateLen + "->" + zn);
// 4) 真 sub_1F918 XTEA-wrap (key = 16B 输入!)
com.github.unidbg.pointer.UnidbgPointer inPtr = emulator.getMemory().mmap(z.length + 32, 7);
for (int i=0;i<z.length;i++) inPtr.setByte(i, z[i]);
com.github.unidbg.pointer.UnidbgPointer kp = emulator.getMemory().mmap(32, 7);
for (int i=0;i<16;i++) kp.setByte(i, key[i]);
com.github.unidbg.pointer.UnidbgPointer o2s = emulator.getMemory().mmap(16, 7);
com.github.unidbg.pointer.UnidbgPointer l2s = emulator.getMemory().mmap(16, 7);
Number r3 = gm.callFunction(emulator, 0x1F918, inPtr.peer, z.length, o2s.peer, l2s.peer, kp.peer);
long co = o2s.getLong(0); long cl = l2s.getLong(0);
System.out.println("[TP] kchain 1F918 ret=" + r3 + " cipher=" + cl);
if (co != 0) {
byte[] c = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, co).getByteArray(0, (int) cl);
java.io.FileOutputStream fos = new java.io.FileOutputStream("/tmp/kchain_out.bin"); fos.write(c); fos.close();
System.out.println("[TP] kchain cipher head=" + java.util.HexFormat.of().formatHex(java.util.Arrays.copyOf(c, Math.min(c.length, 24))));
}
}
} catch (Throwable t) {
System.out.println("[TP] kchain EXC " + t);
t.printStackTrace(System.out);
}
}
/** gacallb: 直调 ga b209202@0x20110 (a6=0 编码!) */
void gacallb(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 { emulator.getMemory().mmap2(gm.base + 0x48000, 0x720, 7, 0x32, -1, 0); } catch (Throwable t) {}
patchGaImports(gm);
try { dm.callJNI_OnLoad(emulator); } catch (Throwable t) {}
try {
com.github.unidbg.pointer.UnidbgPointer gst = emulator.getMemory().mmap(0x40, 7);
gst.setMemory(0, 0x40, (byte) 0); gst.setInt(0, 1);
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, gm.base + 0x48c18).setPointer(0, gst);
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, gm.base + 0x48c20).setPointer(0, gst);
} catch (Throwable t) {}
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
DvmObject<?> sa = vm.resolveClass("android/util/SparseArray").newObject(null);
DvmObject<?> dba = vm.resolveClass("[B").newObject(blob);
DvmObject<?> map = vm.resolveClass("java/util/HashMap").newObject(null);
long env = ((com.github.unidbg.pointer.UnidbgPointer) vm.getJNIEnv()).peer;
int hv = Integer.parseInt(System.getenv().getOrDefault("TP_INT", "0"));
int hsa = vm.addLocalObject(sa);
int hba = vm.addLocalObject(dba);
int hmap = vm.addLocalObject(map);
System.out.println("[TP] gacallb b209202@0x20110 in=" + blob.length + " a6=" + hv);
Number ret = gm.callFunction(emulator, 0x20110, env, 0, hsa, hba, hmap, hv);
System.out.println("[TP] gacallb ret=" + ret);
} catch (Throwable t) {
System.out.println("[TP] gacallb EXC " + t);
t.printStackTrace(System.out);
}
}
/** gaxtest: 直调 ga sub_1F1CC (XTEA) — (buf, ±n, key@5788) */
void gaxtest(String blobPath) {
try {
@@ -635,6 +717,8 @@ public class TuringProbe {
TuringProbe p = new TuringProbe();
String which = args.length > 0 ? args[0] : "both";
p.preResolveClasses();
if (which.equals("kchain")) { p.kchain(args[1]); return; }
if (which.equals("gacallb")) { p.gacallb(args[1]); return; }
if (which.equals("gaxtest")) { p.gaxtest(args[1]); return; }
if (which.equals("etest3")) { p.etest3(args[1]); return; }
if (which.equals("etest2")) { p.etest2(args[1]); return; }