143 lines
6.3 KiB
JavaScript
143 lines
6.3 KiB
JavaScript
// R35: 内存快照 @SSL_write — 定位密文缓冲区全部拷贝 + 邻域转储 + 会话密钥
|
|
// 目标: 在密文刚生成/刚发出的瞬间, 从堆内存里找到 (a) 明文 deflate 缓冲 (b) 16B 会话密钥
|
|
'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 ''; } }
|
|
var STATIC_KEY = 'f15e78d23033c2c4bac8a925677e863a';
|
|
var done = false;
|
|
|
|
function scanRanges(ranges, patternHex, maxHits){
|
|
var pat = '';
|
|
for (var i=0;i<patternHex.length;i+=2) pat += patternHex.substr(i,2)+' ';
|
|
var out = [];
|
|
for (var r=0; r<ranges.length && out.length<maxHits; r++){
|
|
var rg = ranges[r];
|
|
if (rg.size > 96*1024*1024) continue;
|
|
try {
|
|
var ms = Memory.scanSync(rg.base, rg.size, pat);
|
|
for (var m=0; m<ms.length && out.length<maxHits; m++) out.push({a: ms[m].address, mod: rg.file ? rg.file.path : String(rg.base), prot: rg.protection});
|
|
} catch(e){}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function enumAllRW(){
|
|
return Process.enumerateRanges({protection:'rw-', coalesce:false});
|
|
}
|
|
|
|
for (const mod of Process.enumerateModules()) {
|
|
let 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('');
|
|
var idx = hex.indexOf('571882cf664bb39401ee');
|
|
emit({event:'post', num:num, hex:hex, idx:idx});
|
|
if (idx < 0) return;
|
|
|
|
// 线上密文前缀 (magic 后 10B 起, 取 16B 作内存搜索指纹)
|
|
var CIPH = hex.substring(idx+20, idx+20+32);
|
|
emit({event:'cipher-prefix', ciph:CIPH});
|
|
var bt = Thread.backtrace(this.context, Backtracer.ACCURATE).slice(0,30).map(function(a){ var m=Process.findModuleByAddress(a); return m? m.name+'!0x'+a.sub(m.base).toString(16) : a.toString(); }).join(' <- ');
|
|
emit({event:'bt', bt:bt});
|
|
|
|
setTimeout(function(){
|
|
// ---- 1) Java 堆: Perseus/Guava 会话密钥 ----
|
|
try {
|
|
Java.perform(function(){
|
|
try {
|
|
Java.enumerateClassLoaders({
|
|
onMatch: function(loader){
|
|
try {
|
|
Java.classFactory.loader = loader;
|
|
Java.choose('com.tencent.turingfd.sdk.ams.ga.Perseus', {
|
|
onMatch: function(inst){
|
|
try {
|
|
var g = inst.b.value;
|
|
emit({event:'perseus', key: g ? String(g.c.value) : null,
|
|
envLen: g && g.b && g.b.value ? g.b.value.length : -1,
|
|
d: g && g.d ? String(g.d.value) : null});
|
|
} catch(e){ emit({event:'perseus-e', e:String(e).substring(0,150)}); }
|
|
},
|
|
onComplete: function(){}
|
|
});
|
|
} catch(e){}
|
|
},
|
|
onComplete: function(){}
|
|
});
|
|
} catch(e){ emit({event:'j-e', e:String(e).substring(0,150)}); }
|
|
});
|
|
} catch(e){}
|
|
|
|
// ---- 2) native 堆扫描 ----
|
|
var ranges = enumAllRW();
|
|
emit({event:'ranges', n:ranges.length, bytes: ranges.reduce(function(a,r){return a+r.size;},0)});
|
|
|
|
// 2a. 密文拷贝定位
|
|
var hits = scanRanges(ranges, CIPH, 24);
|
|
emit({event:'cipher-copies', n:hits.length, addrs: hits.map(function(h){return String(h.a);})});
|
|
// 每处转储 ±2KB 邻域 (前 8 处全量, 其余只记地址)
|
|
for (var i=0;i<Math.min(hits.length,8);i++){
|
|
var a = hits[i].a;
|
|
var lo = a.sub(2048), hi = a.add(2048);
|
|
emit({event:'nbhd', i:i, addr:String(a), src:hits[i].mod, lo:String(lo), hex: hexb(lo, 4096)});
|
|
}
|
|
|
|
// 2b. 静态密钥出现位置
|
|
var sh = scanRanges(ranges, STATIC_KEY, 40);
|
|
emit({event:'static-key-hits', n:sh.length, addrs: sh.map(function(h){return String(h.a)+'@'+h.src;})});
|
|
|
|
// 2c. 密文邻域内 16 字符 [A-Za-z0-9] 候选密钥串 (±8KB of each cipher copy)
|
|
var cands = {}, cn=0;
|
|
for (var i=0;i<Math.min(hits.length,6) && cn<4000;i++){
|
|
try {
|
|
var buf = new Uint8Array(hits[i].a.sub(8192).readByteArray(16384));
|
|
var run = '', start=0;
|
|
for (var j=0;j<buf.length;j++){
|
|
var c=buf[j];
|
|
var ok = (c>=48&&c<=57)||(c>=65&&c<=90)||(c>=97&&c<=122);
|
|
if (ok){ if(!run) start=j; run+=String.fromCharCode(c); }
|
|
else {
|
|
if (run.length===16 && !(cands[run])) { cands[run]=1; cn++; }
|
|
run='';
|
|
}
|
|
}
|
|
if (run.length===16 && !cands[run]) { cands[run]=1; cn++; }
|
|
} catch(e){}
|
|
}
|
|
emit({event:'key-cands', n:cn, keys:Object.keys(cands)});
|
|
|
|
// 2d. 全堆 zlib 头候选 (78 01/5e/9c/da) — 限量转储 256B 供主机验证
|
|
var zs=0;
|
|
for (var r=0; r<ranges.length && zs<120; r++){
|
|
var rg=ranges[r];
|
|
if (rg.size>96*1024*1024) continue;
|
|
for (var pi=0; pi<4 && zs<120; pi++){
|
|
var zpat = ['78 01','78 5e','78 9c','78 da'][pi];
|
|
var ms=[];
|
|
try { ms = Memory.scanSync(rg.base, rg.size, zpat); } catch(e){ continue; }
|
|
for (var m=0; m<ms.length && zs<120; m++){
|
|
// 过滤: deflate 流应较长 (指纹 JSON ~1-3KB), 跳过落在密文拷贝邻域外的? 不跳, 交给主机验证
|
|
var h = hexb(ms[m].address, 192);
|
|
if (h) { emit({event:'zcand', pat:zpat, a:String(ms[m].address), hex:h}); zs++; }
|
|
}
|
|
}
|
|
}
|
|
emit({event:'scan-done', zcands:zs});
|
|
}, 200);
|
|
} catch(e){ emit({event:'e', e:String(e).substring(0,200)}); }
|
|
}
|
|
});
|
|
}
|
|
emit({event:'armed'});
|