dfpReport: 确定加密为逐字节XOR(动态keystream)+同步pair捕获+IDA导出线索, 记录破解进度

This commit is contained in:
yml2213
2026-08-26 14:17:58 +08:00
parent acb0a737eb
commit 4eec01568f
38 changed files with 4942 additions and 3 deletions
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
"""dump libhydeviceid.so .data 段 (datadiv 就地解密区), 提取全部明文串。
datadiv 解码器把 .data 段 0x3c8xxx 区域的密文就地解密为明文。
dump 整个 .data(0x3bb000 ~ 0x3eac10), 提取可读字符串 + 搜魔数。
"""
from __future__ import annotations
import json
import time
from pathlib import Path
import frida
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
OUT = Path("/Users/yml/codes/douyu_login_py/evidence/dfp_datadiv_data.json")
JS = r"""
'use strict';
Process.setExceptionHandler(function(d){ return true; });
send({type:'armed'});
var DATA_RANGES = [
[0x36aec0, 0x6e75], // .rodata (含 datadiv 密文? 可能)
[0x3b2460, 0x74d8], // .data.rel.ro
[0x3bb000, 0x2fc08], // .data (datadiv 就地解密区)
];
function dumpData(m){
DATA_RANGES.forEach(function(r){
var off = r[0], size = r[1];
try{
var base = m.base.add(off);
var bytes = Array.from(new Uint8Array(base.readByteArray(size)));
// 提取可读字符串 (>=5 连续可打印)
var strs = [];
var cur = '';
for (var i = 0; i < bytes.length; i++) {
var c = bytes[i];
if (c >= 32 && c < 127) cur += String.fromCharCode(c);
else {
if (cur.length >= 5) strs.push({s: cur, off: off + i - cur.length});
cur = '';
}
}
// 搜魔数
var hasMagic = false;
for (var i = 0; i + 5 < bytes.length; i++) {
if (bytes[i]==0x57 && bytes[i+1]==0x18 && bytes[i+2]==0x82 && bytes[i+3]==0xcf) { hasMagic = true; break; }
}
send({type:'data', sec:off.toString(16), size:size, strs:strs.length,
hasMagic: hasMagic,
sample: strs.slice(0, 60)});
}catch(e){
send({type:'data_err', sec:off.toString(16), e:String(e)});
}
});
}
var tries = 0;
var iv = setInterval(function(){
tries += 1;
var m = null;
try{ m = Process.getModuleByName('libhydeviceid.so'); }catch(e){}
if (m) {
clearInterval(iv);
send({type:'mod', base:String(m.base), size:m.size});
dumpData(m);
} else if (tries > 100) { clearInterval(iv); send({type:'no_mod'}); }
}, 50);
"""
def main():
device = frida.get_device_manager().add_remote_device("127.0.0.1:31877")
pid = device.spawn(["com.duowan.kiwi"])
print(f"[*] spawned pid={pid}", flush=True)
session = device.attach(pid)
events = []
def on_message(message, data):
if message.get("type") == "error":
print("[JS-ERR]", str(message)[:200], flush=True); return
if message.get("type") != "send":
return
p = message.get("payload") or {}
t = p.get("type")
if t == "data":
print(f"[data] sec=0x{p.get('sec')} size={p.get('size')} "
f"strs={p.get('strs')} magic={p.get('hasMagic')}", flush=True)
for it in p.get('sample', [])[:40]:
print(f" 0x{it['off']:x}: {it['s']}", flush=True)
events.append({"section": p.get('sec'), "strings": p.get('sample')})
OUT.write_text(json.dumps(events, indent=1))
elif t == "data_err":
print(f"[data_err] {p.get('sec')}: {p.get('e')}", flush=True)
elif t == "mod":
print(f"[*] libhydeviceid @ {p.get('base')} size={p.get('size')}", flush=True)
elif t == "no_mod":
print("[*] 模块未加载", flush=True)
script = session.create_script(JS)
script.on("message", on_message)
script.load()
print("[*] 主JS 已装载(resume前), 加载 bypass + resume", flush=True)
session.create_script(
(RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()
).load()
session.create_script(
(RE / "evidence/scripts/mask_frida_maps_only.js").read_text()
).load()
device.resume(pid)
time.sleep(11)
session.create_script(
(RE / "evidence/scripts/patch_guard_block_termination.js").read_text()
).load()
print("[*] 已 resume, dump 完成即 pkill", flush=True)
try:
while True:
time.sleep(5)
except KeyboardInterrupt:
pass
print(f"[*] 结束, {len(events)} 条 -> {OUT}", flush=True)
if __name__ == "__main__":
main()