#!/usr/bin/env python3 """间接调用解析器 v3 (按需启用, 零常驻开销): 默认不挂任何桩 hook; copy_61098 轮次边界(x1=MAGIC)触发时动态 attach 72 个 br 桩, 3 秒后全部 detach。捕获 crypto 调用图谱定位 keystream 内核。 """ 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/got_resolve.json") JS = r""" 'use strict'; Process.setExceptionHandler(function(d){ return true; }); send({type:'armed'}); var t0 = Date.now(); var MAGIC_HDR = [0x57,0x18,0x82,0xcf,0x66,0x4b,0xb3,0x94,0x01,0xee]; var modBase = null; var counts = new Map(); var roundN = 0; var stubHandles = []; // Interceptor handles to detach var stubsActive = false; var activeSince = 0; function hexb(p, n){ try{ return Array.from(new Uint8Array(p.readByteArray(n))).map(function(b){return ('0'+b.toString(16)).slice(-2)}).join(''); }catch(e){ return ''; } } function startsWith(p, arr){ try{ var b = new Uint8Array(p.readByteArray(arr.length)); for (var i=0;i 2) arr.push([k, v]); }); send({type:'round', n: roundN, t: Date.now()-t0, counts: arr, activeMs: Date.now()-activeSince}); counts = new Map(); send({type:'magic', t: Date.now()-t0, full: hexb(ctx.x1, 4300)}); // 延迟到回调外再挂桩(frida 回调内 attach 会失败) setTimeout(function(){ counts = new Map(); enableStubs(); }, 15); } }); send({type:'round_hooked'}); }catch(e){ setTimeout(tryHookRound, 500); } } setTimeout(tryHookRound, 400); 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, t: Date.now()-t0}); } }); }); send({type:'ssl_hooked'}); }catch(e){ send({type:'ssl_err', e:String(e)}); } """ def load_with_retry(session, js, n=4): last = None for i in range(n): try: s = session.create_script(js) s.load() return s except Exception as e: last = e print(f"[retry {i}] {e}", flush=True) time.sleep(3) raise last 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)[:200], 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 == "round_hooked": print("[*] round boundary hooked", flush=True) elif t == "stubs_on": print(f"[t={p.get('t')}ms] stubs ON x{p.get('n')}", flush=True) elif t == "stubs_off": print(f"[t={p.get('t')}ms] stubs OFF", flush=True) elif t == "round": c = dict(p.get('counts') or []) top = sorted(c.items(), key=lambda x: -x[1])[:15] print(f"[t={p.get('t')}ms round{p.get('n')}] (stubs active {p.get('activeMs')}ms) calls={sum(c.values())} distinct={len(c)}", flush=True) for off, cnt in top: print(f" off=0x{off:x} x{cnt}", flush=True) events.append({"type": "round", **p}) OUT.write_text(json.dumps(events)) elif t == "magic": events.append({"type": "magic", "t": p.get('t'), "full": p.get('full')}) OUT.write_text(json.dumps(events)) elif t == "wire": print(f"[t={p.get('t')}ms] [wire] len={p.get('len')}", flush=True) events.append({"type": "wire", **p}) OUT.write_text(json.dumps(events)) # 严格按约定顺序: spawn -> bypass x2 -> resume + sleep 11 -> patch_guard -> 最后主 hook JS print("[*] loading bypass", flush=True) load_with_retry(session, (RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()) load_with_retry(session, (RE / "evidence/scripts/mask_frida_maps_only.js").read_text()) device.resume(pid) time.sleep(11) load_with_retry(session, (RE / "evidence/scripts/patch_guard_block_termination.js").read_text()) print("[*] loading 主 hook JS (最后)", flush=True) script = load_with_retry(session, JS) script.on("message", on_message) print("[*] all loaded. waiting dfpReport", flush=True) try: while True: time.sleep(5) except KeyboardInterrupt: pass print(f"[*] done -> {OUT}", flush=True) if __name__ == "__main__": main()