- 输入图定案: 库内零硬件文件读, 种子=ANDROID_ID+服务端hydeviceid_config+MID, 属性层无关(X1/X2) - 真机全段解密 dump(phone_dump_hydev_full) + merge_decrypted 合入原文件 → unidbg 可直接加载 - unidbg(JDK21+patch) harness: JNI 桩(SharedPreferences日志化/Settings.Secure/NativeBridge) 金测试: androidId 加密态与 getGUID 全部与真机一致 - 遗留: NativeBridge.b(100) 原生归属库未定(非hydeviceid/udb/device-util), 当前桩替顶 - 修正结论: '单机不可铸造'判断错误, GUID=b(100)=f(androidId密, config, MID) 输入全可控
126 lines
4.1 KiB
Python
126 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""真机: dlopen 拦截所有目标库, JNI_OnLoad 入口挂 RegisterNatives 钩子,
|
|
抓 NativeBridge.klog/b 等全部 native 注册归属(库+偏移).
|
|
|
|
用法: python3 scripts/phone_probe_rn_all.py [serial] [remote]
|
|
"""
|
|
from __future__ import annotations
|
|
import json, subprocess, sys, 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"
|
|
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
|
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
|
|
|
JS = r"""
|
|
'use strict';
|
|
function jmod(a){
|
|
try{ var m = Process.findModuleByAddress(a); return m ? {n:m.name, off:'0x'+a.sub(m.base).toString(16)} : {n:'?',off:'?'}; }catch(e){ return {n:'?',off:'?'}; }
|
|
}
|
|
var rnHooked = false;
|
|
function hookRN(){
|
|
if(rnHooked) return;
|
|
try{
|
|
var env = Java.vm.tryGetEnv();
|
|
if(!env) return;
|
|
var fns = env.handle.readPointer();
|
|
var rn = fns.add(215 * Process.pointerSize).readPointer();
|
|
if(rn.isNull()) return;
|
|
Interceptor.attach(rn, {
|
|
onEnter: function(args){
|
|
var methods = args[3];
|
|
var count = args[4].toInt32();
|
|
var mm=[];
|
|
for(var i=0;i<count;i++){
|
|
var m = methods.add(i * Process.pointerSize * 3);
|
|
try{
|
|
var name = m.readPointer().readCString();
|
|
var sig = m.add(Process.pointerSize).readPointer().readCString();
|
|
var fnp = m.add(Process.pointerSize*2).readPointer();
|
|
var jm = jmod(fnp);
|
|
mm.push({name:name, sig:sig, mod:jm.n, off:jm.off});
|
|
}catch(e){}
|
|
}
|
|
send({type:'rn', methods:mm, tid:Process.getCurrentThreadId()});
|
|
}
|
|
});
|
|
rnHooked = true;
|
|
send({type:'rn-hooked'});
|
|
}catch(e){ send({type:'err', e:String(e).slice(0,120)}); }
|
|
}
|
|
setInterval(hookRN, 100);
|
|
|
|
// 目标库加载即挂 JNI_OnLoad
|
|
var TARGETS = ['libhydeviceid', 'libudbauthunify', 'libudb'];
|
|
var loaded = {};
|
|
function hookLib(md){
|
|
if(loaded[''+md.base]) return;
|
|
loaded[''+md.base] = true;
|
|
var jni = md.findExportByName('JNI_OnLoad');
|
|
if(!jni) return;
|
|
var name = md.name;
|
|
send({type:'lib', name:name, base:''+md.base});
|
|
Interceptor.attach(jni, {
|
|
onEnter: function(){ send({type:'ol-enter', lib:name}); hookRN(); },
|
|
onLeave: function(ret){ send({type:'ol-leave', lib:name, ret:ret.toString()}); }
|
|
});
|
|
}
|
|
setInterval(function(){
|
|
Process.enumerateModules().forEach(function(md){
|
|
for(var i=0;i<TARGETS.length;i++){
|
|
if(md.name.indexOf(TARGETS[i]) >= 0){ hookLib(md); break; }
|
|
}
|
|
});
|
|
}, 100);
|
|
"""
|
|
|
|
|
|
def adb(*a):
|
|
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
|
|
|
|
|
def main():
|
|
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}", flush=True)
|
|
s = d.attach(pid)
|
|
s.create_script(ART_CALLSITE.read_text()).load()
|
|
seen = set()
|
|
regs = []
|
|
|
|
def on_m(m, data):
|
|
p = m.get("payload") or {}
|
|
t = p.get("type")
|
|
if t == "rn":
|
|
for mm in p.get("methods", []):
|
|
k = (mm["name"], mm["sig"], mm["mod"], mm["off"])
|
|
if k not in seen:
|
|
seen.add(k)
|
|
regs.append(mm)
|
|
print(f"[RN] {mm['name']} {mm['sig']} @ {mm['mod']} {mm['off']}", flush=True)
|
|
elif t in ("rn-hooked",):
|
|
print("[*] rn-hooked", flush=True)
|
|
elif t == "lib":
|
|
print(f"[*] lib {p['name']} base={p['base']}", flush=True)
|
|
elif t in ("ol-enter", "ol-leave"):
|
|
print(f"[*] {t} {p.get('lib')} {p.get('ret','')}", flush=True)
|
|
elif t == "err":
|
|
print("[!]", p, flush=True)
|
|
elif m.get("type") == "error":
|
|
print("[JS-ERR]", m.get("description")[:120], flush=True)
|
|
|
|
sc = s.create_script(JS)
|
|
sc.on("message", on_m)
|
|
sc.load()
|
|
d.resume(pid)
|
|
time.sleep(20)
|
|
print(f"[*] total unique: {len(regs)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |