#!/usr/bin/env python3 """定位 dfpReport t2 (魔数+密文) 原生组装函数 —— 轻量版。 v1 教训: 全量 memcpy hook (24B~20KB 都插桩) 把 App 拖到 ANR 被杀。 本版只插桩大拷贝 (2500~20000B): - src/dst 命中 10B 魔数 -> 深捕获 + 回溯 (稀有, 不拖慢) - 其余大拷贝只压轻量环形 (size/src/dst), 供 SSL_write 时与输出缓冲关联 流程: spawn+三件套 bypass -> 自动 dfpReport -> 命中即持久化 (实时写文件) """ from __future__ import annotations import json import time from pathlib import Path import frida PACKAGE = "com.duowan.kiwi" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") OUT = Path("/Users/yml/codes/douyu_login_py/evidence/dfp_builder2.json") JS = r""" 'use strict'; Process.setExceptionHandler(function(d){ return true; }); send({type:'armed'}); var MAGIC = [0x57,0x18,0x82,0xcf,0x66,0x4b,0xb3,0x94,0x01,0xee]; var processStart = Date.now(); function hexb(p, n){ try{ var arr = Array.from(new Uint8Array(p.readByteArray(n))); var s = ''; for (var i=0;i= 2500 && size <= 20000){ ring.push({t: now, fn: fn, size: size, dst: String(dst), src: String(src)}); if (ring.length > 200) ring.shift(); var dm = startsWith(dst, MAGIC); var sm = startsWith(src, MAGIC); if (dm || sm){ var cap = {t: now, fn: fn, size: size, dst: String(dst), src: String(src), dmagic: dm, smagic: sm, srchead: hexb(src, 64), dsthead: hexb(dst, 64), stack: bt.call(this)}; ring.push({t: now, fn: 'CAP:'+fn, size: size, dst: String(dst), src: String(src), stack: cap.stack}); send({type:'capture', cap: cap}); } } } function hookLibc(){ ['memcpy','memmove'].forEach(function(fn){ try{ var p = Module.findExportByName('libc.so', fn); if (!p) return; Interceptor.attach(p, { onEnter: function(a){ try{ onCopy.call(this, fn, a[0], a[1], a[2].toInt32()); }catch(e){} } }); send({type:'hooked_copy', fn: fn, at: String(p)}); }catch(e){ send({type:'hook_fail', fn: fn, e: String(e)}); } }); } hookLibc(); function scanMagic(){ var hits = []; try{ Process.enumerateRanges('r--').forEach(function(r){ if (r.size > 1024*1024*256) return; try{ var m = Memory.scanSync(r.base, r.size, '57 18 82 cf 66 4b b3 94 01 ee'); m.slice(0, 14).forEach(function(x){ var mm = Process.findModuleByAddress(x.address); hits.push({addr: String(x.address), mod: mm ? mm.name : 'anon', pre: hexb(x.address.sub(64), 64), post: hexb(x.address.add(10), 48)}); }); }catch(e){} }); }catch(e){} return hits; } try{ var r = new ApiResolver('module'); r.enumerateMatchesSync('exports:*!SSL_write').forEach(function(m){ Interceptor.attach(m.address, { onEnter: function(a){ var len = a[2].toInt32(); if (len < 100 || len > 20000) return; var head = ''; try{ head = a[1].readCString(Math.min(len, 2000)); }catch(e){ return; } if (head.indexOf('dfpReport') < 0) return; send({type:'wire', len:len, hex:hexb(a[1], len), t:Date.now(), ring: ring.slice(-150), magics: scanMagic()}); } }); }); send({type:'ssl_hooked'}); }catch(e){ send({type:'ssl_err', e:String(e)}); } """ def main(): device = frida.get_device_manager().add_remote_device("127.0.0.1:31877") pid = device.spawn([PACKAGE]) print(f"[*] spawned {PACKAGE} pid={pid}", flush=True) session = device.attach(pid) events = [] def on_message(message, data): if message.get("type") == "error": print("[JS-ERR]", str(message)[:300], flush=True) return if message.get("type") != "send": return p = message.get("payload") or {} t = p.get("type") if t == "armed": print("[*] JS armed", flush=True) elif t == "hooked_copy": print(f"[hook] {p.get('fn')} @ {p.get('at')}", flush=True) elif t == "ssl_hooked": print("[*] SSL_write hooked", flush=True) elif t == "capture": c = p.get('cap') or {} print(f"[CAPTURE] {c.get('fn')} size={c.get('size')} dmagic={c.get('dmagic')} smagic={c.get('smagic')}", flush=True) print(f" src={c.get('src')} -> dst={c.get('dst')}", flush=True) print(f" srchead={c.get('srchead','')[:40]} dsthead={c.get('dsthead','')[:40]}", flush=True) print(f" stack: {(c.get('stack') or [])[:16]}", flush=True) events.append({"type": "capture", "cap": c}) OUT.write_text(json.dumps(events)) elif t == "wire": print(f"[wire] len={p.get('len')}", flush=True) ev = {"type": "wire", "len": p.get('len'), "t": p.get('t'), "hex": p.get('hex'), "ring": p.get('ring'), "magics": p.get('magics')} events.append(ev) OUT.write_text(json.dumps(events)) mg = p.get('magics') or [] print(f" magics: {len(mg)}", flush=True) for mm in mg[:8]: print(f" {mm.get('addr')} mod={mm.get('mod')} pre={mm.get('pre','')[-32:]} post={mm.get('post','')[:20]}", flush=True) # ring entries that wrote near magic addresses if mg: for mm in mg[:3]: try: target = int(mm.get('addr'), 16) for e in (p.get('ring') or []): try: d = int(e.get('dst'), 16) s = int(e.get('dst'), 16) + (e.get('size') or 0) if d <= target <= s: print(f" ring-write-to-magic: {e}", flush=True) except Exception: pass except Exception: pass script = session.create_script(JS) script.on("message", on_message) script.load() print("[*] 主JS 已装载, 加载 bypass + resume", flush=True) session.create_script( (RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text() ).load() session.create_script( (RE / "evidence/scripts/mask_frida_maps_only.js").read_text() ).load() device.resume(pid) time.sleep(11) session.create_script( (RE / "evidence/scripts/patch_guard_block_termination.js").read_text() ).load() print("[*] 已 resume。等待自动 dfpReport", flush=True) try: while True: time.sleep(5) except KeyboardInterrupt: pass print(f"[*] 结束 -> {OUT}", flush=True) if __name__ == "__main__": main()