16 KiB
dfpReport 破解进度总结 (待破解#2)
当前状态:加密算法已确定为逐字节 XOR keystream(非 XXTEA),明文 JSON 已全部拿到。 剩余关键问题:keystream 每会话动态生成,需找到其生成函数/种子。 IDA 已导出两个 so 的完整反编译+反汇编:
/Users/yml/Downloads/so/{libhydeviceid,libudbauthunify}.so_export_for_ai
一、已确定的结论(铁证)
1. dfpReport 加密 = 逐字节 XOR keystream(不是 XXTEA!)
证据链:
- 同会话 5 次 dfpReport(wire 间隔 3-6s),密文两两 XOR 非零位置 3947/3984 → keystream 每会话动态
- 但密文前 10B 魔数恒定
57 18 82 cf 66 4b b3 94 01 ee(跨天、跨会话)→ 魔数是明文头 XOR 固定值或者固定前缀 - 8.23 各样本位置 11-22 部分相同 → 是明文固定字段,不是 keystream 泄漏
- 若为 XXTEA(hyudbxxt),则密文任何字节变化会扩散到全部 → 但实测前 10B 不动 → 排除 XXTEA
2. tag2 密文结构(3984B)
tag2 = [魔数 10B][XOR密文 3974B?]
- 明文 ≈ JSON(554B) + 采集二进制(3420B) ≈ 3974B,与"密文长度-魔数"吻合
- 但注意:3954B 对齐实验(偏移 0..9 all ~1% 相同率)未找到明文精确起点 → 加密输入可能不是纯 JSON,JSON 只是其中一部分(pa 结构待最终确认)
3. 明文 JSON 完整捕获(554B,可铸造)
{"appId":"5008","appVer":"13.4.22","appkey":"865a4924a40897ac1fcfe6b4c2cbb0e3",
"channel":"xiaomi","deviceId":"02df398797432eadefcc12767119ad5e80999389",
"deviceName":"M2102J2SC","hdid":"7c5387e0539c023c31c4ff0e807e7256117385ee",
"heightPixels":"2120","isCloud":0,"isForbidLog":1,"isHome":0,"isPre":0,"openAppId":"",
"savePath":"/data/user/0/com.duowan.kiwi/files/huyaudb","sdkVer":"1.0.80138",
"servantName":"huyaudbwebui","shareAppDataPath":"/data/user/0/com.duowan.kiwi/files/huyaudb",
"systemInfo":"android","systemVer":"M2102J2SC,30,11","terminalType":1,"testEnv":0,"widthPixels":"1080"}
- hdid 字段 = 服务端返回的 device_id (t5)
7c5387...→ 服务端回显本地明文,本地可完全控制设备身份! - appkey
865a...cbb0e3≠ 静态 t1865a...cbb045→ appkey 来自运行时配置
4. 响应回显验证(t1/t5 本地可铸造)
- 重放任意 wire body → HTTP 200,344B 响应,同一 t1+t5,全新 t2
- t1(
865a4924a40897ac1fcfe6b4c2cbb045) 静态埋于 libhydeviceid .data(0x3bb190, 0x3bb230, 0x3e0e50),datadiv 加载时解密 - patch 明文实验:改明文 JSON 的 hdid/appkey/deviceId(等长替换)后 app 崩溃 → 说明有校验或时序问题,但方向正确(见下"待办")
5. keystream 前 24B(从 5 组同步 pair 恢复)
# 修正对齐 off=0 (密文[10:] XOR 明文[0:]):
t=1379: 07 f8 60 f9 b8 bc 30 bf 76 4e d4 8d a8 f6 54 21 31 fa 47 53 72 63 ef 29
t=7268: 17 f9 70 e5 b8 3c 6c be 77 e6 ac cb c3 2a 6a 8c ef aa cd ce eb e5 67 a7
t=3695: 17 f9 70 f9 b1 3c 4c be 77 74 14 ee 5e 7c e0 88 3a e2 c3 2c 72 63 8c 10
...
各会话 keystream 不同 → 动态生成,极可能种子 = 时间戳或会话随机 + 固定密钥
二、IDA 导出可用资源(用户新提供,重点!)
/Users/yml/Downloads/so/libhydeviceid.so_export_for_ai/
decompile/ # 3895 个 C 反编译
disassembly/ # 逐函数汇编
exports.txt # 1832 个导出 (含 70 个 .datadiv_decode)
function_index.txt, strings.txt, memory/...
/Users/yml/Downloads/so/libudbauthunify.so_export_for_ai/
decompile/252700.c # hyudbxxt::xxtea_encrypt 标准 XXTEA (dfp 无关,登录用)
优先排查的加密函数:
decompile/1107CC.c引用0x5718魔数常量 ×3!(sub_1107CC,调用者 0xa6a3c)← 最可疑是真加密/解密函数decompile/1D52C8.c、1D5844.c、294FB0.c、7BF8C.c也含 0x5718- 大 XOR 循环候选:17F75C, 212B04, 21F98C, 2332D4, 23755C, 23C534, 26D7B0, 274BBC, B5A18
三、动态 hook 关键资产
抓同步 (明文, 密文) 对 —— 已成功!
scripts/capture_pair_sync.py:spawn+bp3 绕过 → SSL_write 命中 dfpReport 时 80ms 后全内存扫描 {"appId":"5008" → 同时刻明文。5 组同步 pair 在 evidence/dfp_pair_sync.json
环境
- RE repo venv:
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python -u - 远程 frida:
adb forward tcp:31877 tcp:31877+frida.get_device_manager().add_remote_device("127.0.0.1:31877") - 绕过:spawn 后先
bypass_msaoaid_maps_art_callsite.js+mask_frida_maps_only.js→ resume → 11s 后patch_guard_block_termination.js - hook 规则:后台任务 + tail 实时看 + 命中即 pkill(见
docs/Hook脚本运行与捕获约定.md)
四、下一步行动(按优先级)
- 读 IDA 反编译确认加密:重点
decompile/1107CC.c(引用 0x5718!)及其调用者链 0xa6a3c → 找 keystream 生成函数(输入时间戳/随机种子 + 固定 key,输出 3974B 流) - hook 加密函数:定位后 hook,抓 (明文输入, key/种子, 密文输出) 三元组 → 直接复现纯 Python 加密
- 验证 keystream 种子假设:比对 5 组 keystream 的 Δ 与 wire 时间戳 Δ(3-6s 间隔,keystream 差异是否对应计数/时间)→ 若种子=时间戳,纯代码可复现
- patch 明文实验修复:上次改明文崩溃可能因 datadiv 重解码或时序;改用 hook 加密函数输入 buffer 后直接改明文再放行,避免物理内存写
- 最终目标:纯 Python 生成 dfpReport 请求体 = 构造明文 JSON(改 hdid/deviceId/appkey 铸造新设备) + 魔数 + 复现 keystream 加密 → 重放得到全新 t1/t5
五、账号/数据资产
- 账号
hy_300026595/aa778899(老 refhy_300024708) - HAR:
/Users/yml/Desktop/抓包/hy/8.23/登录-{1,2,3,5}.har(6 个 dfpReport wire,t2 len 3408-4237) - 同一设备(小米 M2102J2SC, Android 11, 快照
5dd8c93f)所有 wire 魔数头相同 - 关键回显:hdid
7c5387e0539c023c31c4ff0e807e7256117385ee= 明文 hdid = 响应 t5
六、关键文件索引
| 文件 | 内容 |
|---|---|
evidence/dfp_pair_sync.json |
5 组同步 (密文+明文) pair(最新最有价值) |
evidence/dfp_pair.json |
早期 3 组 pair(wire len 4278/4397/4413) |
evidence/dfp_wire_stack.json |
8 事件 5 dfpReport wire HEX |
evidence/dfp_plain_big.json |
187 个堆命中(明文 JSON @0x7814096d19 等) |
evidence/dfp_datadiv_full.json |
.data 7895 个已解码字符串(t1 所在) |
scripts/capture_pair_sync.py |
同步 pair 抓取脚本(可复用) |
/Users/yml/Downloads/so/libhydeviceid.so_export_for_ai/ |
IDA 反编译(新!) |
七、新会话起点(最重要的下一步)
IDA 反编译确认加密:/Users/yml/Downloads/so/libhydeviceid.so_export_for_ai/decompile/1107CC.c
- 引用
0x5718魔数 ×3,调用者0xa6a3c→ 极可能是 dfpReport 加解密核心 - 同时看:1D52C8.c, 1D5844.c, 294FB0.c, 7BF8C.c(均含 0x5718)
- 反汇编对应文件在
disassembly/.asm
验证 keystream 种子:evidence/dfp_pair_sync.json 有 5 组同步 pair, 两组 keystream 差 vs 时间戳差 → 判断种子是否时间戳/计数器。
八、本会话重大发现(新!)
1. 输入结构确定: [恒定JSON 554B][collection ~3400B 每轮变化]
- 恒定 JSON(554B,已有全部字段,含 hdid/deviceId):
{"appId":"5008",...,"widthPixels":"1080"} - collection 是大 JSON:
{"touch_events":[...],"net_intfs":"...","phoneInfo":"{...}","qimei16/36":"...","risk_apps":"com.topjohnwu.magisk|bin.mt.plus|","scanned_appname":"<binary ~2900B>",...} - collection 尾部含 Poseidon 反 frida 检测字段:
"Poseidon_D1":"libhydeviceid.so|hooked:libhydeviceid.so|hooked:libc.so|",Poseidon_D3:"zygisk|",Poseidon_D5:"/product/bin/su\n|",adb_input_list:"device_name=Virtual|"(huyaudb 读 /proc/self/maps 检测 hook) - collection 每轮变化(触摸事件/权限/网络/反检测结果/扫描二进制)
2. keystream 每轮变化,但跨轮/跨会话有强共享结构(非纯随机!)
多 session(6+ 个 pipeline 证据文件)9+ 轮 ks[0..553]=cipher[:554]^JSON 分析:
- 跨会话模板复用:相同 10B 前缀在多个独立 session 反复出现!
07f970f9b8bc50be774e×3(155601-r1, 160316-r1, 160316-r2)17f97019b93c6cbe7764×3(155601-r3, 160316-r3, 160855-r2, 170031-r2)17f97ef9b83c6cbe774a×3(args session 多轮)07f870f9b8bc50be774e×2(v2)
- 同一 session 连续轮次可完全相同(160316: r1 = r2 =
07f970f9b8bc50be774e) - 每位置 ks 值域很小:8 轮样本中大部分位置只有 2
8 个不同值(随机期望 8),前 6 字节仅 23 个值 - 高共享对:r2(160855,t=18273) vs r5(170031,t=100816) 共享 140/554B(25%;随机期望 0.4%),最长连续 20B;r0 vs r4 共享 80B
- 结论:keystream 不是每会话随机种子动态生成,而更像 固定模板库 + 状态选择(生成器需进一步确认,但"所有字节每轮全新随机"的假设被推翻)
3. 反 frida 检测: hook 了 libhydeviceid 的事件, collection JSON 的 Poseidon 字段值相应变化(每轮检测结果不同)
4. 骨架 pipeline hook 已稳定(轻量,符合约定)
scripts/hook_dfp_pipeline.py只 hook 6 函数(topA_20e924/midA_5a938/cryptoA_5b1f4/copy_61098/pktA_7f744/pktB_92128)- 必须遵守 hook 约定:spawn → bypass x2 → resume+11s → patch_guard → 最后加载主 hook JS(顺序颠倒会卡启动界面,已踩坑多次)
- 事件每轮:copy_61098 ×2(MAGIC 缓冲)、pktA/pktB(MAGIC + taf 段)、wire(密文全长)、jsonhits(内存搜 JSON 完整 554B)
- GOT 桩方案已放弃:144 个 br x17 桩 hook 太重,App 卡启动/卡点击(动态间接调用表 0x3ba000/0x3b9000 运行时才解密,静态也解不出)
5. 本地 capstone 反汇编(ELF vaddr==fileoff)
- cryptoA_5b1f4: 16 个 bl 目标(0x5f1b4 首调、0x45aa0×26、0x76ab0×9、0x461c0×6、0x6ec68×4、0x7398c×3、0x1f07ec×2…)
- 0x461c0/0x45aa0 = 间接调用桩(adrp x16;ldr x17,[x16,#off];br x17),GOT 表运行时解密失败静态解析
- 0x5f1b4 = 字符串构建函数(状态机混淆),非生成器;cryptoA 主体无标量/NEON XOR 循环 → XOR 内核在间接调用或查表中
6. 下一步(铸造路线评估,优先!)
- 服务端旧结论:重放任意 wire body → HTTP 200,hdid/device_id = 明文回显
- 若 keystream 真是模板库,铸造新设备 = 改明文 JSON(hdid/deviceId) + 复用任意一轮 keystream XOR → 重放
- 行动:纯 Python 构造新设备 t2(改明文 + 模板 ks)重放验证 200 → 若成功,铸造路线打通,生成器破解降级为可选
九、铸造路线实验(本会话最新,部分成功!)
1. 纯 Python 重放 100% 可行(不需要 keystream!)
scripts/forge_replay.py:取证据里一帧完整 wire → 重构 HTTP/1.1 POST(Content-Length 等长) → socket+TLS 直连wsapi.huya.com:443→ 原样重放返回HTTP/1.1 200 OK+ 344B 响应(稳定复现)- XOR 自反性铸造:
新密文 = 原密文 XOR (原JSON XOR 新JSON),JSON 区(554B)等长替换 hdid/deviceId/appkey,collection 区不动 → 不需要 ks、不需要生成器、不需要设备! - 服务端确实解析 t2 明文(见下),不是纯透传 200
2. 响应结构(344B taf/wup 包)
- 4B 总长 + 头 +
huyaudbwebuidfpReport+ 字段:- t1 (tag22):
865a4924a40897ac1fcfe6b4c2cbb045+&+ base64(~43B,每次变化,像会话加密数据) - tag70:
actionV(<40hex>(设备标识回显)
- t1 (tag22):
- 40hex 之后有 taf 尾字段(0x0b 8c... 未解析)
3. 服务端行为(受控实验)
| 请求 | actionV(40hex) |
|---|---|
| baseline 原样 | 7c5387e0...17385ee(= 明文 hdid!) |
| 只改 hdid | 新值(≠ 新 hdid,每次实验都不同) |
| 只改 deviceId | 新值(≠ 新 deviceId) |
| 用 actionV1 当 hdid 再上报 | 又一个新值(不稳定) |
- actionV 对 hdid/deviceId 变化均敏感 → 服务端解密解析了 t2 明文 ✓
- baseline 时 actionV == 明文 hdid(服务端回显/认可当前设备)
- 改身份后 actionV 每次都是新值且不稳定 → 未确认"下发新设备身份"机制,可能 actionV 含时间戳/会话随机,或需配合后续登录流程
4. 待验证决策点
- actionV 是否 = HMAC/带时间戳编码(需要多次同明文请求对照:两次同明文 → actionV 相同则与明文确定性相关;不同则含随机)
- 需要抓完整登录流程(登录请求也用同一 hdid?)确认铸造出的新身份能否通过登录
- 若 actionV 含随机/时间戳 → 服务端可能靠 t1 或其它字段做设备绑定 → 需重新审视铸造入口
十、29 轮 keystream 全量分析(决定性结论!)
合并 6+ session、29 轮 ks[0..553] 分析:
1. keystream 前缀模板复用(铁证)
17f97019b93c6cbe7764 x4: 160316r3, 160855r2, 170031r2, 175740r2 ← 4 个不同 session 同轮次位置!
07f970f9b8bc50be774e x3: 155601r1, 160316r1, 160316r2
17f97ef9b83c6cbe774a x3: args.j r3/4/5
07f870f9b8bc50be774e x2: v2 r1/r2
- 完整 10B 前缀跨 session 精确重复,不是随机
2. 同 session 完全重复
- args.j r3 = r4 = r5: ks[0..553] 全部 554 字节相同!(shared=554)
- v2 r1≈r2(51 共享),160316 r1≈r2(36 共享)
3. 高共享对(跨 session)
- 160855r2 vs 170031r2: 140/554 共享,最长连续 20B
- 170031r3 vs 175740r3: 97 共享
- 170031r2 vs 175740r2: 70 共享,最长连续 31B
- 160855r1 vs 160855r4: 80 共享(同 session 首尾)
4. 结论
- keystream 与"轮次序号/状态"强相关,同一序号位置在多个 session 复用相同(或高相似)ks
- 不是每 session 随机种子;更像 固定生成器 + 状态/序号选择(同轮次位置 → 同 ks)
- 对铸造的含义:任意一帧真实 ks 即可加密伪造明文(服务端行为见下节,这是关键限制)
5. 服务端铸造验证(forge_replay.py 实测,重要!)
- 原样重放:HTTP 200,344B 响应稳定,actionV =
7c5387...(= 真 hdid,稳定回显) - 改明文 JSON 任何身份字段(hdid/deviceId/appkey):200 OK,但 actionV 变为
[32hex 类MD5] + "10" + [6hex]动态 token,同明文两次上报 token 不同 - 响应 t1(tag22)恒定
865a4924a40897ac1fcfe6b4c2cbb045&base64(base64 部分每次变化) - 推论:服务端识别"真设备"靠 明文三元组 (hdid, deviceId, appkey) 与库匹配; 纯改明文不匹配 → 动态 token → 铸造新设备需同时伪造三元组一致的完整设备身份 (可能还要 collection 里 qimei/device_id 对应)否则服务端不认可
6. 下一步两个方向
A. 铸造:找出服务端设备三元组的匹配逻辑(抓真实 App 首次注册流程/或抓登录后的完整身份链), 纯 Python 全真伪造(JSON+collection 齐改)重放验证 actionV 稳定性 B. 生成器:既然 ks 与轮次序号相关,继续定位生成函数(0x5f1b4 初始化 + cryptoA 调用链), 复现 keystream 生成,任意轮次任意长度可算
十一、服务端设备绑定校验(差分实验定论!)
对 t2 密文做逐区差分修改重放,观察服务端响应:
| 修改内容 | actionV 结果 |
|---|---|
| baseline 原样 | 稳定回显真 hdid 7c5387... |
| JSON 非身份字段(widthPixels 1080→1081) | 稳定回显真 hdid(不受影响!) |
| JSON 身份字段 hdid | 动态 token [MD5样式32hex]10[6hex],每次不同 |
| JSON 身份字段 deviceId | 动态 token |
| JSON 身份字段 appkey | 动态 token |
| collection 区任意 1 字节(@560/@617/@753/@953 等) | 动态 token(波及全 collection) |
结论:服务端绑定校验 = (hdid, deviceId, appkey 三元组) + (collection 完整性)
- 服务端解密 t2 → 解析 JSON 三元组 + collection → 与库中设备匹配 → 匹配则回显设备 hdid,不匹配则返回动态 token
- 动态 token 不可复用(同明文两次上报不同)→ 不是"注册下发",更像"未知设备拒绝标记"
- 铸造的真正瓶颈不是加密(已破解),而是服务端以 (三元组, collection) 识别设备
- collection 含 qimei16/36、scanned_appname 等指纹 → 需与三元组自洽才能被服务端认可
后续(铸造完整方案待定)
- 抓真实 App 首次注册/重置设备后的第一帧 dfpReport(看服务端如何初始分配设备 id)
- 或逆向 collection 的指纹算法(哪些字段参与绑定)
- 验证"设备上等长 patch hdid + App 自行生成 t2"路线(修复 patch 崩溃:可能因 JSON 长度校验/内存写坏)