docs(huya): 逆向定位 32hex hdid = NativeEntry.getGUID() + 真机注入通道证据
核心结论:
- 32hex hdid (WUP 登录帧 field1.tag0) = com.huya.security.hydeviceid.NativeEntry.getGUID()
- 金样本 ed0db8 为金样本设备 GUID; 当前真机 M2102J2SC getGUID()=0a7dfaa882938a6ab502511452142c57(稳定)
- NativeEntry 全字段: getGUID(32hex)/getCDID(40hex)/getHDID(40hex)/getMID(16hex)/getSDID(base64 safedeviceid)
- 单机不可铸造: 改 serialno/ANDROID_ID/删files/hydevice持久化 均不变(硬锚底层硬件)
- libhydeviceid.so 为 OLLVM+datadiv 壳; 用 dlopen->JNI_OnLoad->RegisterNatives(env表索引215) 绕开混淆
脚本: phone_probe_registernatives2(注册表)/phone_java_call_nativeentry(Java读getGUID)/phone_read_hdid(native hook)
phone_dump_hydev(dump解密so)/phone_probe_login_frame(登录帧)/arm64_disasm(反汇编)
证据: hdid_regtable/java_nativeentry/hdid_read/login_frames/hdid_probe* + libhydeviceid_{original,dump}.so
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机 v6: hook SSL_write 抓 hypasswordLogin 登录帧, 提取实际 hdid + 追来源.
|
||||
|
||||
目标: 确认登录帧 t1.t0 的 32hex hdid 实际值(应为金样本 ed0db8...),
|
||||
并 hook 它进入 WUP 帧前被读取的调用链。同时 hook:
|
||||
- libc strstr/strcpy 等 (登录帧组装时 hdid 字符串被拷贝处)
|
||||
- BusinessCfg::getSafeDeviceId(0x26a3c4) / setDeviceInfo(0x26a894)
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_hdid6.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_probe6.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
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"
|
||||
LOGIN_ACT = "com.duowan.kiwi/.loginui.impl.gamesdk.GameSdkLoginActivity"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_probe6.json"
|
||||
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 hexb(p,n){try{return Array.from(new Uint8Array(p.readByteArray(n))).map(b=>('0'+b.toString(16)).slice(-2)).join('');}catch(e){return '';}}
|
||||
function stdstr(p){
|
||||
try{
|
||||
if(p.isNull()) return '';
|
||||
var f = p.readU8();
|
||||
if((f&1)===0){ var l=f>>1; return l? p.add(1).readUtf8String(l):''; }
|
||||
else { var d=p.readPointer(); var l=p.add(8).readU64(); return (l&&l<512)? d.readUtf8String(l):''; }
|
||||
}catch(e){ return ''; }
|
||||
}
|
||||
var sslDone=false, udbHooked=false;
|
||||
function hookSSL(){
|
||||
if(sslDone) return;
|
||||
try{
|
||||
var r=new ApiResolver('module');
|
||||
r.enumerateMatchesSync('exports:*!SSL_write').forEach(function(m){
|
||||
Interceptor.attach(m.address,{onEnter:function(a){
|
||||
var len=a[2].toInt32();
|
||||
if(len<50||len>50000)return;
|
||||
var h=''; try{ h=a[1].readCString(Math.min(len,600))||''; }catch(e){}
|
||||
if(h.indexOf('hypasswordLogin')>=0){
|
||||
send({type:'wup_login', len:len, hex:hexb(a[1],len)});
|
||||
}
|
||||
}});
|
||||
});
|
||||
send({type:'sslwrite-hooked'});
|
||||
}catch(e){}
|
||||
sslDone=true;
|
||||
}
|
||||
function hookUdb(){
|
||||
if(udbHooked) return;
|
||||
var md = Process.findModuleByName('libudbauthunify.so');
|
||||
if(!md) return;
|
||||
udbHooked=true;
|
||||
send({type:'udb', base:''+md.base});
|
||||
// 设备信息读取点
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a3c4), { // getSafeDeviceId
|
||||
onLeave:function(ret){ send({type:'getsd', ret:''+ret}); }
|
||||
});
|
||||
}catch(e){}
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a894), { // setDeviceInfo(a1=this, a2..a5=std::string*)
|
||||
onEnter:function(a){
|
||||
send({type:'setdi', args:[stdstr(a[1]), stdstr(a[2]), stdstr(a[3]), stdstr(a[4])]});
|
||||
}
|
||||
});
|
||||
}catch(e){}
|
||||
send({type:'udb-hooked'});
|
||||
}
|
||||
// libhydeviceid 导出函数调用探测
|
||||
var hyTried=false;
|
||||
function hookHy(){
|
||||
if(hyTried) return;
|
||||
var md = Process.findModuleByName('libhydeviceid.so');
|
||||
if(!md) return;
|
||||
hyTried=true;
|
||||
send({type:'hydev', base:''+md.base});
|
||||
['hyfopen64','hy_read','hy_syscall','hy_write','hyftell'].forEach(function(n){
|
||||
try{
|
||||
var a = Module.findExportByName('libhydeviceid.so', n);
|
||||
if(a) Interceptor.attach(a,{onEnter:function(args){
|
||||
var s=''; try{ s=args[0].readCString(128)||''; }catch(e){}
|
||||
send({type:'hycall', fn:n, a0:s, tid:Process.getCurrentThreadId()});
|
||||
}});
|
||||
}catch(e){}
|
||||
});
|
||||
send({type:'hydev-hooked'});
|
||||
}
|
||||
setInterval(hookSSL, 800);
|
||||
setInterval(hookUdb, 50);
|
||||
setInterval(hookHy, 50);
|
||||
"""
|
||||
|
||||
|
||||
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 = {"frames": [], "events": [], "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")
|
||||
result["events"].append(p)
|
||||
if t == "wup_login":
|
||||
result["frames"].append(p)
|
||||
b = bytes.fromhex(p.get("hex", ""))
|
||||
# 提取帧里的 32hex hdid
|
||||
h32 = re.findall(rb"[0-9a-f]{32}", b)
|
||||
print(f"[WUP_LOGIN] len={p.get('len')} hdid候选: {[h.decode() for h in h32[:3]]}", flush=True)
|
||||
elif t in ("udb","udb-hooked","sslwrite-hooked","hydev","hydev-hooked"):
|
||||
print(f"[*] {t}", p if t=="udb" else "", flush=True)
|
||||
elif t == "setdi":
|
||||
print(f"[setDeviceInfo] args={p.get('args')}", flush=True)
|
||||
elif t == "getsd":
|
||||
print(f"[getSafeDeviceId] ret={p.get('ret')}", flush=True)
|
||||
elif t == "hycall":
|
||||
print(f"[hycall] {p.get('fn')} a0={p.get('a0')}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed", flush=True)
|
||||
time.sleep(4)
|
||||
print("[*] 触发登录...", flush=True)
|
||||
adb("shell", "am", "start", "-n", LOGIN_ACT)
|
||||
time.sleep(18)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] 登录帧数: {len(result['frames'])} saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user