# 虎牙 udb 登录证书(biz_token)算法 — 差分法完全闭环(2026-08-25) > 状态:**加密算法 100% 复原,Python 重铸与活体密文逐位一致** > 方法:静态反汇编(objdump) + Frida NativeFunction 直调差分 + C1 构造器抓钥 > 工具:`tools/udb_aes.py`(参考实现) `tools/cert_forge.py`(解析/铸造) —— 自检均通过 > 证据:`evidence/aes_diff.json` `evidence/cert_keycap.json` --- ## 一、结论一句话 ``` 证书 = base64( [0x0C][key_idx][ AES-128-ECB(key16, zeropad(P1_187B)) ] ) ``` - **UdbAESUtil 是教科书级标准 AES-128-ECB**,无任何魔改: - FIPS-197 KAT 直调逐位一致:`Cipher(00112233..ff, key000102..0f) = 69c4e0d8...` - KeyExpansion 标准展开,轮密钥按「字表转置」存储(176B @ this+0x208) - 对象布局:+0x00 vptr | +0x08 S-box拷贝 | +0x108 InvS-box拷贝 | +0x208 轮密钥 - ZeroPadding:补 `\0` 到 16 倍数,**不追加整块**(187→192)。 - 此前"标准解密对不上"的原因:**拿错了密钥**(真钥不是 HuyaUdb 主密钥字符串本身)。 ## 二、P1 明文结构(187B,两份活体样本逐字段对齐) | 字段 | 长度 | 说明 | |---|---|---| | `[01][04][00]` | 3 | 固定头 | | `"5008"` | 4 | appId | | `[u16_le=20]` + rnd | 22 | 每次生成随机(os.urandom 即可) | | `[u16_le=40]` + fp | 42 | 设备指纹 ASCII hex `02df39879743...`,设备绑定跨会话不变 | | `[u16_le=114]` + cred | 116 | **hyCred 二进制**(=base64(udb_cred) 解码),账号绑定 | ## 三、密钥机制(AESkeyMgr) - rodata `0x1bc810` 内置三条 24 字符默认密钥(`AESkeyMgr::C2` 注册 id=3/4/5): ``` 4VYcPdvKKqjBHZtCmbroRXHk ← 本次活体实测在用(encode_aes 收整条,C1 只取前16字节) xXEDWqiKLGwEZ6HubEiswCqK 3FMHubdKosFrhmXNLHTNHZwe ``` - 证书第二字节 = **key_idx**:今日样本 `0x20` ↔ 上表第一条;更早样本 `0x50` 用三条默认钥均解不开 ⇒ 存在**服务端下发的会话密钥**(对应 dckey/check 的 NEWKEY/key_id 老线索)。服务端按 idx 选钥解密。 - ⚠️ `HuyaUdb1928374650qwertyuiop` 是本地登录态文件加密用的主密钥, **不是**证书密钥(此前误判的根源)。 ## 四、调用链(函数地址级) ``` HandlerGetH5InfoEx::onHandler @0x38d548 └ BusinessCfg::getOtp @0x2689f4 └ gen_biz_token(BIZTOKEN,...) @0x3307e4 # P1 组装(enpack_header/body,cred_packls,xxtea) └ encode_aes(P1, key24str, out) @0x330218 # out.assign(常量); C1(key.data()) ├ UdbAESUtil::C1(uchar* key16) @0x24eb08 # ★抓钥点: 每次必经 └ UdbAESUtil::encrypt @0x250038 # 补零逐块 -> _encrypt@0x24f9f0 -> Cipher@0x24f314(10轮) └ CBase64::Encode ``` 全部符号在 `.dynsym` 导出(binary stripped 但 dynsym 完整),可直接 NativeFunction。 ## 五、复现步骤 ```bash # 1) 差分验证(直调 C1/Cipher, FIPS-197 KAT) /Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python scripts/hook_aes_diff.py # 2) 抓当前会话证书密钥 + 完整 wupData /Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python scripts/hook_cert_keycap.py # 3) 离线重铸自检 .venv/bin/python tools/cert_forge.py # FULL MATCH: True # 4) 参考实现自检 .venv/bin/python tools/udb_aes.py ``` frida 注意事项:手机端 frida-server 14.2.18,必须用 RE 项目 venv(frida==14.2.18); 时序不可变:force-stop → spawn挂起 → bypass_msaoaid_maps_art_callsite + mask_frida_maps_only 双脚本 → resume → 10s → patch_guard → 业务脚本。 直调 libc++ string 时**禁止让 push_back 触发 realloc**(会对 frida 缓冲区 operator delete 导致 abort——第一次闪退即此因,非反调试)。 ## 六、剩余开放项 1. **key_idx↔密钥映射的服务端语义**:0x20/0x50 的生成规则、下发通道 (dckey/check? 登录态 JSON?)、轮换周期。工程上可绕过:直接 hook C1 取当期钥。 2. **任意账号 hyCred 获取**(硬骨头②):APP WUP 密码登录已通, 待从登录态/HA.getCred 提取目标账号 114B cred 即可离线铸其证书。 3. 铸造证书的活体验证:新随机数重建证书 → bindQrLoginUser(下一步)。