#!/usr/bin/env python3 """dump libhydeviceid.so .data 段 (datadiv 就地解密区), 提取全部明文串。 datadiv 解码器把 .data 段 0x3c8xxx 区域的密文就地解密为明文。 dump 整个 .data(0x3bb000 ~ 0x3eac10), 提取可读字符串 + 搜魔数。 """ 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_datadiv_data.json") JS = r""" 'use strict'; Process.setExceptionHandler(function(d){ return true; }); send({type:'armed'}); var DATA_RANGES = [ [0x36aec0, 0x6e75], // .rodata (含 datadiv 密文? 可能) [0x3b2460, 0x74d8], // .data.rel.ro [0x3bb000, 0x2fc08], // .data (datadiv 就地解密区) ]; function dumpData(m){ DATA_RANGES.forEach(function(r){ var off = r[0], size = r[1]; try{ var base = m.base.add(off); var bytes = Array.from(new Uint8Array(base.readByteArray(size))); // 提取可读字符串 (>=5 连续可打印) var strs = []; var cur = ''; for (var i = 0; i < bytes.length; i++) { var c = bytes[i]; if (c >= 32 && c < 127) cur += String.fromCharCode(c); else { if (cur.length >= 5) strs.push({s: cur, off: off + i - cur.length}); cur = ''; } } // 搜魔数 var hasMagic = false; for (var i = 0; i + 5 < bytes.length; i++) { if (bytes[i]==0x57 && bytes[i+1]==0x18 && bytes[i+2]==0x82 && bytes[i+3]==0xcf) { hasMagic = true; break; } } send({type:'data', sec:off.toString(16), size:size, strs:strs.length, hasMagic: hasMagic, sample: strs.slice(0, 60)}); }catch(e){ send({type:'data_err', sec:off.toString(16), e:String(e)}); } }); } var tries = 0; var iv = setInterval(function(){ tries += 1; var m = null; try{ m = Process.getModuleByName('libhydeviceid.so'); }catch(e){} if (m) { clearInterval(iv); send({type:'mod', base:String(m.base), size:m.size}); dumpData(m); } else if (tries > 100) { clearInterval(iv); send({type:'no_mod'}); } }, 50); """ 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 == "data": print(f"[data] sec=0x{p.get('sec')} size={p.get('size')} " f"strs={p.get('strs')} magic={p.get('hasMagic')}", flush=True) for it in p.get('sample', [])[:40]: print(f" 0x{it['off']:x}: {it['s']}", flush=True) events.append({"section": p.get('sec'), "strings": p.get('sample')}) OUT.write_text(json.dumps(events, indent=1)) elif t == "data_err": print(f"[data_err] {p.get('sec')}: {p.get('e')}", flush=True) elif t == "mod": print(f"[*] libhydeviceid @ {p.get('base')} size={p.get('size')}", flush=True) elif t == "no_mod": print("[*] 模块未加载", 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, dump 完成即 pkill", flush=True) try: while True: time.sleep(5) except KeyboardInterrupt: pass print(f"[*] 结束, {len(events)} 条 -> {OUT}", flush=True) if __name__ == "__main__": main()