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:
Executable
+3
@@ -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
|
||||
@@ -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);
|
||||
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user