feat(huya): bean为指针对象 - 定位getCred@0x265524出口为最终抓点

This commit is contained in:
yml2213
2026-08-25 17:04:36 +08:00
parent e7edfcee5c
commit 69c089fdeb
3 changed files with 94 additions and 0 deletions
+8
View File
@@ -152,3 +152,11 @@ sendMessage(0x1001, json) 载荷(char*直读):
- 六字段与静态预测完全吻合; 空对象/空数组原样传 - 六字段与静态预测完全吻合; 空对象/空数组原样传
剩余: 响应侧(doMSG a2偏移待修, cred响应未读到) + 信封包装层。 剩余: 响应侧(doMSG a2偏移待修, cred响应未读到) + 信封包装层。
证据: evidence/cred_issue_trace.json 证据: evidence/cred_issue_trace.json
## Round9: bean为指针对象, 改挂getCred出口
saveLoginData bean(1536B dump)=C++对象, cred在堆上经指针引用, 非内联。
最直接抓点: BusinessCfg::getCred(ulong,string&out)@0x265524
-> onLeave 读 out(x2) std::string 即152字符hyCred原文。
scripts/hook_beandump.py 已建(时序模板可复用), 下轮改钩子地址重跑一次登录即得。
另: 响应JSON键已确认含"cred"键(0x1c36e8)。
File diff suppressed because one or more lines are too long
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""抓 hyCred 签发链: 挂 saveLoginData(落盘) + LoginCred handler(vtable探测)。
用法: 脚本跑起来后, 在手机上 手动退出登录 -> 重新登录 一次。
捕获: 登录态JSON明文(含cred字段) / saveLoginData 入参 / LoginCred handler 触发。
"""
from __future__ import annotations
import json
import subprocess
import time
from pathlib import Path
import frida
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
JS = r"""
var base = Process.getModuleByName('libudbauthunify.so').base;
send({type:'armed'});
function hx(p,n){ try{ return Array.from(new Uint8Array(p.readByteArray(n))).map(x=>('0'+x.toString(16)).slice(-2)).join(''); }catch(e){ return 'ERR:'+e; } }
Interceptor.attach(base.add(0x265a6c), {
onEnter: function(a){ send({type:'bean', data:hx(a[1],1536)}); }
});
Interceptor.attach(base.add(0x265fb0), {
onEnter: function(a){ send({type:'bean', data:hx(a[2],1536)}); }
});
"""
def main():
d = frida.get_device_manager().add_remote_device('127.0.0.1:31877')
pid = d.spawn(['com.duowan.kiwi'])
s = d.attach(pid)
s.create_script((RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()).load()
s.create_script((RE / "evidence/scripts/mask_frida_maps_only.js").read_text()).load()
d.resume(pid)
time.sleep(11)
s.create_script((RE / "evidence/scripts/patch_guard_block_termination.js").read_text()).load()
print('armed. >>> 请在手机上退出登录并重新登录 <<<')
sc = s.create_script(JS)
events = []
def on_msg(m, _):
if m.get('type') == 'error':
print('JS ERR:', str(m)[:200]); return
if m.get('type') != 'send':
return
p = m.get('payload') or {}
t = p.get('type')
events.append(p)
if t == 'vt_attached':
print(f"[vt] {p['name']} slot{p['slot']} @+{p['fp']}")
elif t == 'saveLD_str':
print(f"[saveLD_str] str={p['str']['t']}:{p['str']['len']}\n bean={p['bean_head']}\n bt={p['bt']}")
elif t == 'saveLD_bean':
print(f"[saveLD_bean] flag={p['flag']}\n bean={p['bean_head']}\n bt={p['bt']}")
elif t == 'cred_unpack':
print(f"[cred_unpack] in={p['in']['t']}:{p['in']['len']}:{p['in']['v'][:160]} <- {p['ra']}")
elif t == 'bigaes':
print(f"[bigaes] len={p['x2']['len']} head={p['x2']['v'][:120]}")
elif t == 'vtable':
print(f"[VT!] {p['name']}#{p['slot']} a1={p['a1']} bt={p['bt']}")
sc.on('message', on_msg)
sc.load()
deadline = time.time() + 300
while time.time() < deadline:
time.sleep(3)
if any(e.get('type') == 'saveLD_bean' for e in events):
time.sleep(10)
break
Path('/Users/yml/codes/douyu_login_py/evidence/beandump.json').write_text(json.dumps(events))
from collections import Counter
print('saved:', Counter(e.get('type') for e in events))
if __name__ == '__main__':
for attempt in range(4):
try:
main()
break
except frida.InvalidOperationError as e:
print('detached:', e)
subprocess.run(['adb', 'shell', 'am', 'force-stop', 'com.duowan.kiwi'])
time.sleep(3)