#!/usr/bin/env python3 """真机: Stalker 跟踪 NativeEntry.getGUID(0x20f8c8) 执行, 揭示 32hex 生成的硬件读取调用. getGUID 被 OLLVM 平坦化, 静态难读。改用运行时 Stalker: hook getGUID, onEnter Stalker.follow(当前线程) 记录所有 call, 同时 hook libc __system_property_get + libhydeviceid 的 hy_fopen64/hy_read 看它读的属性/文件。Java.use('...NativeEntry').getGUID() 主动触发。 用法: /Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \ scripts/phone_stalk_getguid.py [serial] [remote] 输出: evidence/diag_phone/getguid_stalk.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" / "getguid_stalk.json" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js" GETGUID_OFF = 0x20F8C8 JS = r""" 'use strict'; var hooked=false, propHooked=false; function jmod(a){ try{ var m=Process.findModuleByAddress(a); return m? {n:m.name, off:''+a.sub(m.base)}:{n:'?',off:'?'}; }catch(e){ return {n:'?',off:'?'}; } } // hook __system_property_get: 记录 getGUID 期间读的属性 function hookProp(){ if(propHooked) return; try{ var t=Module.findExportByName('libc.so','__system_property_get'); if(!t) return; propHooked=true; Interceptor.attach(t,{ onEnter:function(a){ this.k=a[0].readCString()||''; this.vp=a[1]; }, onLeave:function(){ try{ send({type:'prop', k:this.k, v:this.vp.readCString()||''}); }catch(e){} } }); }catch(e){} } function hookGetGUID(){ if(hooked) return; var md=Process.findModuleByName('libhydeviceid.so'); if(!md) return; hooked=true; var g=md.base.add(0x20f8c8); try{ Interceptor.attach(g,{ onEnter:function(){ this.tid=Process.getCurrentThreadId(); send({type:'guid-enter', tid:this.tid}); try{ Stalker.follow(this.tid, { events:{call:true, ret:false, exec:false}, onReceive:function(events){ // 记录 call 事件的目标地址 var ev=events[0]||{}; var calls=[]; if(ev.type==='call'){ try{ calls=[{addr:''+ev.target}]; }catch(e){} } if(calls.length){ send({type:'call-ev', calls:calls}); } }, onCallSummary:function(sum){ var list=[]; for(var a in sum){ var p=ptr(a); list.push({addr:''+p, mod:jmod(p).n, off:jmod(p).off, cnt:sum[a]}); } send({type:'call-sum', list:list}); } }); }catch(e){ send({type:'stalk-err', e:String(e)}); } }, onLeave:function(){ try{ Stalker.unfollow(Process.getCurrentThreadId()); }catch(e){} send({type:'guid-leave'}); } }); send({type:'guid-hooked'}); }catch(e){ send({type:'hook-err', e:String(e)}); } } setInterval(hookGetGUID, 200); setInterval(hookProp, 200); // 触发: Java.use NativeEntry.getGUID() setTimeout(function(){ Java.perform(function(){ try{ var NE=Java.use('com.huya.security.hydeviceid.NativeEntry'); send({type:'cls-ok'}); var v=NE['getGUID'](); send({type:'guid-value', v:v}); var c=NE['getCDID'](); send({type:'cdid', v:c}); }catch(e){ send({type:'java-err', e:String(e).slice(0,150)}); } }); }, 8000); """ 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() result = {"props": [], "calls": [], "values": {}, "detached": det} 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 == "call-sum": result["calls"] = p["list"] print(f"[call-sum] {len(p['list'])} 个调用:", flush=True) for e in p["list"][:20]: print(f" 0x{e['off']} x{e['cnt']} {e['mod']}", flush=True) elif t == "prop": result["props"].append(p) print(f"[prop] {p.get('k')}={p.get('v')}", flush=True) elif t == "guid-value": result["values"]["getGUID"] = p["v"] print(f"[*] getGUID = {p['v']}", flush=True) elif t == "cdid": result["values"]["getCDID"] = p["v"] print(f"[*] getCDID = {p['v']}", flush=True) elif t in ("guid-enter","guid-leave","guid-hooked","cls-ok"): print(f"[*] {t}", flush=True) elif t in ("stalk-err","hook-err","java-err"): print(f"[!] {t}: {p.get('e')}", flush=True) sc = session.create_script(JS) sc.on("message", on_message) sc.load() d.resume(pid) print("[*] resumed", flush=True) time.sleep(8) # 等类加载 print("[*] 触发 getGUID (Java.use)...", flush=True) time.sleep(6) result["detached"] = det OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8") print(f"[*] saved {OUT}") if __name__ == "__main__": main()