#!/usr/bin/env python3 """真机探针: 枚举 libhydeviceid.so / libudbauthunify.so 导出符号, 定位 32hex hdid 生成入口. 用真机稳定注入通道 (spawn + art_callsite bypass + resume) 长时存活。 App 启动即加载这两个 so, 枚举 exports 拿函数名+基址偏移, 为下一步 针对性 hook 生成函数(抓输入)做准备。 用法: /Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \ scripts/phone_probe_hdid.py [serial] [remote] 输出: evidence/diag_phone/hdid_probe_exports.json """ from __future__ import annotations import json import subprocess import sys import time from pathlib import Path import frida SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f" REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878" PACKAGE = "com.duowan.kiwi" REPO = Path("/Users/yml/codes/douyu_login_py") OUT = REPO / "evidence" / "diag_phone" / "hdid_probe_exports.json" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js" PROBE_JS = r""" 'use strict'; function listExports(modName, tag){ try{ var md = Process.findModuleByName(modName); if(!md){ send({type:'mod', name:modName, state:'not-loaded'}); return false; } var ex = md.enumerateExports(); var sym = md.enumerateSymbols(); send({type:'mod', name:modName, base:''+md.base, size:md.size, exports: ex.map(function(e){return {name:e.name, type:e.type, off:''+e.address.sub(md.base)};}), symCount: sym.length, syms: sym.map(function(s){return {name:s.name, off:''+s.address.sub(md.base)};})}); return true; }catch(e){ send({type:'err', name:modName, e:String(e)}); return false; } } function hookPropGet(){ var t = Module.findExportByName('libc.so','__system_property_get'); if(!t) return; Interceptor.attach(t,{ onEnter:function(a){ this.k = a[0].readCString()||''; }, onLeave:function(ret){ try{ send({type:'prop', k:this.k, v:(this.ctx.x1.readCString()||'')}); }catch(e){} } }); send({type:'propget-hooked'}); } // App 启动早期就抓 libhydeviceid.so 加载后的符号 + 派生属性读取 setTimeout(function(){ listExports('libhydeviceid.so','hydev'); }, 2000); setTimeout(function(){ listExports('libudbauthunify.so','udb'); }, 2000); setTimeout(hookPropGet, 2000); """ def adb(*a): return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True) def main(): OUT.parent.mkdir(parents=True, exist_ok=True) adb("shell", "am", "force-stop", PACKAGE) time.sleep(1.5) d = frida.get_device_manager().add_remote_device(REMOTE) pid = d.spawn([PACKAGE]) print(f"[*] spawn pid={pid}", flush=True) session = d.attach(pid) det = [] session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True)) session.create_script(ART_CALLSITE.read_text()).load() d.resume(pid) print("[*] bypass loaded + resumed", flush=True) result = {"modules": {}, "props": []} def on_message(m, data): if m.get("type") == "error": print("[JS-ERR]", str(m)[:200], flush=True) return p = m.get("payload") or {} t = p.get("type") if t == "mod": result["modules"][p["name"]] = p print(f"[mod] {p['name']} base={p.get('base')} exports={len(p.get('exports') or [])} syms={p.get('symCount')}", flush=True) elif t == "prop": result["props"].append({"k": p.get("k"), "v": p.get("v")}) elif t == "propget-hooked": print("[*] __system_property_get hooked", flush=True) elif t == "err": print(f"[err] {p.get('name')}: {p.get('e')}", flush=True) sc = session.create_script(PROBE_JS) sc.on("message", on_message) sc.load() print("[*] probe loaded, collecting 12s", flush=True) time.sleep(12) result["detached"] = det OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8") # 打印关键: 各 so 的导出符号名(去重) for name, m in result["modules"].items(): print(f"\n=== {name} exports ({len(m.get('exports') or [])}) ===") for e in (m.get("exports") or [])[:60]: print(f" 0x{e['off']} {e['name']}") print("\n=== 派生属性 (去重) ===") seen = {} for pr in result["props"]: seen[pr["k"]] = pr["v"] for k, v in seen.items(): print(f" {k} = {v}") print(f"\n[*] saved {OUT}") if __name__ == "__main__": main()