feat(huya): 💎💎 R31 活体重抓3密文+Java全逆+native零调用铁证
- 配方: frida-server -l 0.0.0.0 + 清状态(保留登录) + str_magic → 40s命中 - 3新POST (4384/4267/4261B, cipher@70) 存 evidence/dfp_live/ - Java Cprivate XXTEA + Perseus 会话密钥 + Lyra TLV 全解 - 报告加密 = 第三路径 (JNI族+XTEA核心+Java三重钩子全0调用!) - cprivate.py 解密器就绪 (key一到即解)
This commit is contained in:
@@ -1480,3 +1480,27 @@ full: 7c da | 11 | 69 c9 f5 28 9c 4d 46 | 95 6f 21 | 10 bb 80
|
||||
1. frida 抓 guava.c (16字符) — Java 层单点 hook (Perseus.a 本地串 / Guava 实例)
|
||||
2. 用真 key 跑 Cprivate.a(捕获密文) = 明文事件!
|
||||
3. Lynx 全解码器 (13B 头语义 + 帧结构)
|
||||
|
||||
## §11.62 R31: 💎💎💎 活体重抓 3 密文 + Java 全逆确认 + native 全程零调用 (2026-08-29)
|
||||
|
||||
### 配方复现 (用户协助)
|
||||
- frida-server 必需 `-l 0.0.0.0:31878` (否则连接closed!)
|
||||
- 触发 = 只清 turing 状态不 pm-clear (保留登录!) + spawn + hook_str_magic → ~40s 自动重注册上报! (用户"退出重新登录不触发" = 登录态在)
|
||||
- 3 个全新 POST: 4384B(cipher 4117@70) / 4267B(4000@70) / 4261B 同刻捕获!
|
||||
|
||||
### Java 侧全逆 (jadx classes7.dex — 与 native 同构确认)
|
||||
- Cprivate(实类名 ga.private!) = XXTEA: b(enc)/a(dec), key>16B→MD5, 尾字=明文长
|
||||
- Perseus.a() = 会话: SecureRandom 16字符[A-Za-z0-9] → RSA(MIIB...公钥)封装 → guava{seq,env,KEY,d,expire}!
|
||||
- Lyra/Lynx TLV: 帧头=(type<<4)|tag, type 0-13 (0=1B 1=2B 2=4B 3=8B 6=str 7=bytes 8/9=集合 10=map 11=END)
|
||||
- 请求 = Cthrow{Csuper[]} → Octans.a(Lyra帧) → Cextends.a(deflate) → Cprivate.b(data, 16字符UTF8字节)!
|
||||
|
||||
### native 零调用铁证 (同一活体会话!)
|
||||
- xtea-hooked mfa@24D58 + ga@1F1CC ✓ 但 0 次 xtea-got (报告期内核从未被调!)
|
||||
- nat b209202@20110 + k209202@219d8 hooked ✓ 0 次 nat-ent
|
||||
- Java Cprivate.b/Perseus.a hooked ✓ 0 次调用
|
||||
- → 报告加密在 turing 的第三路径 (非 JNI 方法族!): 需hook已知 JNI impl (mfa b87_@25c98 / ga 27DBC 解码族!) 或 native 内部更多偏移
|
||||
|
||||
### 下一步 (R32)
|
||||
1. hook mfa b87_@25c98/27DA4 + ga 27DBC/23DF8 (JNI impl 直挂!) — 关键参数 (byte[], map, mode!)
|
||||
2. 或: mfa 动态符号/内部调用大范围探 (OLLVM 静态已尽)
|
||||
3. 解密器待 key: cprivate.py (Cprivate.a 全等直译) 已就绪 — key 一到即解 3 个新密文!
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
// 双钩 (send() 事件格式!): Cprivate.b(明文+密钥!) + Perseus.a 会话密钥
|
||||
setTimeout(function(){
|
||||
function S(o){ try { send(o); } catch(e){} }
|
||||
try {
|
||||
Java.perform(function(){
|
||||
var hooked = 0;
|
||||
try {
|
||||
var Cpr = null, Melon = null;
|
||||
Java.enumerateClassLoaders({
|
||||
onMatch: function(loader){
|
||||
try {
|
||||
Java.classFactory.loader = loader;
|
||||
var c = Java.use('com.tencent.turingfd.sdk.ams.ga.private');
|
||||
if (c) { Cpr = c; Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
} catch(e){}
|
||||
},
|
||||
onComplete: function(){}
|
||||
});
|
||||
if (!Cpr) { Cpr = Java.use('com.tencent.turingfd.sdk.ams.ga.private'); Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
var Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon');
|
||||
Cpr.b.overload('[B','[B').implementation = function(a, b){
|
||||
var out = this.b(a, b);
|
||||
try {
|
||||
S({event:'cpr-b', dlen:a.length, klen:b.length,
|
||||
key:Melon.a(b), dhead:(Melon.a(a)||'').substring(0,300)});
|
||||
} catch(e){ S({event:'cpr-err', e:String(e)}); }
|
||||
return out;
|
||||
};
|
||||
hooked++;
|
||||
} catch(e){ S({event:'cpr-nohook', e:String(e).substring(0,120)}); }
|
||||
try {
|
||||
var Perseus = Java.use('com.tencent.turingfd.sdk.ams.ga.Perseus');
|
||||
var Guava = Java.use('com.tencent.turingfd.sdk.ams.ga.Guava');
|
||||
Perseus.a.implementation = function(){
|
||||
this.a();
|
||||
try {
|
||||
var g = this.b.value;
|
||||
if (g) S({event:'perseus-key', c:g.c.value, b1:g.a.value, blen: g.b.value ? g.b.value.length : -1});
|
||||
} catch(e){}
|
||||
};
|
||||
hooked++;
|
||||
} catch(e2){ S({event:'cpr-noperseus', e:String(e2).substring(0,120)}); }
|
||||
S({event:'cpr-installed', hooked:hooked});
|
||||
});
|
||||
} catch(e) { S({event:'cpr-perm', e:String(e).substring(0,120)}); }
|
||||
}, 1200);
|
||||
|
||||
// 兜底: SSL_write 抓 dfpReport 密文 (串过滤!)
|
||||
try {
|
||||
var sslw = Module.findExportByName('libssl.so', 'SSL_write');
|
||||
if (sslw) {
|
||||
Interceptor.attach(sslw, {
|
||||
onEnter: function(args){
|
||||
try {
|
||||
var len = args[2].toInt32();
|
||||
if (len < 50 || len > 60000) return;
|
||||
var b = args[1].readByteArray(Math.min(len, 2000));
|
||||
var u = new Uint8Array(b);
|
||||
var s = '';
|
||||
for (var i=0;i<u.length;i++) s += String.fromCharCode(u[i]);
|
||||
if (s.indexOf('dfpReport') >= 0 || u.length>10 && (u[0]==0x7c || u[1]==0xda)) {
|
||||
var hex = Array.prototype.map.call(u, function(x){return ('0'+x.toString(16)).slice(-2)}).join('');
|
||||
send({event:'ssl-dfp', len:len, head:hex.substring(0,120)});
|
||||
}
|
||||
} catch(e){}
|
||||
}
|
||||
});
|
||||
send({event:'ssl-hooked'});
|
||||
}
|
||||
} catch(e) { send({event:'ssl-fail', e:String(e).substring(0,100)}); }
|
||||
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
function emit(row) { try { send(row); } catch (e) {} }
|
||||
function btOk(ctx) { try { return Thread.backtrace(ctx, Backtracer.ACCURATE).slice(0, 40).map(a => { const m = Process.findModuleByAddress(a); return m ? m.name + '!' + a.sub(m.base) : a.toString(); }).join(' <- '); } catch (e) { return ''; } }
|
||||
let fired = false, writes = 0;
|
||||
for (const mod of Process.enumerateModules()) {
|
||||
let addr = null;
|
||||
try { addr = mod.getExportByName('SSL_write'); } catch (e) {}
|
||||
if (addr === null) continue;
|
||||
Interceptor.attach(addr, {
|
||||
onEnter(args) {
|
||||
writes++;
|
||||
if (fired) return;
|
||||
if (writes % 40 === 0) emit({ event: 'ssl-heartbeat', module: mod.name, writes });
|
||||
const buf = args[1], num = args[2].toInt32();
|
||||
if (num < 60) return;
|
||||
try {
|
||||
const u = new Uint8Array(buf.readByteArray(Math.min(num, 30000)));
|
||||
// 编码成可检索串 (简化: 只查 ascii 子串)
|
||||
let s = '';
|
||||
for (let i = 0; i < u.length; i++) s += (u[i] >= 32 && u[i] < 127) ? String.fromCharCode(u[i]) : '.';
|
||||
if (s.indexOf('dfpReport') < 0 && s.indexOf('hyudbwebuif') < 0) return;
|
||||
fired = true;
|
||||
const hex = Array.from(u).map(x => x.toString(16).padStart(2, '0')).join('');
|
||||
emit({ event: 'str-magic', module: mod.name, num, full: hex, bt: btOk(this.context) });
|
||||
// ---- co-located: 命中后立即在 turing 区域 + 全堆扫第二处魔数/明文特征 ----
|
||||
const MAG = [0x57, 0x18, 0x82, 0xcf, 0x66, 0x4b, 0xb3, 0x94, 0x01, 0xee];
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const regions = [];
|
||||
for (const m of Process.enumerateModules()) {
|
||||
if (/turing|Turin/.test(m.name)) regions.push({ name: m.name, p: m.base, n: Math.min(m.size, 2 * 1024 * 1024) });
|
||||
}
|
||||
// 附加: 加载库里的映射区域 (含 .turing.dat mmap) — 用 Process.enumerateRanges 'r--' 太广, 只挑 map 文件路径含 turing 的
|
||||
for (const r of Process.enumerateRanges('r--')) {
|
||||
try {
|
||||
const f = r.file ? r.file.path : '';
|
||||
if (/turing|\.dat/.test(f) && !/\.so/.test(f)) regions.push({ name: f.slice(-40), p: r.base, n: Math.min(r.size, 512 * 1024) });
|
||||
} catch (e) {}
|
||||
}
|
||||
let nFound = 0;
|
||||
for (const rg of regions) {
|
||||
try {
|
||||
const buf = new Uint8Array(rg.p.readByteArray(rg.n));
|
||||
for (let i = 0; i + 10 < rg.n; i++) {
|
||||
let ok = true;
|
||||
for (let j = 0; j < 10; j++) if (buf[i+j] !== MAG[j]) { ok = false; break; }
|
||||
if (!ok) continue;
|
||||
nFound++;
|
||||
if (nFound > 12) break;
|
||||
// 邻域 ASCII 提取
|
||||
const as = [];
|
||||
for (let k = -128; k < 1024; k += 64) {
|
||||
try {
|
||||
const c = Array.from(new Uint8Array(rg.p.add(i + k).readByteArray(64)));
|
||||
const str = c.map(x => (x >= 32 && x < 127) ? String.fromCharCode(x) : '.').join('');
|
||||
if (/[A-Za-z0-9_\-\.\/:]{10,}/.test(str)) as.push({ k, s: str });
|
||||
} catch (e) {}
|
||||
}
|
||||
emit({ event: 'co-magic', mod: rg.name, off: '0x' + i.toString(16), ascii: as.slice(0, 10) });
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
emit({ event: 'co-done', n: nFound, regions: regions.length });
|
||||
} catch (e) { emit({ event: 'co-err', msg: String(e).slice(0, 120) }); }
|
||||
}, 0);
|
||||
} catch (e) {}
|
||||
},
|
||||
});
|
||||
emit({ event: 'ssl-hooked', module: mod.name });
|
||||
}
|
||||
|
||||
// ===== 附加: Java 密钥钩子 (Cprivate.b 明文+密钥 / Perseus.a 会话密钥) =====
|
||||
setTimeout(function(){
|
||||
function S(o){ try { send(o); } catch(e){} }
|
||||
try {
|
||||
Java.perform(function(){
|
||||
var hooked = 0;
|
||||
try {
|
||||
var Cpr = null, Melon = null;
|
||||
Java.enumerateClassLoaders({
|
||||
onMatch: function(loader){
|
||||
try {
|
||||
Java.classFactory.loader = loader;
|
||||
var c = Java.use('com.tencent.turingfd.sdk.ams.ga.private');
|
||||
if (c) { Cpr = c; Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
} catch(e){}
|
||||
},
|
||||
onComplete: function(){}
|
||||
});
|
||||
if (!Cpr) { Cpr = Java.use('com.tencent.turingfd.sdk.ams.ga.private'); Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
var Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon');
|
||||
Cpr.b.overload('[B','[B').implementation = function(a, b){
|
||||
var out = this.b(a, b);
|
||||
try {
|
||||
S({event:'cpr-b', dlen:a.length, klen:b.length,
|
||||
key:Melon.a(b), dhead:(Melon.a(a)||'').substring(0,300)});
|
||||
} catch(e){ S({event:'cpr-err', e:String(e)}); }
|
||||
return out;
|
||||
};
|
||||
hooked++;
|
||||
} catch(e){ S({event:'cpr-nohook', e:String(e).substring(0,120)}); }
|
||||
try {
|
||||
var Perseus = Java.use('com.tencent.turingfd.sdk.ams.ga.Perseus');
|
||||
var Guava = Java.use('com.tencent.turingfd.sdk.ams.ga.Guava');
|
||||
Perseus.a.implementation = function(){
|
||||
this.a();
|
||||
try {
|
||||
var g = this.b.value;
|
||||
if (g) S({event:'perseus-key', c:g.c.value, b1:g.a.value, blen: g.b.value ? g.b.value.length : -1});
|
||||
} catch(e){}
|
||||
};
|
||||
hooked++;
|
||||
} catch(e2){ S({event:'cpr-noperseus', e:String(e2).substring(0,120)}); }
|
||||
S({event:'cpr-installed', hooked:hooked});
|
||||
});
|
||||
} catch(e) { S({event:'cpr-perm', e:String(e).substring(0,120)}); }
|
||||
}, 1500);
|
||||
@@ -0,0 +1,145 @@
|
||||
'use strict';
|
||||
function emit(row) { try { send(row); } catch (e) {} }
|
||||
function btOk(ctx) { try { return Thread.backtrace(ctx, Backtracer.ACCURATE).slice(0, 40).map(a => { const m = Process.findModuleByAddress(a); return m ? m.name + '!' + a.sub(m.base) : a.toString(); }).join(' <- '); } catch (e) { return ''; } }
|
||||
let fired = false, writes = 0;
|
||||
for (const mod of Process.enumerateModules()) {
|
||||
let addr = null;
|
||||
try { addr = mod.getExportByName('SSL_write'); } catch (e) {}
|
||||
if (addr === null) continue;
|
||||
Interceptor.attach(addr, {
|
||||
onEnter(args) {
|
||||
writes++;
|
||||
if (fired) return;
|
||||
if (writes % 40 === 0) emit({ event: 'ssl-heartbeat', module: mod.name, writes });
|
||||
const buf = args[1], num = args[2].toInt32();
|
||||
if (num < 60) return;
|
||||
try {
|
||||
const u = new Uint8Array(buf.readByteArray(Math.min(num, 30000)));
|
||||
// 编码成可检索串 (简化: 只查 ascii 子串)
|
||||
let s = '';
|
||||
for (let i = 0; i < u.length; i++) s += (u[i] >= 32 && u[i] < 127) ? String.fromCharCode(u[i]) : '.';
|
||||
if (s.indexOf('dfpReport') < 0 && s.indexOf('hyudbwebuif') < 0) return;
|
||||
fired = true;
|
||||
const hex = Array.from(u).map(x => x.toString(16).padStart(2, '0')).join('');
|
||||
emit({ event: 'str-magic', module: mod.name, num, full: hex, bt: btOk(this.context) });
|
||||
// ---- co-located: 命中后立即在 turing 区域 + 全堆扫第二处魔数/明文特征 ----
|
||||
const MAG = [0x57, 0x18, 0x82, 0xcf, 0x66, 0x4b, 0xb3, 0x94, 0x01, 0xee];
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const regions = [];
|
||||
for (const m of Process.enumerateModules()) {
|
||||
if (/turing|Turin/.test(m.name)) regions.push({ name: m.name, p: m.base, n: Math.min(m.size, 2 * 1024 * 1024) });
|
||||
}
|
||||
// 附加: 加载库里的映射区域 (含 .turing.dat mmap) — 用 Process.enumerateRanges 'r--' 太广, 只挑 map 文件路径含 turing 的
|
||||
for (const r of Process.enumerateRanges('r--')) {
|
||||
try {
|
||||
const f = r.file ? r.file.path : '';
|
||||
if (/turing|\.dat/.test(f) && !/\.so/.test(f)) regions.push({ name: f.slice(-40), p: r.base, n: Math.min(r.size, 512 * 1024) });
|
||||
} catch (e) {}
|
||||
}
|
||||
let nFound = 0;
|
||||
for (const rg of regions) {
|
||||
try {
|
||||
const buf = new Uint8Array(rg.p.readByteArray(rg.n));
|
||||
for (let i = 0; i + 10 < rg.n; i++) {
|
||||
let ok = true;
|
||||
for (let j = 0; j < 10; j++) if (buf[i+j] !== MAG[j]) { ok = false; break; }
|
||||
if (!ok) continue;
|
||||
nFound++;
|
||||
if (nFound > 12) break;
|
||||
// 邻域 ASCII 提取
|
||||
const as = [];
|
||||
for (let k = -128; k < 1024; k += 64) {
|
||||
try {
|
||||
const c = Array.from(new Uint8Array(rg.p.add(i + k).readByteArray(64)));
|
||||
const str = c.map(x => (x >= 32 && x < 127) ? String.fromCharCode(x) : '.').join('');
|
||||
if (/[A-Za-z0-9_\-\.\/:]{10,}/.test(str)) as.push({ k, s: str });
|
||||
} catch (e) {}
|
||||
}
|
||||
emit({ event: 'co-magic', mod: rg.name, off: '0x' + i.toString(16), ascii: as.slice(0, 10) });
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
emit({ event: 'co-done', n: nFound, regions: regions.length });
|
||||
} catch (e) { emit({ event: 'co-err', msg: String(e).slice(0, 120) }); }
|
||||
}, 0);
|
||||
} catch (e) {}
|
||||
},
|
||||
});
|
||||
emit({ event: 'ssl-hooked', module: mod.name });
|
||||
}
|
||||
|
||||
// ===== 附加: Java 密钥钩子 (Cprivate.b 明文+密钥 / Perseus.a 会话密钥) =====
|
||||
setTimeout(function(){
|
||||
function S(o){ try { send(o); } catch(e){} }
|
||||
try {
|
||||
Java.perform(function(){
|
||||
var hooked = 0;
|
||||
try {
|
||||
var Cpr = null, Melon = null;
|
||||
Java.enumerateClassLoaders({
|
||||
onMatch: function(loader){
|
||||
try {
|
||||
Java.classFactory.loader = loader;
|
||||
var c = Java.use('com.tencent.turingfd.sdk.ams.ga.private');
|
||||
if (c) { Cpr = c; Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
} catch(e){}
|
||||
},
|
||||
onComplete: function(){}
|
||||
});
|
||||
if (!Cpr) { Cpr = Java.use('com.tencent.turingfd.sdk.ams.ga.private'); Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
var Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon');
|
||||
Cpr.b.overload('[B','[B').implementation = function(a, b){
|
||||
var out = this.b(a, b);
|
||||
try {
|
||||
S({event:'cpr-b', dlen:a.length, klen:b.length,
|
||||
key:Melon.a(b), dhead:(Melon.a(a)||'').substring(0,300)});
|
||||
} catch(e){ S({event:'cpr-err', e:String(e)}); }
|
||||
return out;
|
||||
};
|
||||
hooked++;
|
||||
} catch(e){ S({event:'cpr-nohook', e:String(e).substring(0,120)}); }
|
||||
try {
|
||||
var Perseus = Java.use('com.tencent.turingfd.sdk.ams.ga.Perseus');
|
||||
var Guava = Java.use('com.tencent.turingfd.sdk.ams.ga.Guava');
|
||||
Perseus.a.implementation = function(){
|
||||
this.a();
|
||||
try {
|
||||
var g = this.b.value;
|
||||
if (g) S({event:'perseus-key', c:g.c.value, b1:g.a.value, blen: g.b.value ? g.b.value.length : -1});
|
||||
} catch(e){}
|
||||
};
|
||||
hooked++;
|
||||
} catch(e2){ S({event:'cpr-noperseus', e:String(e2).substring(0,120)}); }
|
||||
S({event:'cpr-installed', hooked:hooked});
|
||||
});
|
||||
} catch(e) { S({event:'cpr-perm', e:String(e).substring(0,120)}); }
|
||||
}, 1500);
|
||||
|
||||
// ===== native 实参钩子: ga b209202(0x20110) / k209202(0x219d8) — 抓 16B 块输入! =====
|
||||
setTimeout(function(){
|
||||
try {
|
||||
var mod = Process.findModuleByName('libturingga.so');
|
||||
if (!mod) { send({event:'nat-nomod'}); return; }
|
||||
function hookFn(off, name, nargs){
|
||||
var addr = mod.base.add(off);
|
||||
try {
|
||||
Interceptor.attach(addr, {
|
||||
onEnter: function(args){
|
||||
var regs = '';
|
||||
for (var i=2;i<Math.min(nargs,7);i++) regs += ' a'+i+'='+args[i].toString(16);
|
||||
send({event:'nat-ent', name:name, regs:regs});
|
||||
this.t = Date.now();
|
||||
},
|
||||
onLeave: function(ret){
|
||||
send({event:'nat-ret', name:name, ret:ret.toString(16), dt:Date.now()-this.t});
|
||||
}
|
||||
});
|
||||
} catch(e){ send({event:'nat-fail', name:name, e:String(e).substring(0,80)}); }
|
||||
}
|
||||
hookFn(0x20110, 'b209202', 7);
|
||||
hookFn(0x219d8, 'k209202', 4);
|
||||
hookFn(0x1f918, 'xtea-enc', 5);
|
||||
send({event:'nat-hooked'});
|
||||
} catch(e){ send({event:'nat-exc', e:String(e).substring(0,100)}); }
|
||||
}, 2500);
|
||||
@@ -0,0 +1,179 @@
|
||||
'use strict';
|
||||
function emit(row) { try { send(row); } catch (e) {} }
|
||||
function btOk(ctx) { try { return Thread.backtrace(ctx, Backtracer.ACCURATE).slice(0, 40).map(a => { const m = Process.findModuleByAddress(a); return m ? m.name + '!' + a.sub(m.base) : a.toString(); }).join(' <- '); } catch (e) { return ''; } }
|
||||
let fired = false, writes = 0;
|
||||
for (const mod of Process.enumerateModules()) {
|
||||
let addr = null;
|
||||
try { addr = mod.getExportByName('SSL_write'); } catch (e) {}
|
||||
if (addr === null) continue;
|
||||
Interceptor.attach(addr, {
|
||||
onEnter(args) {
|
||||
writes++;
|
||||
if (fired) return;
|
||||
if (writes % 40 === 0) emit({ event: 'ssl-heartbeat', module: mod.name, writes });
|
||||
const buf = args[1], num = args[2].toInt32();
|
||||
if (num < 60) return;
|
||||
try {
|
||||
const u = new Uint8Array(buf.readByteArray(Math.min(num, 30000)));
|
||||
// 编码成可检索串 (简化: 只查 ascii 子串)
|
||||
let s = '';
|
||||
for (let i = 0; i < u.length; i++) s += (u[i] >= 32 && u[i] < 127) ? String.fromCharCode(u[i]) : '.';
|
||||
if (s.indexOf('dfpReport') < 0 && s.indexOf('hyudbwebuif') < 0) return;
|
||||
fired = true;
|
||||
const hex = Array.from(u).map(x => x.toString(16).padStart(2, '0')).join('');
|
||||
emit({ event: 'str-magic', module: mod.name, num, full: hex, bt: btOk(this.context) });
|
||||
// ---- co-located: 命中后立即在 turing 区域 + 全堆扫第二处魔数/明文特征 ----
|
||||
const MAG = [0x57, 0x18, 0x82, 0xcf, 0x66, 0x4b, 0xb3, 0x94, 0x01, 0xee];
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const regions = [];
|
||||
for (const m of Process.enumerateModules()) {
|
||||
if (/turing|Turin/.test(m.name)) regions.push({ name: m.name, p: m.base, n: Math.min(m.size, 2 * 1024 * 1024) });
|
||||
}
|
||||
// 附加: 加载库里的映射区域 (含 .turing.dat mmap) — 用 Process.enumerateRanges 'r--' 太广, 只挑 map 文件路径含 turing 的
|
||||
for (const r of Process.enumerateRanges('r--')) {
|
||||
try {
|
||||
const f = r.file ? r.file.path : '';
|
||||
if (/turing|\.dat/.test(f) && !/\.so/.test(f)) regions.push({ name: f.slice(-40), p: r.base, n: Math.min(r.size, 512 * 1024) });
|
||||
} catch (e) {}
|
||||
}
|
||||
let nFound = 0;
|
||||
for (const rg of regions) {
|
||||
try {
|
||||
const buf = new Uint8Array(rg.p.readByteArray(rg.n));
|
||||
for (let i = 0; i + 10 < rg.n; i++) {
|
||||
let ok = true;
|
||||
for (let j = 0; j < 10; j++) if (buf[i+j] !== MAG[j]) { ok = false; break; }
|
||||
if (!ok) continue;
|
||||
nFound++;
|
||||
if (nFound > 12) break;
|
||||
// 邻域 ASCII 提取
|
||||
const as = [];
|
||||
for (let k = -128; k < 1024; k += 64) {
|
||||
try {
|
||||
const c = Array.from(new Uint8Array(rg.p.add(i + k).readByteArray(64)));
|
||||
const str = c.map(x => (x >= 32 && x < 127) ? String.fromCharCode(x) : '.').join('');
|
||||
if (/[A-Za-z0-9_\-\.\/:]{10,}/.test(str)) as.push({ k, s: str });
|
||||
} catch (e) {}
|
||||
}
|
||||
emit({ event: 'co-magic', mod: rg.name, off: '0x' + i.toString(16), ascii: as.slice(0, 10) });
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
emit({ event: 'co-done', n: nFound, regions: regions.length });
|
||||
} catch (e) { emit({ event: 'co-err', msg: String(e).slice(0, 120) }); }
|
||||
}, 0);
|
||||
} catch (e) {}
|
||||
},
|
||||
});
|
||||
emit({ event: 'ssl-hooked', module: mod.name });
|
||||
}
|
||||
|
||||
// ===== 附加: Java 密钥钩子 (Cprivate.b 明文+密钥 / Perseus.a 会话密钥) =====
|
||||
setTimeout(function(){
|
||||
function S(o){ try { send(o); } catch(e){} }
|
||||
try {
|
||||
Java.perform(function(){
|
||||
var hooked = 0;
|
||||
try {
|
||||
var Cpr = null, Melon = null;
|
||||
Java.enumerateClassLoaders({
|
||||
onMatch: function(loader){
|
||||
try {
|
||||
Java.classFactory.loader = loader;
|
||||
var c = Java.use('com.tencent.turingfd.sdk.ams.ga.private');
|
||||
if (c) { Cpr = c; Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
} catch(e){}
|
||||
},
|
||||
onComplete: function(){}
|
||||
});
|
||||
if (!Cpr) { Cpr = Java.use('com.tencent.turingfd.sdk.ams.ga.private'); Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon'); }
|
||||
var Melon = Java.use('com.tencent.turingfd.sdk.ams.ga.Melon');
|
||||
Cpr.b.overload('[B','[B').implementation = function(a, b){
|
||||
var out = this.b(a, b);
|
||||
try {
|
||||
S({event:'cpr-b', dlen:a.length, klen:b.length,
|
||||
key:Melon.a(b), dhead:(Melon.a(a)||'').substring(0,300)});
|
||||
} catch(e){ S({event:'cpr-err', e:String(e)}); }
|
||||
return out;
|
||||
};
|
||||
hooked++;
|
||||
} catch(e){ S({event:'cpr-nohook', e:String(e).substring(0,120)}); }
|
||||
try {
|
||||
var Perseus = Java.use('com.tencent.turingfd.sdk.ams.ga.Perseus');
|
||||
var Guava = Java.use('com.tencent.turingfd.sdk.ams.ga.Guava');
|
||||
Perseus.a.implementation = function(){
|
||||
this.a();
|
||||
try {
|
||||
var g = this.b.value;
|
||||
if (g) S({event:'perseus-key', c:g.c.value, b1:g.a.value, blen: g.b.value ? g.b.value.length : -1});
|
||||
} catch(e){}
|
||||
};
|
||||
hooked++;
|
||||
} catch(e2){ S({event:'cpr-noperseus', e:String(e2).substring(0,120)}); }
|
||||
S({event:'cpr-installed', hooked:hooked});
|
||||
});
|
||||
} catch(e) { S({event:'cpr-perm', e:String(e).substring(0,120)}); }
|
||||
}, 1500);
|
||||
|
||||
// ===== native 实参钩子: ga b209202(0x20110) / k209202(0x219d8) — 抓 16B 块输入! =====
|
||||
setTimeout(function(){
|
||||
try {
|
||||
var mod = Process.findModuleByName('libturingga.so');
|
||||
if (!mod) { send({event:'nat-nomod'}); return; }
|
||||
function hookFn(off, name, nargs){
|
||||
var addr = mod.base.add(off);
|
||||
try {
|
||||
Interceptor.attach(addr, {
|
||||
onEnter: function(args){
|
||||
var regs = '';
|
||||
for (var i=2;i<Math.min(nargs,7);i++) regs += ' a'+i+'='+args[i].toString(16);
|
||||
send({event:'nat-ent', name:name, regs:regs});
|
||||
this.t = Date.now();
|
||||
},
|
||||
onLeave: function(ret){
|
||||
send({event:'nat-ret', name:name, ret:ret.toString(16), dt:Date.now()-this.t});
|
||||
}
|
||||
});
|
||||
} catch(e){ send({event:'nat-fail', name:name, e:String(e).substring(0,80)}); }
|
||||
}
|
||||
hookFn(0x20110, 'b209202', 7);
|
||||
hookFn(0x219d8, 'k209202', 4);
|
||||
hookFn(0x1f918, 'xtea-enc', 5);
|
||||
send({event:'nat-hooked'});
|
||||
} catch(e){ send({event:'nat-exc', e:String(e).substring(0,100)}); }
|
||||
}, 2500);
|
||||
|
||||
// ===== 核心: XTEA 核心内部调用钩 — 抓密钥指针! =====
|
||||
setTimeout(function(){
|
||||
function findBase(name){
|
||||
try { return Process.findModuleByName(name); } catch(e){ return null; }
|
||||
}
|
||||
function hookXtea(mod, off, name, inOffset){
|
||||
if (!mod) { send({event:'xtea-nomod', name:name}); return; }
|
||||
var addr = mod.base.add(off);
|
||||
try {
|
||||
Interceptor.attach(addr, {
|
||||
onEnter: function(args){
|
||||
try {
|
||||
// XTEA: a1=buf(输入), a2=±n, a3=key!
|
||||
var n = args[1].toInt32();
|
||||
var kp = args[2];
|
||||
var key = kp.readByteArray(16);
|
||||
var khex = Array.prototype.map.call(new Uint8Array(key), function(x){return ('0'+x.toString(16)).slice(-2)}).join('');
|
||||
// buf 头 (inOffset 字节开读: 输入轮回)
|
||||
var bh = '';
|
||||
try {
|
||||
var inb = args[inOffset].readByteArray(8);
|
||||
bh = Array.prototype.map.call(new Uint8Array(inb), function(x){return ('0'+x.toString(16)).slice(-2)}).join('');
|
||||
} catch(e){}
|
||||
send({event:'xtea-got', name:name, n:n, key:khex, bufH:bh});
|
||||
} catch(e){ send({event:'xtea-err', name:name, e:String(e).substring(0,80)}); }
|
||||
}
|
||||
});
|
||||
send({event:'xtea-hooked', name:name});
|
||||
} catch(e){ send({event:'xtea-fail', name:name, e:String(e).substring(0,80)}); }
|
||||
}
|
||||
hookXtea(findBase('libturingmfa.so'), 0x24D58, 'mfa-xtea', 0);
|
||||
hookXtea(findBase('libturingga.so'), 0x1F1CC, 'ga-xtea', 0);
|
||||
}, 3000);
|
||||
@@ -39,6 +39,19 @@ def adb(*args):
|
||||
return subprocess.run(["adb"] + list(args), capture_output=True, text=True)
|
||||
|
||||
|
||||
def clear_turing_state():
|
||||
"""清 turing 状态 -> 自动重注册触发报告 (文档配方 §11.49)."""
|
||||
try:
|
||||
subprocess.run(["adb","shell","am","force-stop","com.duowan.kiwi"], capture_output=True, text=True)
|
||||
time.sleep(1)
|
||||
for path in ["app_turingdfp","app_turingfd","resinfo","app_turingdmp","cocos","turing_files"]:
|
||||
r = subprocess.run(["adb","shell","run-as","com.duowan.kiwi","rm","-rf",f"/data/data/com.duowan.kiwi/{path}"], capture_output=True, text=True)
|
||||
if r.returncode == 0:
|
||||
print(f"[runner] cleared {path}", flush=True)
|
||||
print("[runner] turing state cleared -> expect auto re-register report", flush=True)
|
||||
except Exception as e:
|
||||
print(f"[runner] clear-EXC {e}", flush=True)
|
||||
|
||||
def run_once(fout, out_path):
|
||||
"""单次 spawn 捕获一轮; 返回该轮事件数 (0 = 该轮无捕获)."""
|
||||
import frida
|
||||
@@ -65,6 +78,7 @@ def run_once(fout, out_path):
|
||||
adb("shell", "am", "force-stop", PKG)
|
||||
time.sleep(1.0)
|
||||
try:
|
||||
clear_turing_state()
|
||||
pid = dev.spawn([PKG])
|
||||
print(f"[runner] spawned pid={pid}", flush=True)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user