#!/usr/bin/env python3 """扫描内存找加密前的 dfpReport 明文采集数据。 明文 = 设备采集序列化(含 deviceinfo/appid/机型 thyme/M2102J2SC 等)。 找到后 dump 前后 16KB, 与密文(魔数开头)配对解 keystream。 """ from __future__ import annotations import json import time from pathlib import Path import frida RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") OUT = Path("/Users/yml/codes/douyu_login_py/evidence/dfp_plain_big.json") JS = r""" 'use strict'; Process.setExceptionHandler(function(d){ return true; }); send({type:'armed'}); // 设备特征串 (加密前明文会出现) var PATTERNS = [ ['thyme', '74 68 79 6d 65'], ['M2102J2SC', '4d 32 31 30 32 4a 32 53 43'], ['deviceinfo', '64 65 76 69 63 65 69 6e 66 6f'], ['appid', '61 70 70 69 64'], ['terminal', '74 65 72 6d 69 6e 61 6c'], ['android-', '61 6e 64 72 6f 69 64 2d'], ['Xiaomi', '58 69 61 6f 6d 69'], ['sdk', '73 64 6b'], ]; function tryRead(p, n){ try{ return p.readByteArray(n); }catch(e){ return null; } } function scan(){ var reported = {}; try{ Process.enumerateRanges('r--').forEach(function(r){ if (r.size > 1024*1024*256) return; try{ PATTERNS.forEach(function(pat){ // 用打头模式避免每次整个段 MB 级扫描太慢: 只对前几个模式做全扫 var key = pat[0]; if (reported[key] > 20) return; var m = Memory.scanSync(r.base, r.size, pat[1]); if (!m.length) return; reported[key] = (reported[key] || 0) + m.length; m.slice(0, 8).forEach(function(x){ var addr = x.address; // dump 前后: 前找结构头, 后 4KB var pre = null, post = null; try{ pre = addr.sub(256).readByteArray(512); }catch(e){} try{ post = addr.readByteArray(4096); }catch(e){} if (post) { var arr = Array.from(new Uint8Array(post)); var hex = arr.map(function(b){return ('0'+b.toString(16)).slice(-2);}).join(''); send({type:'hit', pat:key, addr:String(addr), hex:hex}); } }); }); }catch(e){} }); }catch(e){} send({type:'tick'}); } setInterval(function(){ try{ scan(); }catch(e){} }, 5000); """ def main(): device = frida.get_device_manager().add_remote_device("127.0.0.1:31877") pid = device.spawn(["com.duowan.kiwi"]) print(f"[*] spawned 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 == "hit": h = p.get('hex', '') addr = p.get('addr') # 去重: 同地址只留一次 if any(e.get('addr') == addr for e in events): return tag = p.get('pat') events.append({"addr": addr, "pat": tag, "hex": h}) OUT.write_text(json.dumps(events, ensure_ascii=False)) # 打印: 特征串 + 前 64B 明文上下文 try: idx = h.encode().decode('hex') if False else h except Exception: pass print(f"[hit:{tag}] @{addr}", flush=True) # 提取特征串前后 3B 看明文结构 print(f" hex[:96]: {h[:96]}", flush=True) script = session.create_script(JS) script.on("message", on_message) script.load() print("[*] 主JS 已装载(resume前), 加载 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, 扫描中 (特征: thyme/M2102J2SC/deviceinfo/appid/android-)", flush=True) try: while True: time.sleep(5) except KeyboardInterrupt: pass print(f"[*] 结束, {len(events)} -> {OUT}", flush=True) if __name__ == "__main__": main()