#!/usr/bin/env python3 """hook libhydeviceid.so!0x91ce8: t2 [魔数+密文] 拷贝点 -> 抓完整加密输入。 0x91ce8 是 t2 组装中的 memcpy 调用点 (src 已含 魔数+密文, size~3867-3986)。 onEnter: 1) 从 x0/x1/x2/sp 定位 [魔数+密文] 输出缓冲, dump 4096B 2) 全内存扫 {"appId":"5008 找加密输入 (JSON+二进制) 缓冲, dump 4096B 3) SSL_write 抓 wire (密文基准) """ 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_input_capture.json") JS = r""" 'use strict'; Process.setExceptionHandler(function(d){ return true; }); send({type:'armed'}); var MAGIC_STR = '57 18 82 cf 66 4b b3 94 01 ee'; var JSON_STR = '7b 22 61 70 70 49 64 22 3a 22 35 30 30 38'; // {"appId":"5008 function hexb(p, n){ try{ var arr = Array.from(new Uint8Array(p.readByteArray(n))); var s = ''; for (var i=0;i 1024*1024*256) return; if (hits.length >= cap) return; try{ var m = Memory.scanSync(r.base, r.size, pattern); m.slice(0, cap - hits.length).forEach(function(x){ hits.push({addr: String(x.address), mod: (function(){ var mm=Process.findModuleByAddress(x.address); return mm? mm.name : 'anon'; })()}); }); }catch(e){} }); }catch(e){} return hits; } function dumpAround(addr, pre, post){ try{ var p = addr.sub(pre); var d = hexb(p, pre + post); return {base: String(p), pre: pre, post: post, hex: d}; }catch(e){ return null; } } // 找到含魔数的缓冲 (扫描参数指向的内存) function findMagicBuf(ctx){ var cands = []; [ctx.x0, ctx.x1, ctx.x2, ctx.x3, ctx.x4].forEach(function(p, i){ try{ if (p.isNull()) return; if (startsWithHex(p, MAGIC_STR)) cands.push({reg: 'x'+i, addr: String(p)}); // 也看看指针对应的数据是否在 sp 附近 (std::string 结构: ptr 在偏移8/16) var q = p.readPointer(); if (!q.isNull() && startsWithHex(q, MAGIC_STR)) cands.push({reg: 'x'+i+'_ptr', addr: String(q)}); }catch(e){} }); // 扫最近的匿名内存找 [魔数+密文] (限 512MB 内, 快) if (!cands.length){ try{ var m = Memory.scanSync(ctx.sp, 64*1024*1024, MAGIC_STR); m.slice(0, 3).forEach(function(x){ cands.push({reg: 'sp_scan', addr: String(x.address)}); }); }catch(e){} } return cands; } var nCalls = 0; var hooked91 = false; function tryHook91(){ if (hooked91) return; try{ var base = Process.getModuleByName('libhydeviceid.so').base; Interceptor.attach(base.add(0x91ce8), { onEnter: function(a){ nCalls++; var ctx = this.context; var ev = {type:'t2copy', n:nCalls, x0:String(ctx.x0), x1:String(ctx.x1), x2:String(ctx.x2), x3:String(ctx.x3), x4:String(ctx.x4)}; var mbs = findMagicBuf(ctx); ev.magicbufs = mbs; for (var i=0;i 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()}); } }); }); 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_91ce8": print(f"[*] hooked 0x91ce8 @ {p.get('base')}", flush=True) elif t == "ssl_hooked": print("[*] SSL_write hooked", flush=True) elif t == "hook_err": print("[hook_err]", p.get('e'), flush=True) elif t == "t2copy": ev = p.get('ev') or {} print(f"[t2copy] n={ev.get('n')} x0={ev.get('x0')} x1={ev.get('x1')} x2={ev.get('x2')}", flush=True) for mb in (ev.get('magicbufs') or [])[:3]: print(f" magicbuf reg={mb.get('reg')} @{mb.get('addr')}", flush=True) for jh in (ev.get('jsonhits') or [])[:6]: print(f" jsonhit @{jh.get('addr')} mod={jh.get('mod')}", flush=True) events.append({"type": "t2copy", "ev": ev}) OUT.write_text(json.dumps(events)) elif t == "wire": print(f"[wire] len={p.get('len')}", flush=True) events.append({"type": "wire", "len": p.get('len'), "t": p.get('t'), "hex": p.get('hex')}) OUT.write_text(json.dumps(events)) 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 (0x91ce8 命中)", flush=True) try: while True: time.sleep(5) except KeyboardInterrupt: pass print(f"[*] 结束 -> {OUT}", flush=True) if __name__ == "__main__": main()