Files
live-hub-py/docs/虎牙证书算法闭环-差分实证.md
T
yml2213 a0ba2fe020 feat(huya): 证书算法完全闭环 - 差分法证明标准AES-128-ECB并复现铸证
- NativeFunction直调差分: FIPS-197 KAT逐位一致, UdbAESUtil无魔改
- 证书 = b64([0x0C][key_idx][AES128-ECB(key16, zeropad(P1_187B))])
- P1结构: [010400][5008][u16+20B随机][u16+40B指纹hex][u16+114B hyCred]
- 密钥: rodata默认三条(AESkeyMgr id=3/4/5) + 服务端下发会话钥(key_idx区分)
- tools/cert_forge.py 重铸证书与活体密文 FULL MATCH
2026-08-25 15:21:11 +08:00

4.2 KiB
Raw Blame History

虎牙 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。

五、复现步骤

# 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(下一步)。