Files
live-hub-py/tools/frida/hook_memshot.js
T

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'});