Files
live-hub-py/docs/HUYA_HDID_ALGORITHM_GEN.md
T
yml2213 49aae8b74c docs(huya): 算法生成路径分析 - 标准密码学原语定位/固定密钥候选/决定性实验/双路径(静态对拍+unidbg)
- dump 静态取证: AES S-box@0x36be82 / MD5 IV@0x36c190 / SHA1 K@0x36c1a0 / base64@0x3e8880
- .data 明文密钥候选: 7jbF5...(=AES-256 key) / HuyaUdb1928374650qwertyuiop / 865a4924...×2 / 961c151d...(SHA1)
- getGUID(0x20f8c8) 反汇编确认标准 OLLVM 平坦化 + 内部调用锚点 0x46140/0x1d6e68
- 指出'单机不可铸造'实验未覆盖 fileshydckey/全清数据/IMEI/qimei → 种子集可能可再生成
- 决定性实验 E1-E4(输入图+差分语料) + 路径A(纯Python黑盒对拍, 不需要解全部OLLVM) + 路径B(unidbg 仿真金测试)
- 服务端验收闭环: 铸币→dfpReport注册→WUP登录, 从第一天起验证
2026-08-28 12:55:20 +08:00

11 KiB
Raw Blame History

虎牙 32hex hdid 算法生成路径分析(不用采集,纯算法铸币)

前置结论(见 HUYA_HDID_RESEARCH.md):32hex hdidWUP 登录帧 field1.tag0= com.huya.security.hydeviceid.NativeEntry.getGUID();真机 M2102J2SC 金值 0a7dfaa882938a6ab502511452142c57getGUID 是 getter,值在 init()(偏移 0x20e6e4 时由 libhydeviceid.soOLLVM 混淆 + datadiv 壳)算出并缓存。

用户约束:不能用真机采集设备池;必须用算法生成(在代码里根据可控输入铸出 一套又一套服务端认可的设备身份)。本文是"如何继续"的完整分析。

时间:2026-08-280c6aff6/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 有 AES256 位密钥可能性大)
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 解密后的明文)

常量 形态 角色猜测
7jbF5IiqqIDdgEtiNQLzJSuTZmRk8sbc32B,出现 8+ 次) base64 风格 32 字符 AES-256 固定密钥(最可能)
HuyaUdb1928374650qwertyuiop27B,出现 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 记录其内部调用锚点(模块偏移): 0x461400x1d6e68 等(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 密钥 fileshydckeyinit() 窗口 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 启动噪音 窗口,需要收紧到库内。
  • E3IMEI 变更(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/CBCHMAC-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_getopen/readioctl、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 自动过验能力可兜底。