Files
live-hub-py/docs/dfpReport破解进度.md
T
2026-08-26 18:28:33 +08:00

274 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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,可铸造)
```json
{"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` ≠ 静态 t1 `865a...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`)
---
## 四、下一步行动(按优先级)
1. **读 IDA 反编译确认加密**:重点 `decompile/1107CC.c`(引用 0x5718!)及其调用者链 0xa6a3c → 找 keystream 生成函数(输入时间戳/随机种子 + 固定 key,输出 3974B 流)
2. **hook 加密函数**:定位后 hook,抓 (明文输入, key/种子, 密文输出) 三元组 → 直接复现纯 Python 加密
3. **验证 keystream 种子假设**:比对 5 组 keystream 的 Δ 与 wire 时间戳 Δ(3-6s 间隔,keystream 差异是否对应计数/时间)→ 若种子=时间戳,纯代码可复现
4. **patch 明文实验修复**:上次改明文崩溃可能因 datadiv 重解码或时序;改用 hook 加密函数输入 buffer 后直接改明文再放行,避免物理内存写
5. **最终目标**:纯 Python 生成 dfpReport 请求体 = 构造明文 JSON(改 hdid/deviceId/appkey 铸造新设备) + 魔数 + 复现 keystream 加密 → 重放得到全新 t1/t5
---
## 五、账号/数据资产
- 账号 `hy_300026595` / `aa778899`(老 ref `hy_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 字节仅 2~3 个值
- **高共享对**: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 总长 + 头 + `huyaudbwebui` `dfpReport` + 字段:
- **t1 (tag22)**: `865a4924a40897ac1fcfe6b4c2cbb045` + `&` + base64(~43B,每次变化,像会话加密数据)
- **tag70**: `actionV(<40hex>` (设备标识回显)
- 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. 待验证决策点
1. actionV 是否 = HMAC/带时间戳编码(需要多次同明文请求对照:两次同明文 → actionV 相同则与明文确定性相关;不同则含随机)
2. 需要抓完整登录流程(登录请求也用同一 hdid?)确认铸造出的新身份能否通过登录
3. 若 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 等指纹 → 需与三元组自洽才能被服务端认可
### 后续(铸造完整方案待定)
1. 抓真实 App **首次注册/重置设备后**的第一帧 dfpReport(看服务端如何初始分配设备 id)
2. 或逆向 collection 的指纹算法(哪些字段参与绑定)
3. 验证"设备上等长 patch hdid + App 自行生成 t2"路线(修复 patch 崩溃:可能因 JSON 长度校验/内存写坏)