Files
live-hub-py/docs/HUYA_HDID_ALGORITHM_GEN.md
T
yml2213 9586eaa8a3 feat(huya): 🎯 ga IDA导出核心挖掘: 密钥槽5788=3DF88同款! 全链XTEA/deflate/编码器映射 (R29)
- 5788=5788:f15e78d2… 与 mfa 3DF88 相同 (turing 共享key!)
- ga XTEA sub_1F1CC + 封套 1F918/1F9C8 + 解码 27DBC(inflate!)
- b209202 a6==0→sub_23DF8编码器 / a6==1→27FF8解码器
- k209202 16B块: RiskDetectExt.ExtData schema + key=16B输入
- 请求密文密钥≠静态key (需编码器内部路径)
2026-08-29 07:54:56 +08:00

1449 lines
95 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.
<!-- ⚠️ 命名规范强制约束:本档中所有设备标识必须带前缀。规范见 docs/HUYA_ID_NOMENCLATURE.md。
GUID32=doLaunch sGuid(32hex, launch体系) | HDID32=登录帧t1.t0设备证书(32hex) |
DEVID40=注册链t5/t2.t8 device_id(40hex) | MID16=mid(16hex) | ACTION=注册链t2授权令牌。
旧段落中裸词按上下文语义理解:登录相关=HDID32launch/铸币=GUID3240hex=DEVID40。 -->
# 虎牙 32hex hdid 算法生成路径分析(不用采集,纯算法铸币)
> 前置结论(见 `HUYA_HDID_RESEARCH.md`):32hex hdidWUP 登录帧 field1.tag0= hdidWUP 登录帧 field1.tag0=
> `com.huya.security.hydeviceid.NativeEntry.getGUID()`;真机 M2102J2SC 金值
> `0a7dfaa882938a6ab502511452142c57`getGUID 是 getter,值在 `init()`(偏移 0x20e6e4
> 时由 `libhydeviceid.so`OLLVM 混淆 + datadiv 壳)算出并缓存。
>
> 用户约束:**不能用真机采集设备池;必须用算法生成**(在代码里根据可控输入铸出
> 一套又一套服务端认可的设备身份)。本文是"如何继续"的完整分析。
>
> 时间:2026-08-28`0c6aff6`/`022357a` 之后)。
## 一、任务再定义:什么是"算法生成"
设备身份是**纯函数**
```
getGUID() = F_guid(种子集 S) → 32hex
getCDID() = F_cdid(种子集 S) → 40hex
getHDID() = F_hdid(种子集 S) → 40hex
getMID() = F_mid(种子集 S) → 16hex
getSDID() = F_sdid(种子集 S) → base64 180B
```
- **S = 设备的指纹输入集合**(属性/文件/系统值/持久化密钥,详见 §三)。
- "算法生成" = 我们在**任意环境**(纯 Python / unidbg 仿真)里把 F 跑出来,
输出不依赖任何物理设备;换一套 S 就铸一个新身份。
- **关键澄清**:"单机不可铸造"只证明 `{ro.serialno, ANDROID_ID, files/hydevice/}`
不在 S 里(改它们 GUID 不变);**不证明 S 不可控**。在仿真里 S 由我们提供;
而 S 只要有一丁点可变化的空间(IMEI/qimei/SoC 序列号/密钥文件),铸币空间就是无限的。
## 二、本轮新证据(对 decrypt dump 的静态分析)
`evidence/diag_phone/libhydeviceid_dump.so`(datadiv 已就地解密的内存镜像)做常量/结构扫描:
### 2.1 lib 内置**标准密码学原语**(这是静态移植可行性的决定性地雷)
| 原语 | dump 内偏移 | 说明 |
|---|---|---|
| AES S-box(完整 256B,精确匹配) | `0x36be82` | 有 AES(256 位密钥可能性大) |
| MD5 IV 常量(`01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10` | `0x36c190` | 有 MD5 |
| SHA1 轮常量 K(小端,4×u32 | `0x36c1a0` | 有 SHA1 |
| base64 字母表 | `0x3e8880` | 有 base64SDID 180B 的编码) |
→ 32hex 输出=MD5 类、40hex 输出=SHA1 类、SDID=AES+base64 的组合推测是**标准构造 + 内置密钥**,
不是自研魔改算法 → **不需要解全部 OLLVM 平坦化**,只需定位"哪些函数把哪些输入喂给这些原语"。
### 2.2 .data 里已提取的固定密钥 / 盐候选(datadiv 解密后的明文)
| 常量 | 形态 | 角色猜测 |
|---|---|---|
| `7jbF5IiqqIDdgEtiNQLzJSuTZmRk8sbc`32B,出现 8+ 次) | base64 风格 32 字符 | **AES-256 固定密钥**(最可能) |
| `HuyaUdb1928374650qwertyuiop`(27B,出现 2 次) | 明文 | UDB 体系固定密钥/加盐串 |
| `865a4924a40897ac1fcfe6b4c2cbb045` / `...abc798` | 32hex ×2(只差 4B | appid 对密钥哈希 / dfpReport 通道常量 |
| `961c151d2e87f2686a955a9be24d316f1362bf21 3.11.3` | 40hex + 版本 | SHA1 固定值(某固定材料摘要) |
| `400e18d7b4c8070374bd216279da774b` / `473c8798375341d8849804154d181acc` | 32hex ×2 | 运行时/渠道相关常量 |
| `0123456789abcdef0123456789abcdef` | 32hex | 默认 GUID 模板 / 输入填充 |
> 注意:`getDfpConfig` 是**服务端下发**的接口名(.data 里有),这些 32hex 常量里
> 可能有运行时应答替换的值——金样本对拍时要用真机实测值逐一对账(§五)。
### 2.3 getGUID0x20f8c8)反汇编现场 = 标准 OLLVM 平坦化
`0x20f8c8` 处可见典型 dispatcher 模式(`mov w16,#case; cmp w16,w13; b.gt/b.le/b.eq` 状态机跳转),
函数体完整存在于 dump 中(壳已解密)。Stalker 记录其内部调用锚点(模块偏移):
`0x46140``0x1d6e68` 等(`0x46140` 紧邻 `0x45f20/0x45890/0x45aa0/0x46fd0` 小偏移簇 =
hex/base64/CRC 类工具函数区)。**这些锚点是静态还原的切入点**:先在这些地址反汇编,
认出"读 S → 拼接 → MD5/SHA1/AES 调用"的骨架即可,不需要全量反平坦化。
## 三、种子集 S 的候选清单(当前输入图缺口)
从 .data 字符串、init 窗口 openat 记录、probe 事件三方交叉(**都只是候选,待 E2 定案**):
| 类别 | 具体项 | 备注 |
|---|---|---|
| 属性 | `ro.product.{model,manufacturer,board,device,name,brand,marketname,vendor.model}` | .data 出现完整名单 |
| 系统文件 | `/proc/cpuinfo`(含 `Serial:`)、`/proc/version``/proc/meminfo` | `cat /proc/cpuinfo` 字符串直接出现 |
| 持久化 APP 密钥 | **`fileshydckey`**init() 窗口 open 过 1 次) | 疑似"设备密钥",**是否每次安装重新生成 = 决定性** |
| 腾讯 QIMEI | `qimei16` / `qimei36`(.data 出现 3 组) | 腾讯灯塔设备 ID,持久化于 shared_prefs**清数据会再生成** |
| 蓝牙/WiFi | 蓝牙名称/地址、WiFi 信息 | .data 有读取代码路径 |
| 缓存/上报 | `files/hydevice/resinfo`、上报链路字段 | 已确认与 GUID 值无关(实验删过不变) |
| 硬件锚(待验证) | IMEI、`/sys/devices/soc0/serial_number`、TEE/Keystore 密钥 | 上一轮推断"硬锚 SoC"的落点,**尚无直接实验** |
**为什么上一轮"不可铸造"实验不完整**:它们没有覆盖 `fileshydckey` 删除、全量清数据/
卸载重装(会影响 qimei 与可能重生成的文件)、IMEI 变更。只要 E1/E3 里任一项变了
GUID,就说明 S 里有可再生的成分 → 铸币空间立刻打开。
## 四、决定性实验清单(真机在线:`5dd8c93f`,通道稳定,1~2 天)
- **E1**:全量清数据 / 卸载重装 → getGUID 变不变?(若有持久化种子:变了 = 单机
"重装即新设备",先于一切;不变 = 硬件锚确证)
- **E2(最高价值工件)**init()0x20e6e4**库内定向 Stalker**:只跟踪
libhydeviceid 内部调用 + 库内触发的 `__system_property_get/open/read/ioctl`
带**参数值快照**(属性名+值、文件路径+读入的前 N 字节、ioctl 解码)→ 得到
**完整输入图 + 每个种子的实测值**。上一轮 getguid_init.json 是全 App 启动噪音
窗口,需要收紧到库内。
- **E3**IMEI 变更(root + 模块)→ getGUID 变不变?(确定 IMEI 是否在 S 中)
- **E4**:补记 E2 之外的硬锚路径实测值(`/sys/devices/soc0/serial_number`
`/proc/cmdline` 的 androidboot 段、`getprop ro.boot.*` 值)→ 这些直接把"不可改
硬件"变成"可仿真输入"。
**副产品(对拍语料)**:上述每次变更都记录 `(S 的逐项值 → 5 个输出值)`,形成
**差分语料库**。这是 E2 之外静态移植的主要"答案纸":能直接看出哪个输出依赖哪些输入、
以及单输入变化时的输出变化模式(MD5 雪崩 vs 局部字节变化 → 立刻排除/确认构造)。
## 五、执行路径对比
### 路径 A:纯静态还原 → 纯 Python 生成器(最终形态,周级)
1. E2 输入图 + E4 硬锚值 → 复原"种子串拼接格式"(大概率就是若干固定前缀 + S 逐项);
2. 用金样本 pair(本节 §2.2 常量 + 真机 S)在 Python 里**穷举组合对拍**
`MD5(盐‖S)``SHA1(盐‖S)``AES-ECB/CBC``HMAC-MD5/SHA1` × 各候选密钥/盐;
3. 命中即纯 Python 版本直接成立(**无需解一个字节的 OLLVM**);未命中再走 Ghidra+
锚点反汇编看拼接/变换细节;
4. 产出 `tools/hdid_mint.py` + `core/huya/device_mint.py`(零 native 依赖)。
要点:dump 已解密、原语已定位、密钥候选已提取 —— 静态路上最贵的 "OLLVM
平坦化全解" **不是必需项**,先用黑盒对拍把算法筛出来。
### 路径 B:unidbg 仿真执行(天级,**推荐先行**)
1. 装 JDK(本机 JVM 失效:`/usr/bin/java` 空壳、sdkman 空目录 → brew/sdkman 装
OpenJDK 17/21);拉 unidbgzhkl0228/unidbg);
2. 把 decrypt dump 载入 ARM64 仿真,hookJNI env、`__system_property_get`
`open/read``ioctl`、TelephonyManager 等 JNI 桩;
3. **金测试**:喂真机 S 实测值 → 期望输出 `0a7dfaa8...`;对上了 = 整个算法已在
PC 上可执行;
4. 之后每套虚拟 S 跑一次 init+get* 即铸一套身份 —— **这就是"算法生成"的落地形式**
5. 风险:OLLVM 仿真慢(毫秒级可接受)、反调试(TracerPid 读取,可 hook)、
若算法依赖 Java 服务调用需补 JNI 桩(init 是 `()V` 无参,依赖面小)。
### 路径 C:有根模拟器 / 重装循环(备用,仅对拍用)
lib 内置大量模拟器检测串(qemu_pipe / mumuvmm / genymotion / windroyed…),
此前模拟器直接闪退;且服务端对模拟器身份可能单独风控。**不作为主路径**。
## 六、服务端验收闭环(从第一天起就验,避免白做)
- 验收口径:铸出的身份 → 现有**纯代码 dfpReport 设备注册链**`tools/dfp_gen.py` /
`core/huya` 已有零设备注册)+ **现有 WUP 密码登录链**`core/huya/app_login.py`)→
拿到有效 Cookie/登录态 = 服务端接受该身份。
- 关键测试:用**每个账号一套全新虚拟身份**跑完整登录(替换现在全账号共用的金样本
`ed0db8...`),观察:注册是否成功、登录是否放行、滑块/风控是否升级。
- 若服务端只校验"格式/自洽/已注册",铸币路径畅通;若校验设备库真实性,则需评估
深层对抗(那是另一个量级的问题,先实测出结论再投入)。
## 七、推荐执行序列(蓝线)
| 阶段 | 动作 | 依赖 | 产出 |
|---|---|---|---|
| D01~2 天) | E1/E2/E3/E4 真机实验 | 真机 + 稳定通道(现成) | 输入图、种子实测值、差分语料库 |
| D1(并行) | Python 黑盒对拍(§五-A2 | D0 语料 + §2.2 常量 | 是/否命中标准构造 |
| D1(并行) | 路径 BJDK + unidbg + 金测试 | D0 输入图 | 仿真铸币机(保底形态) |
| D2 | 铸币 → 注册 → 登录闭环实测 | D1 任一成果 | 服务端接受度结论 |
| D3 | `device_mint.py` 接入 `app_login.py`(每账号独立设备) | D2 通过 | 生产落地 |
## 八、风险清单
1. 种子含 TEE/Keystore 级硬锚且无再生路径 → 仿真下仍可铸(S 自拟),但服务端可能
拒绝"无对应真实硬件"的身份 → 以第六节实测为准;
2. §2.2 常量为运行时下发替代 → 对拍用真机实测值逐个对账,避免拿死值硬套;
3. qimei 参与 GUID 时,qimei 本身依赖 AndroidID/IMEI → 铸币需把 qimei 一并纳入
虚拟 S(unidbg 下可控);
4. OLLVM 全量反平坦化(数周)**不作为必经路线**——只有黑盒对拍全失败才需要;
5. 服务端风控升级(针对新设备登录的滑块/验证)→ 已有 safe_auth 自动过验能力可兜底。
---
## 九、执行进展(2026-08-28 晚,本次会话落地)
### 9.1 输入图彻底打开(真机 E2 + unidbg 双证)
- **库窗口内系统调用只有三类**:反模拟器 faccessat 探测(qemu/mumu/nox/bluestacks 路径)、
反调试 `/proc/self/task/*/status`(TracerPid)、自身持久化文件检查。**零硬件文件读取**。
- **种子不在属性层**X1 改 `ro.product.*` 全家族、X2 改 IMEI 属性/`ro.boot.cpuid`/serialno
GUID 均不变(与全清数据 E1 一致)。属性只参与上报,不影响 GUID。
- **JNI 面由 unidbg 暴露**`NativeEntry.init()``AppGlobals.currentApplication`
`getSharedPreferences("table")` → **读/写 key: `hydeviceid_config`(服务端下发) `androidId`
(加密态) `old_androidId` `cdid_version` `hydeviceid_guid`** → `NativeBridge.b(102)`(=MID/androidId)
`getDataDir`。getGUID 读 `hydeviceid_guid` → 空则 `WupHelper.getGuid()``NativeBridge.b(100)`
### 9.2 unidbg 金测试通过(决定性)
- 解密 FULL dumpr--+rw- 全段,`phone_dump_hydev_full.py`+ 解密数据合入原文件
`merge_decrypted.py`,unidbg 重定位自会覆盖指针槽)。
- 喂真机种子(ANDROID_ID / hydeviceid_config / NativeBridge.b 实测值):
- **`androidId` pref = `PDSuHxt854IZRR5Y7AGyaQ==` —— 与真机逐字节一致**(采集器 AES 加密可复现)
- **getGUID = `0a7dfaa882938a6ab502511452142c57` —— 与真机一致**
- 工具链:JDK21brew+ unidbgModule 冲突补丁/JDK21、getrlimit/setrlimit 兜底)+ harness
`tools/unidbg/hydev`JNI 桩:NativeBridge.b/klog、Settings.Secure、SharedPreferences 日志化)。
### 9.3 遗留的最后一块(下一轮主攻)
- **`NativeBridge.b(100)` 的原生实现归属库未定**:不在 libhydeviceid(静态无 xref、注册表 23 条
无它)、不在 libudbauthunify(解密 dump 无该串)、不在 libnative-device-util/libjsiwup。
RegisterNatives 长窗口探测全程 0 次命中(时机/环境因素存疑)。当前 harness 用**真机实测值桩**顶替。
- **结论修正**:此前"单机不可铸造/硬锚 SoC"判断错误 —— 实际链路是
`GUID = b(100) = f(androidId加密态, hydeviceid_config(服务端), MID)`,输入全部可控可计算。
- 下一步两条线(任选其一即可闭环铸币):
1. **unidbg 双库/三库加载**:找到并加载 b 原生所在库(可疑:运行期 dex 动态下载的另一个 .so,
需在真机 dlopen 全拦截清单里补齐),让 b(100) 真实计算;
2. **静态还原 b(100) 公式**:基于 (ANDROID_ID→GUID) 差分对拍(unidbg 内改 ANDROID_ID 即可批量
产对),用 .data 密钥(`7jbF5I...`/`HuyaUdb...`/config 解密链)猜 MD5/SHA1/AES 结构。
## 十、Hook 阶段结果(2026-08-28 深夜续)
### 10.1 NativeBridge 归属排查(穷尽运行时手段)
- **动态 JNI 排除**:全模块 `Java_*` 导出扫描,自研库仅 libhypopup/libjsiwup/AI 库有——无
`Java_com_huya_security_hydeviceid_NativeBridge_*`
- **RegisterNatives 排除**:①v7 注册表(23 条)= NativeEntry(19@hydeviceid)+HuyaAuthCore(4@udb)
无 NativeBridge;②dlopen→JNI_OnLoad 拦截各库(hydeviceid/udb0 命中;③健康进程 attach+
重触发 init/getGUIDRN 全程 0 命中。
- **unidbg verbose 实锤**hydeviceid JNI_OnLoad 只 `FindClass(NativeBridge)+NewGlobalRef`(不注册),
udb JNI_OnLoad 只注册 HuyaAuthCoreNativeBridge.b/klog 在 unidbg 内经 JNI 以"未注册 native"
路由到 AbstractJni(桩)。
- **定位到的关键静态物证**hydeviceid .data@0x3e21e4 起为内联方法描述符块
`klog()(L...Z)V``b (I)L..``c (I)J``h ()L..``g (L)L..`、getDataDir/getPath…),
类名字符串 `com/huya/security/hydeviceid/NativeBridge`@0x3e2f20 紧随其后;但代码无任何
ADRP/GOT 引用该页(间接/运行期构建)。完整 {name,sig,fnPtr} 指针表未以静态形式存在。
- **新输入候选**:采集器还读写 `udb_deviceID`/`udb_deviceID_encode`/`oaid`prefs+
`NativeBridge.b(104)`oaid 相关);`Build.*` 静态字段读。
### 10.2 双库 unidbg 已打通但 b 仍未真注册
- libudbauthunifyJNI_OnLoad@0x262620RN 调用点 0x2629bc)解密合并后 unidbg 加载成功;
- merge_decrypted.py 升级为**段驱动**vaddr→文件偏移,支持 .rodata/delta 各段)+ .dynamic 保留
+ .init_array 置零 + 仅重定位槽指针清理(避免误伤字符串);
- 卡点:NativeBridge natives 从未在任何可见路径注册 → b(100) 仍走桩。
### 10.3 设备稳定性注意(重要)
- 反复 `pm clear` + frida attach 探测会清空应用数据(弹协议)并导致 App 闪退;
已确认无 frida 冷启动 App 健康(首页焦点正常)。**后续禁 pm clear、attach 探测收敛**。
### 10.4 静态还原重启点(step-2 方案,全部 PC 侧)
1. **歧义清除**:确认 NativeBridge.b/klog 到底是否 nativedex 反射 isNative,用 SPAWN+bypass
轻探,一次即可);若 native → 在 hydeviceid 代码中用"运行期构建表"模式(构造
{name,sig,fnPtr} 时 ldr 常量页)找注册函数;若 Java → 直接读运行期 dex 里 getGuid/b 逻辑。
2. **oracle 差分**unidbg 侧可控 ANDROID_ID/Settings.Secure 已证明 androidId 密文会变;
b 一旦真注册,`(ANDROID_ID → GUID)` 差分对即可在 unidbg 内批量生成 → MD5/SHA1/AES 对拍。
3. b 的 fnPtr 兜底获取:运行期 dump hydeviceid .data 时同时读 JNINativeMethod 运行期构造表
spawn+bypass 一次抓全,附 base 校验)。
## 十一、静态还原定案(2026-08-29 凌晨,PC 侧完成,无需 SPAWN/真机)
### 11.1 isNative 判定 = 纯 Javadex 直读,比 SPAWN 更硬)
- 工具 `tools/dex_probe_native.py`(dex 解析器)扫全部 18 个 classes*.dex
- `NativeBridge`classes5.dex):`b(I)L..;``a/c/g/h/klog/i1/i2/j` **全部为 Java**(有 code_off),
**无任何 native 方法** —— 10.1 的怪象全部解释:JNI_OnLoad 只 FindClass+NewGlobalRef 是因为
没有任何 native 需要注册;.data@0x3e31e4(vaddr 0x3e41e4) 的描述符块(klog/b/c/h/g/getDataDir/getPath
+类名 com/huya/security/hydeviceid/NativeBridge + com/duowan/biz/wup/WupHelper) =
**native 代码运行期 GetStaticMethodID 反向调用 Java 的 name/sig 常量表**
- `NativeEntry`getGUID/getCDID/getHDID/getMID/getSDID/init 全部 nativecode_off=0RN 19 条已实锤)。
- `NativeBridge.b(I)` = `fj3.c(I)` 分发器(DeviceInfo.java),`b(100)``pnc.c()` → 静态 `pnc.a`
### 11.2 GUID 值流完整还原(GUID ≠ 本地指纹公式!)
- **native getGUID = 纯缓存 getter**unidbg 旧配置金测试实证):
`pref hydeviceid_guid` → 空则 Java `WupHelper.getGuid()``NativeBridge.b(100)` → 回写 pref 并返回。
即 unidbg 金测试的"GUID 一致"= Java 桩直喂 golden 的闭环,**从未验证过本地公式**。
- Java 链(每环唯一调用者/写入者,交叉验证):
```
YYProtoSdkModule.initHuyaUdbSdk (classes17.dex, code@0x3834d0)
→ HyDeviceProxy.j(HY_APPID, WupHelper.getGuid(), mid) (setAppInfoId)
→ pnc.k(guid) → pnc.a
→ NativeBridge.b(100) → fj3.c(100) → pnc.c()
WupHelper.getGuid() (classes12.dex) = ((IHal)hlf.i(IHal.class)).getGuid()
→ HalImpl.getGuid() = sGuidProperty.get()
→ sGuidProperty 仅两处写入: initCache() 与 initialInner 网络回调 →
LiveLaunchRsp.sGuid (live-launch/doLaunch 服务端响应, 32hex 硬校验: 非32位触发 mIllegalGuidCallBack)
```
- **结论修正(推翻 §9.3"GUID=f(androidId密, config, MID)"**:铸币本体不是本地算法,而是
**服务端 doLaunch/device_register 按设备指纹签发 sGuid**。doLaunch 请求携带
`LiveUserbase{tUAEx.sDeviceId(Config 持久化), sIMEI, sMId} + tId(userId.sGuid=当前guid, lUid)}`
响应 `sGuid` 即后续所有登录帧 tag0 的 32hex hdid。⚠️ 旧假设 —— 已于 §11.10 证伪: GUID32(sGuid) ≠ HDID32(登录帧t1.t0)。
- 与既有认知吻合:旧实验"改 ANDROID_ID/全清数据 GUID 不变"(服务端签发+本地缓存)、
`tools/` 注记"hdid 服务端硬锚/不可铸造"。
### 11.3 新工具(PC 侧,无设备依赖)
- `tools/dex_probe_native.py`dex 类/方法 access_flags 判定(含 class_data fields 跳过修复)。
- `tools/dex_scan_calls.py`:跨 dex 指令级扫描指定方法的调用点(找 setAppInfoId→j() 即用此)。
- 用法:`python3 tools/dex_probe_native.py work/apk_extract` /
`python3 tools/dex_scan_calls.py work/apk_extract Lcom/hy/HyDeviceProxy; j "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"`
### 11.4 unidbg 金测试回归修复(重要教训)
- 症状:新 harness(加入 libudbauthunify 第二库 + 扩展桩)后 getGUID/getCDID/… 全 null
init 阶段 Dynarmic "assertion failed" 崩溃(PC 跳 libc++ 零区)。
- 根因:**C++ locale/facet 路径**`std::locale::use_facet`、`ios_base::getloc` 等)在 unidbg
内置 libc++ 上 ABI 不兼容;第二库加载改变了执行路径触达该处。
- 修复/验证:**14:16 金测试同款 harness(无 udb 第二库,git 70621cf+ 当前 merged so →
getGUID = 0a7dfaa882938a6ab502511452142c57 MATCH**,并首次实证 native 缓存写回流程
WupHelper.getGuid→null → b(100)→golden → prefs-put hydeviceid_guid → return)。
- dump 真实运行基址 = **0x7814200000**(非 probe 记录的 0x7813211000);merge v3 重定位结果
正确(merged = dump - 0x7814200000 = 模块内偏移)。
### 11.5 铸币路径重定义(下一步蓝线)
1. **服务端签发复现**:用现有纯 Python WUP 链(tools/huya_wup_encoder.py 等)构造虚拟设备指纹的
`doLaunch`/`device_register` 请求(deviceId/IMEI/mid 取自虚拟 profile)→ 收 sGuid →
**先验确定性**:同一指纹多次请求 sGuid 是否一致 / sGuid 是否 = f(请求设备字段)。
2. 若确定性签发:`device_mint.py` = 虚拟指纹 → doLaunch → sGuid →(可选 dfpReport 注册)
→ 现有 WUP 登录链验证服务端接受度(用户既有"每账号一套全新虚拟身份"验收口径不变)。
3. 若 sGuid 随机签发(仅库内下发),则评估"锁定身份"的现实等价物:设备指纹→注册→登录
全链路用同一份虚拟 profile 保持一致性,铸币=整套身份模板随机化。
- 真机/未决项:sGuid 签发是否依赖 qimei16/36、hdid/cdid/sdid 上报(采集器还是把设备信息上报
给 dfpReport 侧),这些在 §六 的注册链实测中一并验证。
### 11.6 doLaunch wire 已离线还原(2026-08-29 续)
- **JCE struct 规格**dex writeTo 精确还原,全部位于 classes9/com/duowan/HUYA):
- `LiveLaunchReq`t0=tId(UserId) t1=tLiveUB(LiveUserbase) t2=bSupportDomain(int16)
- `LiveUserbase`t0=eSource(=2) t1=eType(=1) t2=tUAEx(LiveAppUAEx)
- `LiveAppUAEx`t1=sIMEI t2=sAPN t3=sNetType t4=sDeviceId t5=sMId
- `UserId`(classes11)t0=lUid(int64) t1=sGuid t2=sToken t3=sHuYaUA t4=sCookie
t5=iTokenType t6=sDeviceInfo t7=sQIMEI
- `LiveLaunchRsp`**t0=sGuid**(铸币目标) t1=iTime t2=vProxyList t3=eAccess t4=sClientIp
- **信封**UniPacket(同密码登录);servant=`launch``@WupServant("launch")`),func=`doLaunch`
sBuffer=map<string,bytes> 键 `_wup_data`4B 大端长度前缀。
- **工具 `tools/huya_launch_mint.py`**build_live_launch_wup(profile)→请求字节、
parse_launch_rsp(bytes)→候选 sGuid(递归解 SIMPLE_LIST 载荷)、`--self-test`(回环)/
`--dump`/`--live [url]`。默认虚拟 profile 见 DEFAULT_PROFILEmid 16hex、imei 15 位、
device_id 32hex、guid=""→服务端签发)。
- **传输待 live 实测确认**hyns/KiwiServant(a09)栈的最终 URL/头(先按 wup.huya.com 平铺,
与服务端对拍后再校准);响应 sBuffer 可能是 map<string,JceStruct> 假象(解析器已兼容
bytes/struct 两种形态,sGuid_candidates 兜底)。
### 11.7 doLaunch live 对拍结论(2026-08-29 实测,wire 已 App 对齐)
- **已确认 App 侧全部细节**(bjj/cjj 构建器反编译):
- `a09.getRequestKey() = "tReq"``getResponseKey() = "tRsp"`
- `bjj.a(servant,func,key,req,otherParams)` + `new UniPacket(true)`= v3 + _newData
map<string,byte[]>);值 = `write(struct,0)` = struct_begin(0)+fields+endmap 值头
SIMPLE_LIST(0x1d) 与登录黄金帧逐字节同构;
- **URL 路径 = /launch/doLaunch**hyns 约定 `"/"+servant+"/"+func`);
- **sBuffer 额外键**a09.getOtherParams() 追加 `platform/version/channel/(yyuid/uid/imei)`
u9a.put 裸字符串值,map 头含全部键,实测服务端接受此形态);
- **传输类型服务端动态下发**:`FunctionTransportModule.getTransportType("launch#doLaunch")`
查动态配置 mFunctionTransportMap(缺省 mDefaultTransportType)——HTTP 是否 doLaunch 真渠道
未定;zij.a().b() 还支持 cdn.wup.huya.com IP 列表直连 :80(明文 HTTP/WS 形态)。
- **live 实测序列**(全部 HTTP 200,服务端回声 servant/func + requestId):
| 变体 | STATUS_RESULT_DESC |
|---|---|
| 键 `_wup_data` / 根路径 | `UniAttribute not found key:tReq` |
| 键 `tReq`(值=wrapped/SIMPLE_LIST) | `read 'struct' type mismatch, tag: 0, get type: 12.` |
| 键 `tReq` 值=裸字段/STRING4/guid=空/黄金guid/5键 map | 同上(**恒定** |
| 值=0x0c 单字节 | 完全复现同错(=tag0 ZERO,服务端确实在解析值) |
| 值=空 | `require field not exist, tag: 0`(解析走到流尾) |
| 无 tReq 键 | `UniAttribute not found key:tReq` |
- **结论**:信封/键/路径已被服务端接受;tReq 值永远被拒且错误与内容无关 → 最可能 =
服务端对 tReq 值的解析语义与本地 djce 版本不同(或 HTTP 非 doLaunch 真渠道)。
悬而未决点,定案需 App 真实 doLaunch 帧:
- 约束内路径全试过:logcat 无 klog(自定义 xlog 落盘);xlog/mmap2 拉取为 mars 压缩
格式,无公开魔数解不动;17:17 冷启动走 `initCache from disc`(缓存未过期 → 不发
网络 doLaunch);禁 pm clear ⇒ 无法制造首次启动场景;无模拟器;spawn/attach 收敛。
- 可选项:① 授权一次"代理+系统CA"被动抓包(可逆,无 pm clear/attach);② 装模拟器
全新安装(必然触发 doLaunch);③ 继续纯 PC 侧拆 mars xlog。
- 前置条件不变:**live 复测前先落 docs §11.5 步骤 1 的确定性实验设计**(同一虚拟指纹
多次请求 → sGuid 是否一致 / 是否 = f(字段))。若 sGuid 随机签发,则铸币=整套身份模板
随机化后由登录链验证服务端接受度。
## §11.8 铸币机打通:doLaunch 结构 bug 定案 + 服务端确定性签发 (2026-08-28)
### 破案链
1. **真机全通道对拍**:多轮被动抓包/WG 全解密/Frida SSL 明文 — wup.huya.com 之外的
launch servant 真帧 (queryHttpDns) = **map 只有 tReq 一个键**,值 = JCE struct:
`0a`(struct_begin tag0) + 字段…… (非此前追加多键 + 0x0c 包装)。
2. **tReq 恒拒根因**`encode_live_launch_req` 少写外层 struct_begin(0) — UserId 结构体
被直接顶到顶层,服务器解析 LiveLaunchReq 时 tag0(tId) 读到 UserId 内部 lUid 的
ZERO_TAG(0x0c=type12) → 恒定错误 `read 'struct' type mismatch, tag: 0, get type: 12`。
3. 修复 = 双层 struct_begin(0) + 收尾 struct_end() + map 只留 tReq 键。
### live 实证 (POST https://wup.huya.com 根路径)
| 指纹 | sGuid (tRsp.tag0, 32hex) | 确定性 |
|---|---|---|
| 默认 (mid=1e8bdf7d4f7a01d3, device_id=f2f6…) | `0a7dfaa882938a6ab502511452142c57` | 多次同值 ✓ |
| mid=9c41d0a7b3e5f281 (+device_id 换) | `0a7dd9dc1190916aba02dcf4ddf45b78` | 两次同值 ✓ |
| mid=31415f26a7c8b9d0, imei=861234… | `0a7d90756f90916a9e023aa03ba0e901` | 两次同值 ✓ |
| 仅 device_id 变 (mid 不变) | = 默认值 | device_id 不驱动 |
**结论:sGuid = f(mid)** —— 服务端对同一 mid 确定性签发同一 sGuidmid 一换 sGuid 即变。
mid = LiveUserbase→tUAEx→t5=sMId (16hex)**可任意铸造** → doLaunch 收 sGuid →
§11.10 更正: GUID32(sGuid) 与 HDID32(登录帧t1.t0) 是两套独立 32hex 体系, sGuid 不能作登录 hdid。
§11 旧结论"GUID 唯一不可铸造"正式推翻;
**纯算法铸币闭环 = mid(铸) → doLaunch → sGuid → 登录链**。
### 工具落地
`tools/huya_launch_mint.py`:
- `encode_live_launch_req` 外层 struct_begin/end 修复;
- build 只发 tReq 单键 (对齐真机帧);
- `parse_launch_rsp` 支持 gzip + `\x06\x20(32hex)` sGuid 可靠提取;
- `--live` 一条命令出 sGuid; 确定性矩阵可复现。
## §11.9 铸币机接入登录链验收 (2026-08-28 夜, 3 个测试账号)
### device_mint.py 落地
`tools/device_mint.py` (编排器):
- `--mint-only [mid]` : mid → doLaunch → sGuid ×3 确定性
- `--login <acct> <pwd> [--mid X]` : 铸币 → 零设备注册链(gen_fresh_identity) → WUP 登录(铸币 hdid)
- `--control <acct> <pwd>` : 金样本 hdid 登录 (对照基线)
- `tools/huya_launch_mint.mint_sguid()` : 单函数铸币上线 (gzip + sGuid 提取)
### 验收矩阵 (账号 hy_300030430/aa778899)
| 路径 | 结果 | 解读 |
|---|---|---|
| 零设备注册链 (getDfpConfig→selectOperator→dfpReport) | ✅ HTTP200, 新鲜 action(180B) + device_id(40hex) | 注册链仍在线 |
| 铸币 doLaunch (mid=aabbccdd00112233 ×2) | ✅ `0a7d910eb99a916a3801d7b0d8b02bce` 两次同值 | sGuid=f(mid) 确定性 |
| 铸币 hdid 登录 (hdid=上述 sGuid) | ❌ 652B `APP_SIGN_NOT_MATCH` "APP签名不匹配" | 签名校验拦截 |
| 对照: 金样本 hdid 登录 (ed0db8…) | ⚠️ NEED_RISK (safe_auth 滑块 URL) | **签名通过**, 进风控 (非签名错误) |
### 结论: 铸币机的边界精确化
1. **sGuid(32hex hdid) = 可铸** —— doLaunch 按 mid 确定性签发, 服务端接受度 ✅ (§11.8)。
2. **登录链的签名 = 独立硬锚**: hypasswordLogin 的 APP_SIGN = native
(libudbauthunify/libhydeviceid) 私钥签名覆盖请求字段 (hdid/app_version 换值即错),
与 doLaunch 的 sGuid 是两套体系 —— 铸币 hdid ≠ 签名证书, 登录被签名层拦截。
3. 旧结论"32hex hdid 不可铸造"的准确表述修正为:**hdid 可铸造** (服务端签发),
但 WUP 登录需要 native 私钥证书重签 (注册链/OTA/safe_auth 证书) ——
**登录签名的纯代码复刻 = 下一道关** (记录: AESkeyMgr 钥表 + bigaes 事件已定位, 未复刻)。
4. device_mint 的登录接入 = 完成到"签名边界"可复现: 铸币→注册链→登录→APP_SIGN 恒定复现。
## §11.10 登录 hdid 隔离实验: sGuid ≠ 登录 hdid (两个 32hex 体系) (2026-08-28 夜)
### 隔离实验 (金样本账号 hy_300023887, 同一构建器, 仅换 hdid)
| 请求 | 响应 | 解读 |
|---|---|---|
| 原样重放证据金样本 bin (旧 session/旧 requestId) | 1571B 完整登录数据 | R3 奇迹今夜复现 — **登录链 cred 可用** |
| Python 构建 金样本hdid (ed0db8…) | 1571B OK-data | 构建 = 100% 金样本 (1B 差无关) |
| 同构建 铸币hdid (0a7d91…, doLaunch sGuid) | 652B APP_SIGN_NOT_MATCH | **唯一变量 = hdid** |
### 决定性澄清: 手机同时存在两个 32hex, 之前文档混为一谈
- **GUID/sGuid** = `0a7dfaa882938a6ab502511452142c57` (HySignalGUIDCache) ——
**doLaunch 按 mid 签发**, 铸币机已打通 (§11.8)。
- **登录 hdid** = `ed0db8334cadd236c00cadf7e11ab5a5` (golden) —— **登录帧 t1.t0**,
格式同为 32hex 但= **另一套服务端校验体系**: 换值 → APP_SIGN_NOT_MATCH 硬错。
- 目标原始假设 "sGuid → 登录帧 tag0" = **被证伪**: doLaunch 的 sGuid 不能直接作登录 hdid;
登录 hdid 是 native(libhydeviceid/libudbauthunify 私钥)签发的设备证书, 服务端按
证书(而非注册链)校验 —— 纯 Python 铸造 sGuid 后仍需:
(a) native 私钥证书重签 (unidbg 侧差分 / AESkeyMgr 钥表) 或
(b) 若 hdid=服务端可重签 token, 找到签发 hdid-cert 的 HTTP 通道 (device_register/OTA 变体)。
### 已固证据 (evidence/mint/)
- login_golden_acct_golden_hdid.bin (1571B cred 响应)
- login_golden_acct_mint_hdid.bin (652B APP_SIGN)
- login_golden_hdid.bin / login_minted_hdid.bin (测试账号版)
- doLaunch_rsp_gzip_313B_partial.bin
### §11.10 补充: hdid 全链路不可见 (2026-08-28 夜实测)
- 登录 hdid (ed0db8…) **不出现在**: 全部 WG/mitm 解密流量(WUP/HTTP 各帧)、
Frida SSL 明文、shared_prefs、files —— 唯一出现点 = 登录帧 t1.t0 本身 + 旧 frida 的
FOUND_HDID_32 (lib setSafeDeviceId 内存事件)。
- 推论: hdid-cert = 设备首次初始化时 native(私钥)签名 + 服务端侧存档;
服务端按"该 hdid 是否存在于注册设备库"校验 (APP_SIGN)。无上行明文通道可抓,
纯代码复刻 = 需 unidbg 重放 native 签名 (AESkeyMgr/bigaes 钥表线索已记录)。
## §11.11 标识符全清单与去混 (2026-08-28 夜, 用户要求彻底厘清)
### 设备上/证据中的全部 hex 标识 (逐一核实)
| # | 值 | 长度 | 角色 | 来源/渠道 | 可铸造? |
|---|---|---|---|---|---|
| G1 | `0a7dfaa882938a6ab502511452142c57` | 32 | **GUID/sGuid**: HySignalGUIDCache.xml + hysignalconfigcache 双处一致 | doLaunch 按 mid 签发 (mid=1e8bdf7d=手机当前mid) | ✅ **可铸** (§11.8) |
| L1 | `ed0db8334cadd236c00cadf7e11ab5a5` | 32 | **登录 hdid** (WUP hypasswordLogin t1.t0) = GOLDEN | native 生成 + setDeviceInfo 上报, 不上行明文 (§11.10) | ❌ **纯Python不可铸** |
| D1 | `5b434c778490889697170e225029f56aff19ca47` | 40 | HYSIGNAL_DEVICE_ID_KEY (当前手机) | hysignal 缓存 | ? (未研究) |
| D2 | `7c5387e0539c023c31c4ff0e807e7256117385ee` | 40 | 登录帧 t2.t8 device_id (GOLDEN) = 库 getHDID 期望 | libhydeviceid getHDID@0x20f0c8 | unidbg 可调 |
| D3 | `02df398797432eadefcc12767119ad5e80999389` | 40 | CDID (库 getCDID 期望) | libhydeviceid | unidbg 可调 |
| M1 | `1e8bdf7d4f7a01d3` | 16 | mid (库 getMID 期望 = 手机当前 mid = 铸币默认 mid) | libhydeviceid getMID@0x20fba8 | ✅ 可铸 (任意16hex) |
### 当前在搞哪一个 (定论)
**登录帧 t1.t0 的 32hex hdid (L1)** —— 它就是目标。决定性实验 (金样本账号,
仅换 hdid): 手机当前 GUID (G1) 作 hdid ❌ 被拒; 金样本 hdid (L1) ✅ 出 cred。
### 已完成 / 未完成
- ✅ doLaunch 铸币机: sGuid (G1 系) = f(mid), 确定性, live 多次复现
- ✅ 零设备注册链: 每账号新鲜 action/device_id (t2/t5)
- ✅ 登录管线: 金样本 hdid + 新鲜 action → 1571B cred (纯 Python 出 cred 全链路通)
- ❌ **登录 hdid (L1) 的纯代码铸造**: 与 GUID 无关 (手机自己的 G1 也被拒);
它只存在于登录帧 + native 内存 (setSafeDeviceId), 无 HTTP 签发通道 (§11.10 全链路不可见)
- ❌ unidbg 重放 32hex-hdid 生成: 库已定位 getGUID/getCDID/getHDID(40hex)/getMID,
**32hex 登录-hdid 的生成函数尚未定位** (getHDID 是 40hex, 非登录 hdid)
### 下步(未完成工作)
1. libhydeviceid 内定位 32hex-hdid 生成函数 (unidbg 枚举 32hex 返回的导出/偏移)
2. 若存在: 输入=?(androidId/config/MID 模型已有) → 铸币设备独立 hdid → 登录
3. 若不存在: 登录 hdid = 设备级私钥证书, 纯代码不可达 → 每账号独立设备需真机/模拟器池
## §11.12 Java 侧 HDID32 链全映射 + 签名锚定实验 (2026-08-28 夜)
### Java 调用链 (jadx classes5.dex 实证)
```
HuyaAuth.initSDID() (com/huyaudbunify/HuyaAuth.java:1261)
├─ sdid = dj3.getSDID() → HyDeviceProxy.g() → NativeEntry.getSDID() ← ACTION(注册链t2)
└─ hdid = dj3.getHDID() → HyDeviceProxy.e() → NativeEntry.getHDID() ← native getHDID@0x20f0c8
(setSafeDeviceId(sdid, hdid) → WUP 帧绑定)
NativeBridge.b(i) 语义全表 (b→fj3.c):
1=APPID(5008) 2=versionName(13.4.22) 3=SDK版本(1.14.68) 5=安装时间json
100=GUID 101/102=MID(ANDROID_ID) 104/105=空 106=64hex
107=qimei16 108=qimei36 300/303/304=...
```
- 登录帧 t1.t0(HDID32) 的 JAVA 源头 = NativeEntry.getHDID() (registerNative@0x20f0c8)。
- unidbg 收集器 b(3)=SDK版本 已补桩 (此前 null 导致收集不完整), 但 getter 仍读缓存
(GUID/HDID 均为缓存读, 缓存由 JAVA 侧 doLaunch/注册链回填) —— 下步: 仿真 JAVA 回填序。
### 签名锚定实验 (金样本账号, 逐变量)
| 变量 | hdid(t1.t0) | device_id(t2.t8) | 结果 |
|---|---|---|---|
| 全金样本 | ed0db8334cadd236c00cadf7e11ab5a5 | 7c5387e0…(40hex) | ✅ 1571B cred |
| 换 device_id | ed0db8… | **deadbeef…(随机40hex)** | ✅ **1571B cred** |
| 换 hdid | **0a7dfa…(手机当前GUID32)** | 7c5387e0… | ❌ APP_SIGN_NOT_MATCH |
| 换 hdid | **0a7d91…(铸币sGuid)** | 7c5387e0… | ❌ APP_SIGN_NOT_MATCH |
**定论: 登录 APP_SIGN 唯一锚 = HDID32(t1.t0); DEVID40 自由; GUID32(铸币)≠登录证书体系。**
## §11.13 32hex 派生被证伪 + AESkeyMgr 钥表清单 (2026-08-28 夜)
### MD5/sha1 派生实验 (6 组合全拒)
hdid = md5(AID)/md5(MID)/md5(AID+MID)/md5(AID+CFG)/md5(AID+MID+CFG)/sha1(AID)[:32]
→ 全部 APP_SIGN_NOT_MATCH (对照金样本 ed0db8 = 1571B cred)。**32hex ≠ 无钥哈希**。
### libudbauthunify_merged.so 明文钥表 (24 字符 base62, rodata)
已定位 16 把 + AESkeyMgr::C2@0x26fcc8 装载钥:
| 钥 | VA | 备注 |
|---|---|---|
| `owNMiaCgcHmqoTr3iRamFuHj` | 0x1b898b | **C2(26fcc8) 实际装载** (std::string assign) |
| `KFWAH2cbkkNgFAgQPzriFPcU` | 0x1b9f27 | R2 旧记录 "请求签名专用钥" 候选 |
| `ybEWdvkjGjPQa2ugireHJCLL` | 0x1b9f40 | R2 "邻串" |
| `4VYcPdvKKqjBHZtCmbroRXHk` | 0x1bc810 | 证书钥族 (R2 记录) |
| `xXEDWqiKLGwEZ6HubEiswCqK` | 0x1bc829 | 证书钥族 |
| `3FMHubdKosFrhmXNLHTNHZwe` | 0x1bc842 | 证书钥族 |
| alkdfEINVBE4fjhidfioYgMN / asdfhSHFSDFI2sSDjksSDFks / dzTkHQDPmAsPVgEhrwWs7RzP / hgtuioiulbsdjwEHHMYU5gjt / fNoMrJhbEMXm8nHcHXTNovaL / jBxBvaruXYzrvNsacsAD4BfT / masldDSIFGJjdfio5hkhsSDF / novwSHidrhDg1ADUKLkejnHR / nskdI7MDGKSDJsnadjdoonvs / nvirAGiohDsfdiHDSUIGH5A9 / qZsBbgYpBBcdkUFp3qfaFRCW / ubPzXJwj6hrYxPYYjnpePrzJ / whdFNGFdfJHMJbdfg6jhdsFG / xnkdDFIERRIPT5dfhfgiJ0FD | 分散 | 疑似 AESkeyMgr 全钥表 (C1/C2/… 各 id) |
完整清单: evidence/mint/udb_keytable_24char.json
### 下步 (unidbg 执行路线)
C2/钥表已定位 → 下一步 = unidbg 调 AESkeyMgr/C2-相关 sign/AES 函数 (hyudb_otp_encrypt@0x32fa24,
UdbAESUtil::encrypt, xxtea...) 以可控输入差分 → 复刻 32hex-HDID32 生成。
## §11.14 AESkeyMgr 钥表按 id 注册 + encode_aes API 定案 (2026-08-28 夜)
### AESkeyMgr::C2@0x26fcc8 = 钥表构造 (按 id 注册 24 字符钥)
| id | 钥 | VA |
|---|---|---|
| 1 | `ZMHAVPRaxJ3MtXDjduUnXAKQ` | 0x1c155e |
| 2 | `4VYcPdvKKqjBHZtCmbroRXHk` | 0x1bc810 |
| 3 | `xXEDWqiKLGwEZ6HubEiswCqK` | 0x1bc829 |
| 4 | `3FMHubdKosFrhmXNLHTNHZwe` | 0x1bc842 |
| (C2 前置装载) | `owNMiaCgcHmqoTr3iRamFuHj` | 0x1b898b |
| KFWAH/ybEWdv 族 | 0x1b9f27/0x1b9f40 | R2 旧线索 |
### encode_aes API (0x330218) 完全解码
```
encode_aes(IN:std::string&, KEY:std::string&, OUT:std::string&)
1. OUT.assign(常量串@0x1ba000+0xa90) ← 初始化输出
2. UdbAESUtilC1(KEY) ← 构造器吞 KEY
3. UdbAESUtil::encrypt(IN, OUT) ← 加密
```
- lib 自带完整 AES 原语: KeyExpansion@0x24ebd4, Cipher@0x24f314, MixColumns/AddRoundKey/
InvSubBytes/InvShiftRows/InvMixColumns/FFmul — **纯自研 AES (非 openssl!)**
- `UdbAESUtil::_encrypt@0x24f9f0` = 底层; `hyudb_otp_encrypt@0x32fa24` = OTP 封装
### 朴素 AES 爆破实验 (证伪)
24B 钥 × {ECB, CBC-zeroIV} × {md5(aid/mid/…), sha1, aid-bytes, cfg-b64…, 10 候选明文}
→ 与 ed0db8 零命中。**key 必经 UdbAESUtilC1 加工 (派生/置换) 或自研变异 AES**。
### 下步 (定位派生)
A. unidbg 调 encode_aes(in, key) → 输出对拍 (可控输入差分)
B. 静态读 UdbAESUtilC1/KeyExpansion(0x24ebd4) 取派生规则 → Python 复刻
## §11.15 标准 AES 确认 + 爆破全证明伪 (2026-08-28 夜)
### SBOX 对照实验
UdbAESUtil 的盒表 @0x1c5d8e (SBOX) + @0x1c5e8e (INV-SBOX) = **标准 AES 盒** (逐字节一致)。
→ 自研代码 = 标准 AES 的重实现 (非变异)。KeyExpansion@0x24ebd4 列主序装载 (Nk=6 → AES-192)。
### 爆破总矩阵 (全部零命中, 对照 ed0db8)
| 维度 | 变体 |
|---|---|
| 钥 | 24B 原文 / 列转置 / base64 解码 18B→补丁 / md5(钥) / … × 9 键 |
| 模式 | ECB / CBC-zeroIV |
| 明文 | md5(aid/mid/aid+cfg/…), sha1, aid-bytes, cfg-b64[:16], mid-bytes … × 11 |
**结论: 32hex-HDID32 = 标准 AES 密文, 但 钥材料/明文序列化/模式 三者未定。**
### 下步 (决定性二选一)
A. **unidbg 调 encode_aes(IN, KEY) → OUT**: 可控输入差分, 直接对拍 ed0db8 + 反推钥/明文
B. **静态读完整 encrypt 路径** (0x24f9f0 _encrypt + Cipher@0x24f314): 明文源 + 模式 + 派生
## §11.16 unidbg encode_aes 探针上线 + 64B 钥材料发现 (2026-08-28 夜)
### tools/unidbg/hydev/src/hydev/AesProbe.java (可执行差分源!)
- 直接调 libudbauthunify_merged.so 的 encode_aes@0x330218 (IN, KEY, OUT)
- NDK-libc++ std::string ABI (short ≤22B inline / long heap) 正确读写字
- 权威输出 (已知输入):
| IN | KEY | 输出 |
|---|---|---|
| 0123456789abcdef | ZMHAVPRaxJ3MtXDjduUnXAKQ(24B) | ba3fb8f156b03a9d7db185d1254e0730 |
| 0123456789abcdef | owNMiaCgcHmqoTr3iRamFuHj | 74bd517c7e5d2bbce63c8e98a192c760 |
| 0123456789abcdef | ZMHAV…+64B-pad | (同24B) ba3fb8f1… |
| 0123456789abcdef | 64B-"0123456789…" | 72727e881edcfd0100a718687909b565 |
### KeyExpansion@0x24ebd4 关键发现
- 密钥装载 = **以 16B 步长读入** (x1[0]/x1[16]/x1[32]/x1[48] → 字) → **钥材料 64B** (非 24B!)
- 输出 = 64B (状态/扩展轮钥?) — Nk/Nr 分支在 0x24ec78-cmp#0x29
### 对比结论
标准 AES-192-ECB(同一输入+24B钥) = b1e3abcc… ≠ 原生 ba3fb8f1… — **原生 AES 变体 (64B 钥材料 + 自定义扩展)**。
华生线索: 原生输出对「输入块内含 NUL 的 64B 钥」敏感 — 下一轮 = Nk 分支 + Cipher 轮结构静态定案 → Python 复刻。
## §11.17 hyudb_otp_encrypt 组合链解码 (2026-08-28 夜)
### 0x32fa24 (hyudb_otp_encrypt) 组件链
```
cred_packls(…多个字段打包) ← 请求材料构建
xxtea_encrypt @0x457e60 ← XXTEA 层
AESkeyMgrC2 → AESkeyMgr::getkey(int,int) ← 双 id 取钥
string-insert(钥) + md5_char16 ← 32hex 材料 (MD5-hex 32 字符!)
UdbAESUtilC1 + encrypt ← AES 层
```
### 32hex 生成假设
- md5_char16 = 32 字符 MD5-hex — **与 HDID32 的 32hex 形态吻合** (候选公式候选)
- md5(钥)/md5(钥+域) 组合爆破 → 与 ed0db8 仍零命中 (材料=getkey 双 id 产物 + cred_pack 序列化, 未定)
### 工作台状态 (本轮新增)
- tools/unidbg/hydev/src/hydev/AesProbe.java: encode_aes 差分源 (已验证)
- 子代理: 静态复刻 KeyExpansion/Cipher → Python (验证目标 = 探针 3 向量)
## §11.18 md5_char16 变体识别 + 探针全家桶 (2026-08-28 夜)
### md5_char16@0x32e71c = "MD5-hex 中段 16 字符" (非 32hex 本体)
- md5_char16("") → 8f00b204e9800998 = md5("").hex[8:24] (d41d8cd9⁈**8f00b204e9800998**⁈ecf8427e) ✓
- md5_char16("0123456789") → 5d69b566979b86e2 = md5.hex[8:24] ✓
→ OTP 链的 32hex 形态 = 其它环节 (md5 全串 / AES 输出)。
### AesProbe 可调函数 (unidbg 权威差分)
- encode_aes@0x330218 (IN,KEY,OUT) ✓
- md5_char16@0x32e71c (OUT,IN) ✓
- xxtea_encrypt@0x32e83c (OUT,IN,KEY): xxtea("0123456789abcdef","ZMHAV…")→124105f9d7a549dfb6bc756cade6679ed412a6bd
- hyudb_otp_encrypt@0x32fa24 (string,h,h,string,string,string,h,m,string&): 参数语义待定 (现按序猜, 出空)
### 在途
- 子代理静态复刻 KeyExpansion/Cipher → huya_aes_replica.py (3 验证向量)
- 下步: OTP 参数语义 (读 32fa24 前半 arg 处理) + getkey 双 id 材料
## §11.19 hyudb_otp_encrypt 击穿: 参数语义 (arg1 必须=2) + 32hex 结构现形 (2026-08-28 夜)
### 参数语义 (静态)
- arg1 (uint8) 必须 == 2 (cmp #2 b.ne early-return) — 探测值修正后链路全通
- 32fa24 签名: (string-in, h=2, h, string, string, string, h, m, string&out)
### OTP 权威输出 (unidbg)
| 输入 | 输出 (hex) |
|---|---|
| in=abc key=ZMHAV… | `03 0088fa6ee519d52f61348e01d6e74c81cd8b38f113056fe498b0c7de2ffd52c7b0` |
| in=1e8bdf7d… key=KFWAH… | `03 0043c5bf3f792216e249116c968c84ff3a8b38f113056fe498b0c7de2ffd52c7b0` |
**结构: 0x03 + 可变(输入依赖) + 固定 32hex 尾 `8b38f113056fe498b0c7de2ffd52c7b0`** — 32hex 形态实锤,
可变段 = 输入序列化 + AES/xxtea 层, 固定尾 = 静态钥派生 MAC (跨输入稳定!)。
### 下步 (round7)
1. OTP 输入差分 (变 in/变 key → 可变为/尾变?)
2. 固定尾溯源 (8b38f1… = md5/aes(静态钥材料)?)
3. 32hex-HDID32 (ed0db8) vs OTP 体系对拍 (登录帧 t1.t0 可能 = OTP 输出的某段!)
### §11.19b OTP 差分定性 (2026-08-28 夜)
| in | key | 中段(输入依赖) | 32hex 尾 |
|---|---|---|---|
| abc | ZMHAV… | 0088fa6ee519d52f61348e01d6e74c81cd | 8b38f113056fe498b0c7de2ffd52c7b0 |
| abcX | ZMHAV… | 00bc4882b312f73d5c6f6dcbc15681db76 | **同** |
| abc | owNMia… | 0088fa6e…(同 abc!) | **同** (key 参数被忽略/固定位) |
- 32hex 尾 = **静态钥材料计算值** (非嵌入: so 全文件无 8b38f1/ed0db8 字节)
- 中段 = f(输入) (变输入即变) — 待定性为 md5/xxtea/AES 哪层
- 32hex-HDID32 (ed0db8) 与 OTP 尾 = 同族 MAC 理论的验证对象 (下轮)
## §11.20 OTP 中段公式实证: md5_char16(input ∥ getkey(1)-派生) (2026-08-28 夜)
### 静态流 (32fa24: 32fb60-32fbdc)
```
AESkeyMgrC2() ← 钥表构造
getkey(this, w1=1, x2=第二参数) ← id1=1 (ZMHAV…族)
string::insert(input, pos, getkey产物) ← 钥材料插入输入串
md5_char16(合成串) ← 中段 16B
UdbAESUtilC1(…) + encrypt ← 后段
```
### 差分数据 (中段, 输入依赖)
| in | 中段 (16B) |
|---|---|
| a | 78ea165d2d375a55a13989d176e96f3b |
| ab | 8c84ce0f1b734b1dc8227930936a4fd1 |
| abc | 88fa6ee519d52f61348e01d6e74c81cd |
| abcd | 0cd8ddaeb48a97b03459d4a1dc24a148 |
朴素的 md5_char16(in+key)/md5_char16(key+in) 全组合 = 零命中 → getkey 产物 = **派生钥** (非裸 24B)。
### 下轮
1. 探针直接调 AESkeyMgr::getkey(1,…) 取派生钥 → md5_char16 合成验证
2. 子代理 AES 复刻 (运行中)
## §11.21 里程碑: UdbAESUtil 纯 Python 复刻成功 + t1=ProtoInfo(appSign=HDID32) 对应 (2026-08-28 夜)
### 1) tools/huya_aes_replica.py (617 行, 逐指令复刻, 子代理产出)
- 5 段内嵌 objdump 原文逐条比对一致; **A/B/D 三向量逐字节命中**:
enc("0123456789abcdef", ZMHAV-24B) = ba3fb8f156b03a9d7db185d1254e0730 ✓
enc("0123456789abcdef", 64B-0123…) = 72727e881edcfd0100a718687909b565 ✓
enc("0123456789abcdef", owNMia-24B) = 74bd517c7e5d2bbce63c8e98a192c760 ✓
- **算法 = AES-128 变异**: 64B 钥材只用 key[0..15] (转置装载 rk[i]=key[4*(i%4)+i//4]);
扩展 10 轮×16B=176B (AES-128 形); SubWord 输入=相邻列末字节 + 链式 XOR (非标准词独立);
无独立 ShiftRows (位移吸收进寄存器置换共轭); SBOX/RCON/INV = 标准表。
### 2) 登录帧 t1 结构 = mobilequicklogin.ProtoInfo (结构级定案!)
| t1.tag | 字段 | 金样本值 |
|---|---|---|
| 0 | **appSign** | **ed0db8334cadd236c00cadf7e11ab5a5 (HDID32!!)** |
| 1 | appVer | 13.4.22 |
| 2 | sdkVer | 1.0.80138 |
| 3 | lcid | "" |
| 4 | clientIp | 127.0.0.1 |
| 5 | channel | xiaomi |
| 6 | countryCode | "" |
- **"APP_SIGN" 错误名 = 字段名 appSign 的校验失败** — 32hex-HDID32 = appSign 值 = 签名函数产物!
### 3) 下一关: appSign (ed0db8) 的生成式
- ed0db8 = encode_aes(16B-plaintext, key) 形态候选; 朴素明文组合 (aid/mid/DEVID40…异或/拼/零垫) 零命中
- 下步: (a) 补 InvCipher 解密路径 → 直解 ed0db8 见明文; (b) 探针 encode_aes(设备材料块) 差分对拍
## §11.22 复刻 15/15 全量实证 + C 向量更正 (2026-08-28 夜, 子代理终报)
### tools/huya_aes_replica.py (逐指令转录) + DiffProbe.java (差分权威源)
- **A/B/D/E/R0-R7 共 15 组全部位精确命中真实二进制** (含 8 组随机 16B 明文×随机 64B 钥)
- C 期望值更正: in="1e8bdf7d4f7a01d3"(16 ASCII), key=64B-ZMHAV+NUL → **实跑 = ae26d00a4d5a837baba2903d35135102**
(旧 2cdcf6… = brief 过时值; 算法零残差由随机明文组证明)
- 结论: KeyExpansion 64B 钥材只用 [0..15] (4×4 转置 rk[i]=key[4*(i%4)+i//4]);
扩展 10 轮×176B (列旋转字节链, 标准 SBOX/RCON); 10 轮末轮无 MixColumns;
ShiftRows 吸收进寄存器置换 → 逐指令模拟必需。
### appSign (ed0db8) 唯一剩余未知
- 16B 密文 (encode_aes 形态) — 朴素明文组合 (aid/mid/DEVID40/版本/配置) × 6 钥族 × 转置/b64/md5 = 零命中
- decode_aes 直解 (6 钥) = 32B 无明文 → **钥 = getkey(双 id) 派生材料** (非裸 24B)
- 下步 (round10): 逆向 replaaica 得 decrypt → 或 DiffProbe 调 getkey 取派生钥 → 对拍
## §11.23 getkey 等于裸钥! 堆节点取证 + 明文族爆破零命中 (新目标 R1)
### AESkeyMgr 堆节点取证 (unidbg 全内存扫)
- @0x127ddda0: `ZMHAV…24B` + len-0x18 = **getkey 返回的 = 裸 24B 钥串 (无派生!)**
- @0x127ea000: ZMHAV + 4VYcPdv… + xXEDWq… 相邻节点 = 钥表顺序装载
- @0x127ddf00: ZMHAV + "abc" (输入副本同址 = OTP 的 insert 目标 = 内部副本!)
- 此前「派生钥」假设 → **推翻: 24B 钥直读直用**
### appSign (ed0db8) 明文族爆破 (复刻引擎 × 8 钥 × 19 材料 × 8 形态 = 零命中)
- md5/sha1/sha256 摘要 16B、首尾 16B、双 md5、hex 编码等全灭
- 结论: 明文 ≠ 简单材料哈希 — 可能是 config 解密产物 / 复合序列化 (下轮: OTP insert-pos 差分 + config 88B 解码路径)
## §11.24 OTP 内部字符串缓存池取证 (R2 前夜, 0x127ddbe0 1056B dump)
- 相邻 string 对象: key1(24B ZMHAV!) + "abc"(SSO) + "k"(SSO) + "ivx"(SSO) + key2(owNMia!) + ...
- **`MsgRegSendSms`(13) + `MsgResponceRegSendSms`(21) 协议标签现形** → OTP = 注册/登录
wup 请求加密器 (消息名入串池, 参与 md5/xxtea/AES 组合!) — 纯协议路线的关键拼图
- 各对象 = std::string (data/cap/len/ptr) 排布, 与§11.23 堆节点同区
- 下步: 消息名 + 请求字段序列化 进入 OTP 组合的完整还原 (md5_char16 输入=消息级结构)
## §11.25 decode_aes 全 11 钥直解 ed0db8 = 全二进制垃圾 (R2)
- ybEWdvk…→576ead68… / HuyaUdb27B→c336e348… / xXED/4VYc/3FMH/owNMia/KFWAH/ZMHAV = 全无明文
- **appSign 加密钥 ∉ AESkeyMgr 24B 目录(11把) 文件钥(27B list)**
- decode_aes 输出 = 32B (16B 入双块) → ed0db8 = 16B 密文块 (32hex ✓) 但钥 = 他处
- 方向修正: 找 32hex-hdid 的真正生产方 (encode_aes 之调用者三处已列: OTP/文件/encode — 若 hdid 不走 encode_aes → 查 GetHDID 注册原语周边 + initSDID 的 Java 侧回填)
## §11.26 hydeviceid_config(112B) 直解 = 全钥垃圾 (R3)
- 服务端 config b64→112B; decode_aes(HuyaUdb27B/ZMHAV/owNMia) → ef1a5bb2…/全无明文
- config 加密 = 文件层多密钥组合 (writeFileEx@2523fc 周边) — 非单钥 AES
- appSign 未知收敛: {encode_aes 钥目录 11 把 × 明文族} {config 直解 3 钥} = 零命中
- 下步候选: ①hdid 走非 encode_aes 路径 (GetHDID native 40hex 体系周边/initSDID-Java 回填)
②OTP 消息结构串池 (MsgRegSendSms) 完整排序后 md5 组合验证 ③config 文件层双钥 (xxtea 前垫)
### §11.26b OTP mid 组合排列爆破 (5040 序 × 4 输入) = 零命中
- 串池 7 元素全排列拼接 md5-char16 无一命中 → 组合含长度前缀/TAF 序列化 (池对象带 0x1b/0x17/0x31 头值)
- mid 材料 = cred_packls 编码产物 (len-prefix 字段流), 非裸拼接
## §11.27 harness 复活 + GUID 缓存读取器输入透明性 (R4)
- **重合并 so (merge_decrypted 重跑) → 70621 级: getGUID 回 0a7dfa MATCH!** (缓存恢复)
- 修 b 桩 (101/102 接 System.getProperty) → 指纹可控
- **透明性证明: getGUID/getMID = 直接返回输入值** (aabbccdd00112233 → 同值; 0000…→同值)
→ GUID32 缓存 = Java b(100) 直填 (非 native 计算) — 与 §11.8-§11.10 缓存读取器定案闭环
- 遗留: getHDID = 空 (40hex 缓存缺 b-cell) — 登录 32hex (ed0db8) 与 GUID/MID 缓存非同一格
- 产物: so/libhydeviceid_merged_r2.so (缓存正常版)
## §11.28 hydeviceid r2-so hdid 字符串地图 + init b-cell 全集 (R5)
- init b-cell 调用 = {3, 100, 101, 102, 104} (SDKver/GUID/MID/empty) — 32hex 不在此路径
- r2-so "hdid" 字符串 (6 处):
- 0x3ba484: "id: " / "hdid: " = 日志格式 (32hex 打印点!)
- 0x3dec8c: "hdid"/"sdid" 键对 = device 映射 (setSafeDeviceId 存储)
- 0x3c8930/0x3ddef4/0x3e0040: mid/hdid/guid 键集
- 0x3ba2f4: hdid/ok/adb_ (调试)
- OTP mid 长度前缀族 (JCE varint/1/2/4B BE/LE + 消息名前置) = 零命中 → 组合含更深的 cred_packls 字段流
- 下轮: 0x3ba484-xref (hdid: 日志的调用者 = 32hex 装配函数) + cred_packls 静态串流
## §11.29 A路径实测边界 + getSDID 新值族发现 (R6)
### A路径 (unidbg 跑全 Java 登录流) = 实验性定界
- APK 直载成功 (createDalvikVM(apk) ✓), 双库加载 ✓, HuyaAuth 类可解析 ✓
- **纯 Java 方法不可调** (callStaticJniMethod 只走 JNI 符号; unidbg 无 dex 字节码解释器)
- 若要跑 Java 侧 → ProxyClassFactory 主机 JVM 方案 (dex→class 编译登录链 = 重型)
### getSDID 新值族!
- getSDID@0x20ede8 = `*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T` (=36B base64)
- **输入不透明性: 换指纹 GUID/MID 全变, SDID 恒定** = 静态钥材料计算的 native 证书
- SDID 36B: 16B 块 × encode_aes(8钥)/decode_aes(6钥)/md5/sha1/hmac = 全零命中; 帧字段×SDID 签名族 = 零
- 与 ed0db8 关系未定 (golden 帧 = 08-24 态; harness SDID 恒定同值 ⇒ 若关系存在则可验!)
### OTP 消息名差分
hypasswordLogin/MsgLoginReq/LogLoginReq → mid 各异, tail 恒定 8b38f1 → appSign ≠ OTP 任何段
### 下轮: setSafeDeviceId 直调后 getHDID 状态变化 + 0x64670 "hdid: " 装配函数直调
## §11.30 真实 getOtp 调用语义全解 (R7) - 直调路线就绪
### OTP 双真实调用方 (BusinessCfg::getOtp!)
- 268ca0 (getOtp(y, string&, int&)) + **0x269324 (getOtp(AppLoginData&) = 登录路径!)**
- 269324 参数: x0=to_string(serviceTime)!, x1=2, x2=AESkeyMgr计数器(w25), x3=AppLoginData+0x40,
x4=AppLoginData+0x10, x5=全局串(0x484000+0xa0), x6=4, x7=out, [sp]=m(串)
- **可直调**: BusinessCfg::getInstance@0x281270 + getOtp(AppLoginData&)@0x26916c (T/W 符号)
- AppLoginData 字段布局: +0x40/+0x10/+0x8 (多 string), 全局材料=0x484000+0xa0
### OTP tail = f(c,d) 差分 (推翻"固定"结论) + 真实布局空输出 (参数微差待调)
### 下轮 (R8): getInstance→getOtp(AppLoginData) 直调, 金样本值注满结构 → 输出 vs ed0db8
## §11.31 getOtp 直调成功 (R8) - C2 全链路在 emulator 内跑通
### 实验定案
- BusinessCfg::getInstance@0x281270 直调 ✓ → instance=0x12491540 (C2 真初始化!)
- BusinessCfg::getOtp(AppLoginData&)@0x26916c 直调 ✓ 无崩溃 (此路可行!)
- AppLoginData 伪造: std::string 双形态 (SSO<23 内联 / heap: len<<1|1 + [8]ptr + [16]cap) ✓
- OTP 真布局 (269324): x0=to_string(serviceTime), x1=2, x2=counter(w25=0首调), x3/x4=AppLoginData+0x40/+0x10,
x5=全局串, x6=4, x7=nonce-scalar, [sp]=OUT& (nonce 不入 mid!)
- mid = f(in, cnt, s3, s4, s5); OTP 输出=[h6][00][16B mid][s5 加密块...]
### 新候选: 堆 0x127d4b90 = 275d0ff676c0d65114acdefbd2ad87a3 (32hex! 来源待查)
### R9: ① hook 269328 (post-OTP, X29 读 [x29-0x40] = OUT!) ② 0x123M-0x130M 扫窗 before/after 差分
## §11.32 Hook 突破 (R9) + getOtp 结构校验 + 275d0ff6 排除
### Hook 首次触发! (此前"不触发"=地址错)
- unicorn2 hook_add_new ✓ → getOtp 入口 0x26916c 命中:
x0=0x12491540(this) x1=0x127c2700(AppLoginData&)
- OTP 调用点 0x269324 未到 → getOtp 在 2691a4 "cmn x20,#0x8; b.hs 2695dc(函数尾)" 提前返回
= 对伪造结构判定失败 (x20-layout 未知, 需读 0x26916c-0x2691a4 前导)
### 275d0ff676c0d65114acdefbd2ad87a3 = 调用前已有 (pre/post 扫描同现) → 非 getOtp 输出, 排除
### 堆窗 pre/post 差分法就绪 (getOtp-pre/post 双扫描框架)
### R10: ① hook 2691a4 读 x20 (分支条件) + 读 0x26916c-0x2691a4 前导定 AppLoginData 布局
### ② 布局修正 → getOtp 走到 269324 → hook 抓 OTP 实参+OUT
## §11.33 C2 新钥表 + AppLoginData 真布局 + getOtp 流程走通 (R10)
### C2(getInstance) 注入的 AESkeyMgr 新钥表 (堆 0x127ddbe0, 8×24B, 与已知11钥族完全不同!):
SHBfgytjtoikooru+hogji7ER / KNSDNjfohweeromn+mkladj3g / xnkdDFIERRIPT5df+hfgiJ0FD
NDFiroqpmvd4JDIJ+hidtiwex / fNoMrJhbEMXm8nHc+HXTNovaL / novwSHidrhDg1ADU+KLkejnHR
hgtuiouilbsdjwEH+HMYU5gjt / masldDSIFGJjdfio+5hkhsSDF
→ 新钥 × enc/decode/md5/sha1/hmac 对 ed0db8 = 全零
### AppLoginData 真布局 (jadx): hyOpenId@0(8B) + userId@8(24B) + userIdState@0x20(4B) + emailMask@0x28 + ...
→ 重构后 getOtp 流程穿透: 26916c→…→2691c0 (x20=0 首串校验过) 不再早退!
### 未捕获: OTP 调用点 269324 hook 未触发 (流程在 2691c4-2692fb 未hook窗口内或 4542c0 getServiceTime 段)
### 状态: appSign 生成复刻仍未完成 - 差距仅剩"抓到 getOtp 内部真实 OTP 输出"
## §11.34 getOtp 全流程钩子追踪定界 (R10b)
- 全覆盖 hook (0x2691a4-0x269330) 40 命中: 流程走通到 **0x269278 (bl to_string@0x4543c0, to_string(serviceTime=0))** 后无后续
- OTP 调用点 0x269324 未达 → **流程死在 to_string-CALL (libc++-PLT 0x4543c0)**
- x0=serviceTime=0 (仿真时钟 0) — 需查 0x4543c0 解析/或给 getServiceTime 注入真实时间
- 剩余步骤: ① 过 to_string (hook 0x4543c0 验证) ② OTP 调用 0x269324 [sp]=OUT& ③ 输出 vs ed0db8
## §11.35 getOtp 全流程执行成功! + 配置依赖定界 (R11)
### to_string@0x4543c0 = UC_ERR_READ_UNMAPPED (libc++ 符号缺失) → NOP 补丁 + hook 预写 [x29-0x58]
### 里程碑: OTP 调用 0x269324 实弹命中, 139 指令全流程走通 (getOtp 完整返回!)
- 真实参数: x0=in串(预写) x1=2 x2=counter(1) x3/x4=BusinessCfg+0x40/+0x10 x5=len0-SSO x6=4 x7=nonce OUT@[sp]
- OUT 槽=全零 + s3/s4 全空 → 根因: BusinessCfg 实例(0x12491540) 配置字段空 (真机=服务器 112B config 填充!)
### 下步: 112B config(2AQq9oUCCZ8M...) 解析→注入实例 +0x10/+0x40 → OTP-OUT 出现 → 差分进 ed0db8
## §11.36 实例注入实验 (R11b)
- 112B config × C2钥 decode = 全垃圾 (非AES)
- BusinessCfg 诸符号: load/saveLoginData/getLoginData@0x26984c etc. (+0x10/+0x40 = 登录数据字段源)
- 注入 +0x10/+0x40 (name/sha1) 后 getOtp 流程 269280-2692a4 段 = 新 UC_ERR_READ_UNMAPPED (s4 拷贝路径)
→ 实例字段注入 = SSO 布局细节需修 (s4 拷贝读 [x8+0x10+0x10] cap 越界?)
## §11.37 🎯 OTP-OUT 内容捕获成功! (R11c)
### getOtp 内部 OTP 输出完整读取:
content=0401 7c0e461e8c52e9360e90b5af264af667 0f127c3af8c69c3428ef3d1e9b7a8618 (34B)
结构 = [04=ver][cnt=1][16B mid][16B tail]
- libc++ 长串对象: [0-7]cap [8-15]size [16-23]ptr → 内容读取法定案
- SSO-only 注入 (s3=s4="hy_300023887") 后 getOtp 全流程 985 hooks 无错误!
### mid = f(in, cnt, s3, s4, s5) (s5 也入 mid!) — 差分闭环就绪
### 下步: 全组合爆破 (in/cnt/s3/s4/s5 候选族) → mid == ed0db8
## §11.38 首次全组合爆破 (R11d)
- 30976 combos (in×cnt×s3×s4×s5 候选族交叉) = 8.1s, 零命中
- 结论: 真实输入不在候选族内 → 需 s3/s4/s5 真源 (loadLoginData 持久化数据 / AppLoginData 真实 x20-s5)
- 差分引擎就绪 (30K calls/8s, 可 10 倍扩容)
## §11.39 R12: s5 零串长度族 (0-40) × S3精选 × in × cnt = 23616 combos / 5s / 零命中
- 累计差分 53K+ combos 零命中; s3/s4 真源 = native BusinessCfg (Java 无类), 需 loadLoginData/配置注入路线
## §11.40 R12b: nonce 影响 mid (推翻"豁免"结论)
- direct(nonce=0)=f0012daf / nonce=1=34bd49f5 / nonce=real(0x1a049abb20b0000)=561269ff
- nonce=real 仍 ≠ getOtp 内部 7c0e461e → 剩余差异 = s5 槽 junk 字节 / OTP 内部状态
- 差分空间扩展: mid = f(in, cnt, s3, s4, s5, nonce)
## §11.41 🏆 nonce 算法反汇编破解 (R12): nonce = counter | (serviceTime << 16)
### nonce_next@0x32ff10 直读:
[0x4933b0] 存 base; 若 arg0(serviceTime) 变化→counter 清零+存新 base; 否则 counter++
return counter | (serviceTime << 16)
### 验证完美: 直调 (in="0",cnt=1,s3=s4="hy_300023887",s5="",nonce=0x1a049befebf0000) = getOtp 内部 04c7537f 逐字节一致!!
### 差分靶向化 (5184 combos: ST×cnt×s3×s4) = 零命中
- 遗留: ①金样本真实 serviceTime (13位ms) ②s3/s4 真值 (BusinessCfg 登录数据)
## §11.42 【会话交接总结】R1-R12 全景 (新会话从这读起)
### 已铁证 (勿重做)
1. encode_aes 复刻 15/15 ✓ (tools/huya_aes_replica.py)
2. getkey = 裸 24B 钥; 双钥表 (11 已知 + C2 8×24B SHBfg族) × enc/dec/hash 全穷举 vs ed0db8 = 零
3. getOtp (BusinessCfg::getOtp(AppLoginData&)@0x26916c) 直调可执行:
- BusinessCfg::getInstance@0x281270 → instance; AppLoginData 真布局 (hyOpenId@0/userId@8/userIdState@0x20/emailMask@0x28)
- 269278 to_string@0x4543c0 = UC_ERR_READ_UNMAPPED → NOP 补丁 + hook 预写 [x29-0x58]
- OTP 调用点 0x269324 实弹: [04][cnt][16B mid][16B tail]; OUT@[sp] 读取法 (libc++ 长串 [0]cap [8]size [16]ptr)
4. **nonce 算法破解**: nonce_next@0x32ff10 = counter | (serviceTime << 16) (counter 按 serviceTime 变化清零)
5. **直调逐字节复现成功**: reproOtp(in,cnt,s3,s4,s5,nonce) = getOtp 内部完全一致 (验证值 04c7537f)
### 当前边界 (新会话主攻)
- appSign(ed0db8) = 金样本 (in=to_string(st), cnt, s3/s4=BusinessCfg 登录数据, s5, nonce=cnt|st<<16) 的 OTP-mid
- 未知输入: ①金样本 serviceTime (2024-08-24 手机时钟 ms, 禁 attach) ②s3/s4 真值 ③s5
- 差分引擎: 直调 ~500 calls/s; 天窗口 86M st × s3/s4 族 = 后台爆破可行
### 关键工具
- tools/unidbg/hydev/src/hydev/AesProbe.java (全能探针: callOtpReal/reproOtp/goldBurst2/seqOtp/hooks/NOP)
- 编译运行: cd tools/unidbg/hydev && CP=$(cat /tmp/unidbg_cp.txt):...+apk-parser... ; java -cp $CP:out hydev.AesProbe so/libudbauthunify_merged.so
- 金样本: account hy_300023887 / mid 1e8bdf7d4f7a01d3 / dev40 7c5387.../ sdid *hZrPb62... (36B)/ appSign ed0db8334cadd236c00cadf7e11ab5a5
---
## §11.43 R13: OTP 真机逐字节复现 + resinfo 解密 + ed0db8 定论前夜 (2026-08-29)
### 1) 里程碑: 真机 146B OTP 逐字节复现 (GoldSweep V2 模式)
- 引擎: tools/unidbg/hydev/src/hydev/GoldSweep.java (直调 0x32fa24, ~2500 calls/s)
- 输入 (真机 final_capture 六元组): in="1471224845212", cnt=2, s3="5008", s4=K1, s5=完整114B hyCred(0a60...), nonce=0x1a037881a430000
- 输出 = 真机 out **292/292 hex 逐字节一致** (0402b4024c4a6069cb3c38...)
- **此前"零命中"根因 = mid 提取窗口 bug**: midOf(out)=out[4:36] 对 146B 输出 = cipher[2:18] (偏移2字节!!)
正确首块 = **out[2:34]** (cipher[0:16])。旧 A/P/F/C/X/W 全部扫错窗口, 作废。
- 真明文结构 (AES 密钥 312334e88d8c35cb 解密确认):
`[02][u16le 12][xxtea 12B][u16le 114][hyCred 114B][零垫]` — s5=完整 cred, xxtea 12B 精确一致
- OTP 全部语义锁定: AESkey=md5_char16(s4+getkey(cnt)) | plaintext=[02]∥cred(xxtea(nonce,key=in))∥cred(s5)
- 金样本六元组 (待命中, 已正确化): **in = 账号稳定 uid = "1199666914671"** (hy_300023887; req-json 的 1471238907296 为会话号, 非 xxtea 密钥), cnt 1..15, s3="5008", s4=K1, s5=hyCred(0a80ee...), nonce=st<<16, st=金样本时窗 — GOLD 模式实测零命中
### 2) 差分覆盖统计 (全部 out[2:34] 正确窗口, 无命中)
| 模式 | 范围 | 次数 |
|---|---|---|
| H | 登录窗 st[5165..5172.7]×cnt1..15×nc0..2×in{uid,"0"}×s5{cred,""} | 1.39M (98%处崩) |
| H2 | st 单点{090906,102531,167182,172449,172452,172455,172458}×in4×cnt0..15×nc0..2×s5 2 | 3072 |
| I(完成) | 装机窗 st[1787582670000..1787582810000]×cnt1..15×nc0..2×in"0"×s5"" (8 并行 JVM) | 6.3M, **零命中** |
### 3) resinfo 文件层解密成功 (工具: python Crypto AES-ECB)
- 文件: /data/user/0/com.duowan.kiwi/files/hydevice/resinfo (528B)
- **密钥 = `HuyaUdb1928374650qwertyuiop` 前16B, 标准 AES-ECB 零垫**
- 明文 JSON:
```json
{"channelKey":"865a4924a40897ac1fcfe6b4c2cbb045","channelKeyVersion":"10",
"huyaDeviceId":"7c5387e0539c023c31c4ff0e807e7256117385ee",
"phoneInfo":"{\"resultCode\":\"103000\",\"desc\":\"true\",\"securityphone\":\"195****6018\",\"operatorType\":1}",
"safedeviceid":"PQwemAN9...","time":1787949486}
```
- 结论: **resinfo 含 channelKey/DEVID40/safedeviceid, 无 32hex hdid** — ed0db8 不在任何持久化文件
- channelKey(865a49...cbb045) = dfpReport t1 设备指纹; k1/sessHex32(865a49...cbb0e3) 为 BusinessCfg+0x10, 两者差末4 hex
### 4) 真机 getter 全量定案 (evidence/diag_phone/hdid_read.json 实测)
- getGUID=0a7dfaa882938a6ab502511452142c57(32hex) | getMID=1e8bdf7d4f7a01d3(16hex)
- **getHDID=7c5387e0539c023c31c4ff0e807e7256117385ee(40hex!!)** — 非 32hex 登录 hdid
- getCDID=02df3987...(40hex) | getSDID=PQwem...(180B b64)
- 32hex hdid (ed0db8) 不存在于任何 getter / resinfo / files / prefs (唯一出现点=登录 WUP t1.t0 本身)
### 5) harness 修复记录 (本轮)
- HyDeviceId.java: b(100) 误喂 MID → 改喂真 GUID=0a7dfaa8... (init 现 MATCH GUID/CDID/SDID/MID)
- 后端 Dynarmic → Unicorn2Factory 修复 init 崩溃 (dynarmic 在 datadiv 区误仿)
- 补桩: getApplicationInfo/getPackageName/targetSdkVersion/b(6)=账号/b(2001)=渠道/applist JSON 等 → init 完整跑通
- **但 init 不产 32hex**: 32hex hdid 计算不在 NativeEntry.init() 路径 (getHDID 缓存槽无人填充)
### 6) Frida 真机捕获实验 (结论)
- 真机唯一稳定注入通道 = spawn 挂起 + 只加载 `bypass_msaoaid_maps_art_callsite.js` (STATUS.md)
- **登录可带 frida (两阶段配方)** — 2026-08-29 实测 2 次真实密码登录成功, app 存活 170s+/多轮, OTP 全链捕获; "不可登录"旧结论作废 (msaoaidsec 标记上报风险仍存在, 仅限低风险实验)
- 捕获成功部分: 6 个导出钩子全部定位 (hyudb_otp_encrypt/0x32fa24, getOtp/0x26916c, setSafeDeviceId/0x26a2e0, getHdid/0x26a484, getkey/0x26a71c, md5_char16/0x32fb7c) — 偏移与 merged so 完全一致 => **lib 版本无漂移, 差分引擎可信**
- 工具: tools/frida/run_capture.py + hook_otp_capture.js
### 7) 真机持久化文件存档 (evidence/live_device/)
- resinfo.bin(528B) / hydckey.b64(148B, = /dckey/check 下发, 前缀 AAAAAMC1eP4iV43WYoI57ZOu0) / uuid.b64(172B) / guid.xml(GUID=0a7dfaa8...)
### 8) 剩余开放问题 (按可能性)
1. **st 在装机窗外/更早**: I 模式进行中; 若落空 → hdid 非"首启计算"或非 OTP 系
2. **ed0db8 非 getOtp-mid**: 1.55M 差分 + resinfo + getters 全面证伪 → 真源 = libhydeviceid 内 "hdid:" 装配函数(0x3ba484 区) 或 setDeviceInfo 上报前的独立计算
3. 服务端校验: hdid = 注册锚 (dfpReport 加密体=设备身份), 纯代码铸造需先破 dfpReport 加密体 (未破解#2)
### 关键工具 (本轮新增)
- GoldSweep.java: V2(真机复现)/H/H2(登录窗)/I(装机窗并行)
- tools/frida/run_capture.py + hook_otp_capture.js
- python: HuyaUdb1928374650qwertyuiop[:16] AES-ECB 解 resinfo (evidence/live_device/resinfo.bin)
### 9) R13 终局判定 (2026-08-29 05:04)
- 全部差分合计: H 1.39M + H2 3072 + I 6.3M = **~7.7M 次 OTP 调用 + AES 密钥直解 + resinfo + getters = 六路证伪**
- **定论: ed0db8 (WUP t1.t0 appSign) 不是 libudbauthunify getOtp 的输出** (prob > 99%)
- 剩余真源候选: ① libhydeviceid "hdid:" 装配函数 (0x3ba484 格式串, 0x64670 区, OLLVM 混淆)
② setDeviceInfo(msgType 0xb000021) 上报前的独立 32hex 计算
③ dfpReport 加密体内的设备身份派生 (未破解#2)
- 后续路线: (a) libhydeviceid 0x64670 区静态攻坚 (datadiv 已解, OLLVM 状态机)
(b) frida 真机 (stable bypass) 抓 setDeviceInfo 入参 → 32hex 直读
(c) 接受"hdid=设备级证书不可纯代码铸造"结论, 维持金样本 hdid 共用方案 (已跑通多账号)
### 10) R13b: frida 稳定性优化 (2026-08-29)
- 配方定案: **spawn 挂起 + 只注入 bypass_msaoaid_maps_art_callsite.js + resume**, 后延迟+6s 注入业务钩子
(同时注入 frida_bypass.js/javaexit = 模拟器向配方, 会与 msaoaid 补丁冲突 → 真机启动卡死, 勿用)
- 自愈运行器: tools/frida/run_capture.py (多轮自动重 spawn, 事件汇总单 JSONL, 进程死亡检测)
- 实测: 两阶段配方下 app 存活 >120s, crypto-otp/getHdid 全触发 (vs 旧三脚本配方 <60s 被杀)
- 已知残余: msaoaid solist 快照面 (G2-0019) 未掩; 登录流程仍不推荐带 frida (标记上报)
---
## §11.44 R14: 登录路径 OTP 全链 LIVE 验证 + appSign 结构性证伪 (2026-08-29)
### 1) 决定性捕获 (tools/frida + hook_huya_crypto.js 全链 hook, 用户真实密码登录 ×2)
捕获证据: evidence/frida/capture_20260829_052648.jsonl
| 链节 | LIVE 值 | 结论 |
|---|---|---|
| hyudb_otp_encrypt arg0 | frida 入口读 "" (误读!!) | 入口寄存器读不可靠 |
| **xxtea 内部密钥** (crypt_util xxtea hook) | **"1471259186024" / "1199666914671" = 账号稳定 UID** | xxtea key = uid ✓ |
| AESkeyMgr::getkey(1,2) | MKDKeridjing7avnsasdSDHI | getkey 表实锤 ✓ |
| getkey(1,3) | nskdI7MDGKSDJsnadjdoonvs | 表序实锤 ✓ |
| md5_char16 输入 | `865a4924a40897ac1fcfe6b4c2cbb0e3` + `MKDKeridjing7avnsasdSDHI` | AESkey = md5_char16(k1+getkey) ✓ |
| UdbAESUtil::encrypt 明文 | `02 0c00 [12B xxtea] 7200 [114B cred]` | 与引擎模型逐字节一致 ✓ |
| AES 密文 | b0a0af88aaf53a411b0eb1d283c88ff8... | OTP out[2:34] = 密文首块 ✓ |
| nonce | nc=0, st=调用时刻 | nonce=counter?st<<16 ✓ |
**结论: getOtp 全链语义 100% 定案** — in=账号uid, s3=非空门(值不进计算), s4=k1, s5=cred, AESkey=md5_char16(k1+getkey(1,cnt)), 明文结构如 V2。
### 2) 差分终态 (全部零命中)
| 模式 | 语义 | 次数 |
|---|---|---|
| GOLD | in="1199666914671"(正确uid) s3="5008" s5=金cred st登录窗+单点 | 346,545 |
| J2 | 全笛卡尔 in{uids,"0",""} × s3{5008,"",k1} × s5{cred,""} 登录窗 | 6,233,760 |
| I | 装机窗 in"0" s5"" | 6,300,000 |
| H/H2 | 旧uid窗+单点 (含 K1C 补齐) | 1.39M+3072 |
| **合计** | | **~14.3M** |
### 3) ★ 结构性证伪: t1.t0 appSign ≠ getOtp 输出 (QED)
- ed0db8 **跨账号稳定** (device_profiles.json: 多账号共用同一登录 hdid) — 设备级值
- getOtp 六元组含 **s5=账号级 cred** (每次登录轮换 → 每次 OTP mid 不同 — 08-25 biztoken mid vs 今日两次 mid 均不同)
- **账号级输入不可能产出设备级稳定值** → t1.t0 不可能是 getOtp 的 mid
- 推论: 770万+ 差分验证的是一必败假设; OTP 引擎已完全解码 (V2+live 双证), 但 **32hex appSign 由 libhydeviceid 设备级装配产出** (0x3ba484 "hdid:" 格式串 / 0x64670 区 / setSafeDeviceId 上报链)
### 4) 旧文档更正 (R13 → R14)
- ~~登录路径六元组 in=""/s3=""~~ → **in=账号uid, s3 非空门** (frida 入口读 "" 为误读, xxtea/内部 hook 为真值)
- ~~登录不可带 frida~~ → **两阶段配方可带登录** (2026-08-29 实测)
- ~~金样本 in="1471238907296"~~ → **in="1199666914671"** (稳定 uid; req-json 1471238907296=会话号)
- ~~I 模式"运行中"~~ → 完成零命中
### 5) 下一步 (appSign 真源)
- 反汇编登录 WUP 构建器 createWupRequestData<AppCommonData>@0x38dab0 → ProtoInfo.appSign 填充点 → 找 32hex getter
- 或 frida 全 hook BusinessCfg 设备 getters + setDeviceInfo 链直接读 appSign 来源
---
## ★ §11.45 R15: 🏆 appSign(t1.t0) 算法完全破解 — ed0db8 逐字节复现 (2026-08-29)
### 公式 (实机全链捕获 + 复现验证)
```
t1.t0 appSign (32hex, "hdid") = MD5( appId + "_" + appVersion + "_" + k1 )
金样本: MD5("5008_13.4.22_865a4924a40897ac1fcfe6b4c2cbb0e3")
= ed0db8334cadd236c00cadf7e11ab5a5 ✅ 逐字节命中
```
- appId = "5008" (byte-appId), appVersion = 版本名 ("13.4.22"), k1 = BusinessCfg+0x10 设备钥 (865a49...cbb0e3)
- 与网页 appSign 公式同族: web=md5(appId+vCode+k1)[:8] (60576904) — native 用版本名+完整32hex
- HuyaMd5 类 = 标准 MD5 封装 (实机 toString 输出 = ed0db8 直接命中)
### 发现链路 (本轮)
1. createWupProtoInfo(ProtoInfo&)@0x273f1c 反汇编 → appSign = HuyaMd5(拼接串) — 3 个全局单例串 @base+0x484000+0xe0 (+0x10/+0x28/+0x40)
2. frida 读全局串: "5008" / "13.4.22" / k1 (SSO 旗标字节混读修正后)
3. HuyaMd5::toString() 实机输出 = **ed0db8334cadd236c00cadf7e11ab5a5** (WUP 公共帧构建时触发, 无需登录)
4. python: md5("5008_13.4.22_865a4924a40897ac1fcfe6b4c2cbb0e3") = ed0db8 ✅
### 结构性解释 (与 R14 证伪一致)
- appSign 只依赖 {appId, appVersion, k1} — 无账号输入 → **跨账号稳定** ✓ (device_profiles 多账号共用)
- k1 = 设备级键 (dfp t1/ channelKey 同族: cbb045/cbb0e3) → 版本更新则 appSign 变化 → 服务端按版本校验
- 与 web appSign 公式同族: web=md5(appId+vCode+k1)[:8] (60576904) — native 用版本名+完整32hex
- OTP 链 (getOtp) = 账号级 (s5=cred) — 与 appSign 职责分离 ✓
### 工程意义
- 制造任意 (appVersion, k1) 组合的合法 appSign — 登录帧 t1.t0 可自铸
- 现有多账号方案 (金样本 hdid/shareConfig) 可升级为"按设备钥动态计算"
- 设备身份 (dfpReport 加密体) 仍是仅剩的铸造缺口 (#2)
### 工具/证据
- tools/frida/hook_otp_capture.js: createWupProtoInfo@0x273f1c + HuyaMd5C1/toString 全链 hook
- capture_20260829_054317.jsonl: ctor inHex=353030385f31332e342e32325f383635... (5008_13.4.22_k1) + tostr=ed0db8...
- 复核: md5("5008_13.4.22_865a4924a40897ac1fcfe6b4c2cbb0e3") = ed0db8334cadd236c00cadf7e11ab5a5 ✅
---
## §11.46 R16: dfpReport 加密体侦察 (未破解#2 现状) (2026-08-29)
### 结构解码 (evidence/dfp_chain_golden.json 全链 WUP)
```
getDfpConfig: tReq{version="5008"} -> tRsp = 81B 密文配置 (lNLlGtxbxSB2FhJg...=, 非JSON)
selectOperator: _wup_data{version"1.0", hyudb_<ts10>, "5008", "LV", SDID(PQwem...)}
dfpReport: tReq = [10B 魔数 57 18 82 cf 66 4b b3 94 01 ee][3988B 加密采集体]
tRsp = code"10" + t1(865a4924a40897ac1fcfe6b4c2cbb045=channelKey!) + SDID
```
### 侦察结论 (静态全量)
- **魔数/协议串不在任何静态库**: APK 147 lib 全扫 + 设备 lib + 本地 merged = 零 (协议名/魔数均运行时装配或混淆)
- dfp = **腾讯 turing 反欺诈体系** (app_turingdfp/app_turingfd 目录, libturingga.so, 108298=appid)
- 关键材料已拉取: app_turingfd/mpdc_108298_1 = 32hex 设备号(0399ED0BEADBA7124D445AA7D7843BF9!)
app_turingfd/12/108298_ga_2 (1860B), app_turingdfp/1/.turing.dat (76B)
- 链路: getDfpConfig(密文配置) -> selectOperator(交SDID) -> dfpReport(3988B密文体) -> 服务端回 t1/SDID
- Java 侧无 dfp 类 (classes12 仅 URL 黑名单配置); 协议全 native; turing = 工业级混淆 SDK
### 影响评估
- 唯一剩余缺口 = "纯代码铸造全新设备身份" (需破 turing 加密体, 多日专项)
- 现有多账号方案 (金样本设备身份) 不受影响; 缺口#1 不影响 hdid/appSign 本身
### 工具/资产
- tools/frida/hook_dfp_java.js (Java 全量 hook — 太重, 会闪退, 弃用)
- /tmp/turing/: mpdc1/ga2/tur 数据存档
- 建议: turing-dfp 立为独立子项目 (证据链齐全); 或轻量运行时 trace 追 tReq 装配点
---
## §11.47 R17: dfpReport 密文体实时捕获成功 (2026-08-29) — turing 加密体现场!
### 捕获 (SSL_write 明文扫描, hook_ssl_magic.js)
- 端点: **POST wsapi.huya.com** (okhttp) — 非 udbdf 域名!
- 3798B 请求: hyudbwebuif/dfpReport WUP -> tReq{tag6="android-", tag1=[0dcb]3531B = [10B魔数 571882cf664bb39401ee] + 密文}
- 证据: evidence/dfp_live/dfpReport_post_full.bin (完整 3798B HTTP POST)
- 组件: libturingmfa.so(v87, tss;rfr;ite) + libturingga.so(v2.92.2) 动态插件 (app_dynamic_so_64/)
- 密钥材料: mpdc_108298_1=32hex(0399ED0B...), getDfpConfig tRsp=81B密文配置, ga2(1860B), .turing.dat(64B)
### 触发方法 (重注册)
- 备份 tar (604MB) -> 清 app_turingdfp/app_turingfd/resinfo -> 启动 -> 自动重注册 -> SSL_write 命中 -> 状态已再生 (ga2/resinfo 恢复)
- 无副作用 (备份在 /data/local/tmp/dfp_backup.tar)
### 下一步 (turing-dfp 独立子项目)
- 解析 WUP 完整结构 + 抓 SSL_read 响应
- 破 getDfpConfig 81B 配置 (含加密钥?) -> 破 dfpReport 密文
- 或运行时 trace 密文装配点 (SSL_magic 命中时刻的调用者)
---
## §11.48 R18: turing 密文流式模式判定 + 全双工捕获 (2026-08-29)
### 三样本差分 (同设备 08-24/06-14/06-25)
- 密文长 3531~3931B; **全三样本仅 5 稳定字节**: [7c da][XX][69 c9 f5] (位置0-2 + 3-5)
- 其余全运行时变 -> **非确定性ECB, 是带 nonce/头部的流式(CTR)加密** — 5B稳定段=头部/标志
- 密码结构: [10B魔数 571882cf...][5B头][nonce/数据...][密文流][尾部 14B]
- 尾部特征: 400c0b8c980ca80c (两次 fresh 均有, 疑包尾/校验)
### 全双工捕获 (SSL_write+SSL_read, hook_ssl_magic.js)
- 请求 4198B (POST wsapi.huya.com, okhttp) + 响应帧 108B (826a...hyudbwebuif resp)
- 证据: evidence/dfp_live/dfpReport_post_full_0625.bin
- SSL_write 回溯 = ART/Java 直呼 (JAVA 侧拼 body, 非 turing 直接写 TLS)
- 采样集: golden(3908B) + f1(3531B) + f2(3931B) 三份密文齐备
### turing-cipher 专项起点 (会话内到此为止)
1. 密文=流式+nonce头; 明文=采集数据(可能gzip先压 — SSL_read 见 1f8b0800 gzip 流)
2. 密钥材料候选: mpdc_32hex / getDfpConfig 81B配置 / ga2 / turing.dat
3. 下一步: (a) frida 追 turing 内部 encrypt 调用 (JNI 层), (b) 静态 OLLVM 破 ga/mfa,
(c) 若只求实用: 重放已捕获密文即可 (同设备身份)
---
## §11.49 R19: turing 运行时路线终态 (2026-08-29) — 专项交接
### 本轮排查结论
- turing 类定位: com.tencent.turingfd.sdk.ams.ga.{Lemon,Sultana,Solar,Blueberry,Pineapple,...} (果汁混淆系)
- Java 侧载荷 hook = 0 触发: turing 采集/加密全 native, Java 仅薄壳 (payload 不经过 Java 方法) -> Java-hook 路线关闭
- 密文体流式模式实锤 (nonce 头 7cda..69c9f5, 仅5稳定字节)
- 静态密钥假设 (XOR-mpdc/内生钥) 全否定: 81B 配置/64B turing.dat = 真加密非简单 XOR
### 破解 dfp 加密体的可行路线 (专项, 估时)
1. native 静态: libturingga/mfa (296KB, OLLVM, -fvisibility=hidden) — 多日
2. native 动态: turing 加密函数的内存级 buff 追踪 (SSL 命中时刻向前追) — 日级
3. 密钥链: 破 getDfpConfig 81B 配置 (服务端下发镜钥) — 依赖 1/2
4. 实用主义: **重放已捕获密文 = 同设备身份注册成功** (现状方案不受影响)
### 交接资产 (evidence/dfp_live/ + docs)
- 密文采样 ×3: golden(3908B) + f1(3531B) + f2(3931B)
- 请求全文: dfpReport_post_full*.bin (3798B/4198B)
- 组件: libturingga.so / libturingmfa.so / 32hex mpdc / 81B config / ga2 / turing.dat
- 工具: hook_ssl_magic.js (全双工) / hook_turing_jni.js / run_capture.py 自愈
- 触发方法: 清 turing 状态重注册 (备份 tar 可回滚)
---
## §11.50 R20: turing 运行中 dump 工程化 (2026-08-29)
### 捕获工具定案 (hook_str_magic.js — 每轮可靠命中!)
- **字符串过滤 > 魔数过滤**: SSL_write 明文含 "dfpReport/hyudbwebuif" 串才 dump (魔数过滤会漏轮次, 串过滤 100%)
- 每轮: POST wsapi.huya.com + hyudbwebuif/dfpReport WUP (libjavacrypto okhttp) — 全量请求快照
- 触发: 清 app_turingdfp/fd 重注册 (重注册稳定); pm-clear 全清+登录 = 新身份再注册 (也稳定)
### 采样集 (7+, 同设备多身份)
| 样本 | 密文长 | 头 12B | 身份 |
|---|---|---|---|
| golden 3908 | 3898 | 7cda0169c9f5 489d4c4695ef | 08-24 |
| f1 3531 | 3521 | 7cda11 69c9f5 289c4d46956f | 08-29 A |
| f2 3931 | 3921 | 7cda11 69c9f5 289c4d46a1de | A |
| f4 4310 | 4033 | 7cda11 75c8f5 349c4d0390de | A |
| 0647 4258 | 3991 | 7cda11 69c9f5 289c4d46a1de | **新身份 B** |
- 新身份 B 密文头 = 旧身份 A 的 f2 完全一致 12B → **nonce/加密流与身份无关(设备公共)**; 计数位 28/34 递增
- nonce 块结构: [7cda][版本位 01/11/13][69c9f5][计数位][9c4d46...稳定]
### 运行时路线终态 (明文未获)
- Java 桥 0 调用 (加密全 native 内部) / co-located 扫描 0 (工作缓冲已回收) / SSL 回溯全 Java 帧 (加密提前完成)
- 明文/密钥获取 = turing native 静态破 (OLLVM) 或 harness 装载同库直调内部偏移
### 工程交付
- 触发→捕获→存档 全自动化 (清状态+spawn+str_magic+杀) — 每次 ~40s
- evidence/dfp_live/: 请求全文 ×5 + 采样集 + 组件 + fresh 状态文件
---
## §11.51 R21: turing native 静态突破 — JNI 表解出 + harness 装载 (2026-08-29)
### 💡 重大进展: turing mfa JNI 原生方法表解出 (静态!)
- 从 libturingmfa.so 的 JNI_OnLoad (导出@0x25414) 反汇编 + JNI 方法名串交叉定位
- **JNI 表 (data.rel.ro @0x45040)**:
- `b87_D2D99D56FECC73FD (Landroid/util/SparseArray;[BLjava/util/Map;I)Landroid/util/SparseArray;` **impl@0x25c98**
- `f87_D2D99D56FECC73FD (SparseArray;[BI)` (impl 邻近) — v87 是加密核心入口族!
- 方法名带版本哈希: D2D99D56FECC73FD = mfa v87 (ga 侧 2A742FA224BA8A2A 同类, 在 turingga.so)
### ELF 布局洞察 (harness 装载崩因实锤)
- turing 库程序头正常 (LOAD-1 rx 0x0-0x43988, LOAD-2 rw @vaddr0x45648) — 但 **LOAD-2 vaddr 未4K对齐** (0x648)
- 真机按页映射覆盖 0x45000-0x45648 间隙; **unidbg 按精确边界 → 间隙未映射 → JNI_OnLoad 的 GOT 读 (0x45000+#dd0) 必崩**
- harness 补映射 (mmap2 gap) 后过导出调用, 再崩于 turing 共享指针原子操作 (x0 未映射 = 对象模型深度问题)
### harness 状态 (TuringProbe.java)
- ✅ unidbg 装载 turing 双库 + 预注册 11 个 turing 类 (FindClass OK)
- ✅ gap 补映射 (mmap2@0x45000)
- ⏳ JNI_OnLoad 内 x0 原子崩溃 (turing 自研共享指针 vs harness 对象)
- ⏳ b87_@0x25c98 直调: DvmObject→jobject 封送待办 (addLocalObject int 句柄机制)
### 下一步路线 (已提前到位)
1. **直调 b87_@0x25c98** (JNI 封送: vaList/int-handle) — 喂真实捕获 blob → 输出 SparseArray = 证据事件 = 明文!
2. JNI_OnLoad x0: 追踪 onLoad 内部分配链 (0x12b6c 原子函数的调用者)
3. ga 侧 JNI 表 (turingga.so 同法解剖) → a209202_ 族入口
### R21-补充: b87_ harness 直调结果 (2026-08-29)
- addLocalObject 句柄封送 (int 句柄作 jobject) — **调用执行成功** (不再 Unsupported arg)
- 三种输入 (全POST/cipher-only/magic+cipher) × int{0,1} = 全部确定性 **ret=-1**
- 判定: jobject 封送通; -1 = turing 全局初始化态缺失 (config/dat 未载入) 或 SparseArray 内容格式不符
- 下一步: (a) onLoad 初始化链修复后直调 (x0 原子问题先解), (b) 或喂 SparseArray 事件内容探格式
---
## §11.52 R22: harness onLoad 迭代修复进展 (2026-08-29)
### 崩因链逐级突破 (每补一个槽位推进一段)
1. **0x48a90 全局槽 NULL → 引用计数崩溃** (0x12b6c ldxr x0):
- onLoad 的 init 函数 0x11690 先读该槽 (adrp 0x48000 + ldr #0xa90) 再 refcount
- 真机上该槽在 init 链前序已赋值; harness 中静默为空
- **补丁: 塞 8B dummy 对象指针 → 过此关!**
2. 新崩溃: BR-x8 空跳 (X8=0x0, PC=0x0), X0=X1=0x1203ced5 (rodata 字符串)
- 变体函数指针表空槽 / C++ 虚表未建 / PLT 未解析
3. 根因深化: turing 类定义 (字段/方法) 缺失 — 预注册的空类无法支撑 JNI 静态字段链
- unidbg VM 无 dex 加载 (仅 setDvmClassFactory/ProxyClassFactory 宿主代理)
### 状态判定
- onLoad 全仿真 = 类定义工程 (ProxyClassFactory) 或连续空槽 wack-a-mole — 小时级
- 替代: 跳过 onLoad, 直接按 JNI 表构造 b87_@0x25c98 的 C 结构对象模型直调
- 进展固化: gap补映射 + 预注册类 + 全局槽补丁 = 已进 TuringProbe.java 可复现
---
## §11.53 R23: ga 侧 JNI 解剖状态 + 双线进展固化 (2026-08-29)
### ga (turingga.so) JNI 面
- 方法名族: a209202_2A742FA224BA8A2A .. n209202_ (14个, 哈希=ga v2.92.2)
- JNI 字符串在 .rodata (vaddr 0x4bd7/0x4c4e/0x5164...); **注册表 = 非文件字面三元组**
(281 个 RELATIVE 重定位扫描 = 0 命中 -> 表为栈构建/运行时填充, 需 JNI_OnLoad 反汇编)
- ga JNI_OnLoad 符号存在 (symtab 隐藏), harness 调用崩溃: PC=0x31ec8, X0=1 (运行中-非空指针)
### 双线状态 (顺序第1步受阻, 已固化)
- 线1 (mfa onLoad): 引用计数关已过 (0x48a90 槽补丁); 后续空槽 wack-a-mole ~小时级
- 线2 (ga 表): 需 JNI_OnLoad 栈构建表反汇编 — 与线1 同深度
- 两线公用的更优路径: **运行中 RegisterNatives 捕获** (frida vite 已多路试, ART 内部符号未暴露 —
备选: ART JNIEnv vtable 正确索引再试 / halt_handler)
### 建议下一步 (下会话入口)
- A: mfa onLoad 空槽批量补丁 (0x48000-0x48a90 全槽扫描, 非零即赋 dummy) 冲过初始化
- B: ga JNI_OnLoad 反汇编解栈构建注册表 (RegisterNatives 调用参数恢复)
- C: 重置方向 — 解析已捕获 7 样本的 nonce/计数位规律 (零依赖纯分析)
---
## §11.54 R24: C线纯分析终局 — 密钥流每次独立 + 头部字段解码 (2026-08-29)
### 7 样本对分析结论
1. **同 nonce 组 (f2/0647 头13B相同) 异或 = 98.6% 随机差** (共享散点41个≈期望15, 无ASCII结构)
-> **密钥流每次运行独立 (防重放), 同头≠同密钥; 头部非密钥**
2. LCP 分组: GOLDEN=2B(版本位01) / f1-f2-0647=10B / f2-0647=13B — 分歧从 byte10
3. byte2 版本位: 01(golden原始注册) / 11(今日重注册) / 13 — 疑重注册计数/模式
4. **byte6-7 = 运行秒数低16位** (39944/39976/39988/40392 ≈ 40s-uptime!) — 报告时刻微秒窗
5. 库内无格式常量/无gzip/zlib魔数 (bb80@turingga-0x11599=巧合)
### 判定
- 密文 = 每次独立的密钥流 (真防重放); 头16B = 加密元数据块(版本+计时+填充)
- **纯样本分析无法破** (无密钥派生链); 密钥源 = getDfpConfig 81B 服务端下发镜钥 + 设备材料
- 破解必须回到加密内核: A (harness onLoad 空槽冲关) / B (ga JNI_OnLoad 注册表反汇编)
- 已获价值: 报告时刻可判定 (头 byte6-7 ≈ uptime-ms), 版本/重注册模式可区分
---
## §11.55 R25: 💥 加密器识别 — turing mfa XXTEA 变体 + harness 直调运行 (2026-08-29)
### 关键突破 (IDA 导出 + unidbg 双线合流)
1. **JNI 表全解 (11 方法)**: a87_@0x25680 b87_@0x25c98 c87_@0x262a8 d87_@0x26418 e87_@0x26768
f87_@0x26edc g87_@0x271d0 h87_@0x274c0 i87_@0x276d0 j87_@0x27a78 k87_@0x27b74
(+ onServiceConnected@0x37324) — 类 TNative$aa (com/tencent/turingface/sdk/mfa)
2. **b87_ 语义**: JNI 分发壳 (a6==1 → sub_2E4A4!) — **sub_2E4A4 = 解包链**:
sub_2E250 { sub_252EC [拷贝 + **sub_24D58 = XXTEA变体解密** + 尾4B长度校验] + sub_34A00 [inflate!] }
-> "resp" 存 SparseArray — **输入 = [XOR/XXTEA密文][4B len] → inflate → 明文!!**
3. **sub_24D58 反编译 = XXTEA 变体**: 负长度参数 + 0x9E3779B9(2B16!) + 掩码混淆运算
(constant: 6CC17688/933E8977/D3115A55/5613AC09/A9EC53F6/B13795CC/4EC86A33/73067D6A/8CF98295)
key索引 = (sum>>2&3 变体) — 自定义变体非标准XXTEA
4. **harness 直调 sub_24D58 成功**: (v-ptr, -n, key@3DF88) — n=977/976/978 全量运行无崩!
输出非zlib — key槽或输入格式待定
### harness 进展
- 导入补丁 115/121 解析 (ASensor 族缺 libandroid)
- 0x48A90/0x48A98 构建器全局按语义手工构建 (rc@0 len@8 data@16!)
- 日志链桩 (11890/11928/3e80/3f70 = mov-w0-#0;ret)
- Unicorn2 = ldxr 原子缺陷; Dynarmic = 过 init 到注册段 (JNI-Env 回调 abort); 三后端均可跑纯函数
### 下一步 (解密侧续攻)
- key 槽验证: 3DF88 结构内偏移 / 或 key 由 config 派生
- b87_ 全链 harness 直调 (喂真实捕获密文 → inflate → 明文!)
- a87_@0x25680 = 加密侧 (sub_27DA4 链) — "纯代码铸造" 的核心
## §11.56 R25.5: 🎯 XXTEA 变体加密侧回环验证成功 — 3DF88 密钥确认 (2026-08-29)
### harness 直调 sub_24D58 回环实验
- `sub_24D58(buf, +n, key@3DF88)` = **加密** (a2 正参数分支)
- `sub_24D58(buf, -n, key@3DF88)` = **解密** (负参数分支)
- 44B 已知明文 → ENC → DEC → **100% 还原** (roundtrip match=True!)
- **key@0x3DF88 = 真密钥**: d2785ef1-c4c23330-25a9c8ba-3a867e67 (u32 LE)
- 三层后端对比: Unicorn2 直跑 XXTEA 无原子指令 ✓ (dynarmic 在 2E250 链 JIT 崩)
### ga (turingga.so) JNI 表全解 — harness 注册后读表 @0x4d1a0
14 方法: a209202(SparseArray,Context,Map,Map,I)@0x&1fcb8 b209202(SparseArray,[B,Map,I)@0x20110
c209202(SparseArray,Context)@0x20574 d209202(SparseArray,Context,I)@0x20688 e209202(SparseArray,Context,Map,I)@0x20984
f209202(SparseArray,[B,I)@0x20fc4 g209202(SparseArray,Context,Map,I)@0x211e8 h209202(...)@0x21468
i209202(SparseArray,Context,Obj,Obj,Obj)@0x21624 **j209202()String@0x21948 (取DID!)**
**k209202([B)[B@0x219d8 (字节进出 = 传输加密!)** l209202(InvocationHandler,AtomicReference,ClassLoader)@0x42d50
m209202(SparseArray,Context,Map)@0x21bdc n209202(SparseArray,[B,I,String,J)@0x43900
- ga JNI_OnLoad @0x1faf0 (dynsym!) — 注册 = JNIEnv vtable-215 + 表@0x4d1a0 + 14计数
- ga 传输加密入口 k209202@0x219d8: strncpy@plt + 内部链 (0x19378/0x13eb8×2/0x1c640×2/0x24334/0x19258)
### 现状判断
- **捕获密文 ≠ mfa 的 b87_ 内部序列化** (sub_2E250 全偏移网格拒绝 = key 或格式不匹配)
- **捕获密文 = ga 传输层加密** (k209202 家族) — ga 调用返回 -1 = SDK 全局状态未初始化
- mfa 侧 a87_@0x25680 = 事件收集序列化 (sub_27DA4: timing-tags + 0x11-version-key + sub_2C76C 收集器) — 非传输加密
- 捕获密文 10B 魔数 7cda... = 运行时合成 (无反编译常量)
### 下一步 (两条关键路径)
1. **用户导出的 turingga.so IDA export** → k209202@0x219d8 反编译 → 传输加密链静态全解 (同 mfa 方法论)
2. harness 继续喂 ga 运行时状态 (0x48XXX 构建器等价物) → k209202 真调用 (同 b87_ -1 问题)
## §11.57 R26: ga k209202 = 16B 块加密器 + 状态门全打通 (2026-08-29)
### k209202@0x219d8 反汇编解构 ([B)[B)
- **GetArrayLength; `cmp w0,#0x10; b.ne FAIL` = 严格 16B 输入** (非大块传输加密!)
- GetByteArrayElements + strncpy(16B) → bl 0x19378 (对象创建!) → bl 0xc8a4 (builder-init)
→ bl 0x13eb8×2 (序列化!) → bl 0x1c640×2 → **bl 0x24334 (块加密核心: calloc+memcpy+全局0x48000+c18状态+0x5754串!)**
→ bl 0x19258 (收尾) → NewByteArray(0x580)+ 结果 vs 全局计数器 cmp
- **0x24334 = 带状态的块加密机器** (读 [0x48000+0xc18] 指针再解引用 → 计数器对比 w21!)
- 失败路径: 0x21b4c (len≠16 → x20=null) / 0x21bb0; 成功 = NewByteArray-route
### harness 状态门全打通
- **mfa: 0x48C18 = 单例 48BD8 对象字段 a1+64 (id!) — 补非零 → b87_ 全链 LIVE!!**
(sub_347C8(map-check) → GetByteArrayElements → sub_2E4A4 → XXTEA+inflate 全部执行!)
- b87_ 对捕获密文三种输入 (strip/only/magic-full) 全 -1 = **确定性拒绝 — 捕获 ≠ b87_'resp' 格式**
- ga: 0x48c18 状态指针已补 → k209202 仍 -1 (0x19378 对象创建需更深的 SDK 全局态)
### 结论 (本阶段定论)
- **捕获密文的密钥 = 设备材料派生 (mpdc 16B→ga 16B块加密→主密!) — 非静态 3DF88**
- mfa 的 XXTEA (24D58) = 内部序列化 (resp-解码侧!) — 已 100% 回环验证
- **传输加密链 0x19378/0x13eb8/0x24334 的静态明文 = 需要 turingga.so 的 IDA 导出**
(同 mfa 方法论: 反编译这些函数的 key 派生/块加密逻辑 = 密钥再推导!)
### 立即下一步
1. **用户导出 turingga.so 的 IDA decompile** (k209202 全家: 0x219d8/0x24334/0x19378/0x13eb8/0x1c640)
2. harness 继续: mfa 单例 34D68 全量 ctor (48BD8) + 各字段填充 → b87_ 对正确 resp 输入成功
3. 或 frida 捕获一次 **服务器 dfpConfig 响应** (b87_ 的 resp 输入 = 密钥推导的旁路!)
## §11.58 R27: 🎯 完整加密链验证 — deflate+XTEA 闭环出炉 (2026-08-29)
### 加密链全解 (mfa IDA 反编译闭环!)
- **sub_2E164 = 加密封装**: `deflate(a1, a2, &def, &dlen, 0xFFFFFFFF)` + `sub_25234(...)`
- **sub_25234 = XTEA 封套**:
```
v5 = len + 4*(len&3!=0) + 4 (对齐到4B + 长度字!)
calloc; memcpy; 尾字 = 明文长度!
sub_24D58(buf, +n, key@3DF88) (加密!)
```
- **解密对偶**: sub_252EC (XTEA-dec + 尾字长度校验) → sub_34A00 (inflate!)
- **链条**: 明文 → deflate(zlib) → [pad + 4B len] → XTEA-enc@3DF88 → 密文!
→ XTEA-dec → 尾字取长 → inflate → 明文 ✓
### harness 全链运行成功!
- Java-deflate 44B→47B (zlib) + **真 sub_25234 ret=1 密文 52B** (4012a3a8e4...)
- **GOT RELRO 坑找到: 导入槽在只读页!! — mprotect(0x45648,0x3440,7) 后补丁才真正写入**
(之前所有 -1/崩溃 = 未写成的 GOT 槽 br-0!)
- 修复顺序: load → gap-mmap → **mprotect RELRO→RWX** → patchImports → 调用!
- 52B 密文 = 48B (pad+XTEA 块) + 尾 4B 长度字段的所有权 = XTEA 加密涵盖
### 捕获密文格式确认
- **捕获 = [13B 请求头][XTEA-wrap(key=设备派生)]** — 13B 头 = 请求协议 (7c da/版本/nonce/uptime)
- 密钥 ≠ 静态 3DF88 (设备材料 mpdc/ga2/turing.dat 派生)
### 下一步 (密钥推导战)
1. 候选密钥批量解: mpdc-16B / ga2 / turing.dat / sha256(materials) / 13B-nonce
2. 13B 头语义解码 → nonce→key 关系 (R24 已证: 同 nonce 对 13B 头相同!)
3. ga k209202-16B 块 = 设备材料块加密器 (状态补全后 = 主密钥源!)
## §11.59 R28: 头结构破译 + 密钥候选批量否定 (2026-08-29)
### 13B 头结构 (4 样本对比!)
```
0625: 7c da | 11 | 69 c9 f5 28 9c 4d 46 | a1 de 05 | 43 48 82
0647: 7c da | 11 | 69 c9 f5 28 9c 4d 46 | a1 de 05 | 11 bb 80
0649: 7c da | 01 | 89 c8 f5 08 9e 4c 48 | e3 ec 07 | 13 55 a7 (fresh-identity-B!)
full: 7c da | 11 | 69 c9 f5 28 9c 4d 46 | 95 6f 21 | 10 bb 80
```
- [2B 魔数 7cda][1B 版本: 01=首次/11=重注册][7B 会话ID (同会话 3 样本相同!)] [3B 会话动态][...]
- 0649 (fresh-B) 会话ID 完全不同 → 会话ID = 身份相关 (非纯随机!)
### 密钥候选批量解码 (真 sub_24D58, 20 密钥 × off-11 3920B)
- mpdc/mpdc_rev/md5/sha1/sha256(mpdc|sess/mpdc|sess|turing)/sess7pad/sess7trace/
mpdc_xor_sess/sha256(sess)/dat 派生 — **全部 lencheck=False** (尾字非长度!)
- 结论: 密钥 ≠ 简单材料哈希组合 — 派生逻辑含未知变换 (版本化/轮换!)
### 判定
- 完整加密链已验证 (deflate→pad+len→XTEA@24D58!) = 纯代码铸造的可执行底座
- **密钥派生 = ga 传输层内部 (k209202-16B块/0x24334 状态机!) — 需 turingga.so 的 IDA 导出**
(同 mfa 方法论: 反编译 = 派生逻辑可读!)
## §11.60 R29: 🎯 ga 全密钥/编码链映射 (IDA 导出到手!) (2026-08-29)
### ga 导出核心突破
1. **密钥槽: `&unk_5788` = f15e78d23033c2c4bac8a925677e863a — 与 mfa 3DF88 完全相同!!**
(turing 共享代码段常量 key!)
2. **ga XTEA 核心 = sub_1F1CC** (24D58 同款变体!) — 封套:
- 加密 sub_1F918 = [pad+4Blen] + sub_1F1CC(+n, key)
- 解密 sub_1F9C8 = sub_1F1CC(-n, key) + 尾字长度校验!
3. **解码链 sub_27DBC** = XTEA-dec + **sub_2E704 inflate** (全部确认!)
4. **b209202@0x20110 (SparseArray,[B,Map,I) JNI 分发**:
- a6==1 → sub_27FF8 = 解码 (27DBC + 5788!)
- a6==0 → **sub_23DF8 = 编码器** (sub_1BAD8 上下文 + sub_1AA64 喂入 + finalize!)
5. **k209202@0x219d8 16B 块链**: 16B→sub_19378对象(RiskDetectExt.ExtData schema!)
+ sub_24334 状态拷贝 + deflate(sub_2E904) + XTEA(sub_1F918, **key=16B输入!**)
6. sub_192B0: **"RiskDetectExt.ExtData" schema-string + sub_1A7A8("int32","string")!**
### harness
- ga 导入 127 槽补丁 (mprotect 0x48720 RELRO) + 直调 sub_1F1CC 5788 全偏移扫描
- **所有静态 5788/3DF88 解密 lencheck=False** → 请求密文密钥 ≠ 静态 key (进一步派生!)
### 下一步
1. sub_1BAD8/1AA64 (编码上下文!) — 请求密钥路径
2. b209202 a6=1 harness 全调用 (喂捕获密文 → 解码输出!)
3. 或: 版本字节 01/11 的密钥变体