109 lines
4.1 KiB
JavaScript
109 lines
4.1 KiB
JavaScript
// R36b: 只挂 libz.so 的 deflate/inflate — 抓 XXTEA 输入明文 + 隐藏路径 backtrace
|
|
// 教训: GL/EGL 驱动也有同名 deflate 导出但非 zlib ABI, hook 必崩 — 只认 libz.so
|
|
'use strict';
|
|
function emit(o){ try{ send(o); }catch(e){} }
|
|
function hexb(p,n){ try{ return Array.from(new Uint8Array(p.readByteArray(n))).map(b=>('0'+b.toString(16)).slice(-2)).join(''); }catch(e){ return ''; } }
|
|
function bt(ctx, depth){
|
|
var out = [];
|
|
try {
|
|
var fr = Thread.backtrace(ctx, Backtracer.ACCURATE).slice(0, depth).map(function(a){
|
|
var m = Process.findModuleByAddress(a);
|
|
return m ? m.name + '!0x' + a.sub(m.base).toString(16) : a.toString();
|
|
});
|
|
if (fr.length) return fr;
|
|
} catch(e){}
|
|
try {
|
|
out = Thread.backtrace(ctx, Backtracer.FUZZY).slice(0, depth).map(function(a){
|
|
var m = Process.findModuleByAddress(a);
|
|
return 'F:' + (m ? m.name + '!0x' + a.sub(m.base).toString(16) : a.toString());
|
|
});
|
|
} catch(e){}
|
|
return out;
|
|
}
|
|
// z_stream (LP64): next_in@0 avail_in@8 total_in@16 next_out@24 avail_out@32 total_out@40
|
|
var streams = {};
|
|
var nDumps = 0;
|
|
var armed = false;
|
|
|
|
function armDeflate(){
|
|
if (armed) return;
|
|
var libz = Process.findModuleByName('libz.so');
|
|
if (!libz) return;
|
|
var d = null, i = null;
|
|
try { d = libz.getExportByName('deflate'); } catch(e){}
|
|
try { i = libz.getExportByName('inflate'); } catch(e){}
|
|
if (d) {
|
|
armed = true;
|
|
emit({event:'armed-deflate', mod:'libz.so'});
|
|
Interceptor.attach(d, {
|
|
onEnter(args){
|
|
var strm = args[0]; var flush = args[1].toInt32();
|
|
try {
|
|
var key = strm.toString();
|
|
var st = streams[key];
|
|
if (!st){
|
|
st = streams[key] = {out_base: strm.add(24).readPointer(), bt0: null, n: 0};
|
|
var nin = strm.add(8).readU32();
|
|
if (nin > 0 && nin < 200000){
|
|
st.in_hex = hexb(strm.readPointer(), Math.min(nin, 65536));
|
|
st.in_len = nin;
|
|
}
|
|
}
|
|
if (!st.bt0 && st.n < 3) st.bt0 = bt(this.context, 24);
|
|
st.n++;
|
|
this.strm = strm; this.flush = flush;
|
|
} catch(e){}
|
|
},
|
|
onLeave(ret){
|
|
try {
|
|
var st = streams[this.strm.toString()];
|
|
if (!st) return;
|
|
var zret = ret.toInt32();
|
|
var totalOut = this.strm.add(40).readU64().toNumber();
|
|
if (this.flush === 4 && (zret === 1 || zret === 0)){
|
|
// 只转储 turing 调用 (bt 含 turing) 或大输入 (Lyra 帧 JSON) — 其余是 UI 资源压缩噪音
|
|
var btStr = (st.bt0 || []).join(',');
|
|
var isTuring = /turing/i.test(btStr);
|
|
if (!isTuring && (st.in_len || 0) < 300){ delete streams[this.strm.toString()]; return; }
|
|
var hex = hexb(st.out_base, Math.min(totalOut, 131072));
|
|
nDumps++;
|
|
emit({event:'deflate-out', i:nDumps, total: totalOut, ret: zret, turing: isTuring,
|
|
in_len: st.in_len || 0, in_hex: st.in_hex || '', out_hex: hex, bt: st.bt0});
|
|
delete streams[this.strm.toString()];
|
|
}
|
|
} catch(e){}
|
|
}
|
|
});
|
|
}
|
|
if (i) {
|
|
// inflate 不挂 — 图片解码热路径, 挂了首屏卡死 (R36 教训)
|
|
}
|
|
}
|
|
|
|
var poll = setInterval(function(){ armDeflate(); if (armed) clearInterval(poll); }, 150);
|
|
|
|
// SSL_write: 抓同轮 wire 密文
|
|
var done = false;
|
|
for (var mod of Process.enumerateModules()){
|
|
var addr = null;
|
|
try { addr = mod.getExportByName('SSL_write'); } catch(e){}
|
|
if (!addr) continue;
|
|
Interceptor.attach(addr, {
|
|
onEnter(args){
|
|
if (done) return;
|
|
try {
|
|
var num = args[2].toInt32();
|
|
if (num < 60 || num > 60000) return;
|
|
var u = new Uint8Array(args[1].readByteArray(num));
|
|
var s = '';
|
|
for (var i=0;i<u.length;i++) s += (u[i]>=32 && u[i]<127) ? String.fromCharCode(u[i]) : '.';
|
|
if (s.indexOf('dfpReport') < 0) return;
|
|
done = true;
|
|
var hex = Array.prototype.map.call(u, function(x){return ('0'+x.toString(16)).slice(-2)}).join('');
|
|
emit({event:'post', num:num, hex:hex});
|
|
} catch(e){}
|
|
}
|
|
});
|
|
}
|
|
emit({event:'script-loaded'});
|