Files
live-hub-py/docs/HUYA_HDID_RESEARCH.md
T
yml2213 0c6aff6a6d docs(huya): 逆向定位 32hex hdid = NativeEntry.getGUID() + 真机注入通道证据
核心结论:
- 32hex hdid (WUP 登录帧 field1.tag0) = com.huya.security.hydeviceid.NativeEntry.getGUID()
- 金样本 ed0db8 为金样本设备 GUID; 当前真机 M2102J2SC getGUID()=0a7dfaa882938a6ab502511452142c57(稳定)
- NativeEntry 全字段: getGUID(32hex)/getCDID(40hex)/getHDID(40hex)/getMID(16hex)/getSDID(base64 safedeviceid)
- 单机不可铸造: 改 serialno/ANDROID_ID/删files/hydevice持久化 均不变(硬锚底层硬件)
- libhydeviceid.so 为 OLLVM+datadiv 壳; 用 dlopen->JNI_OnLoad->RegisterNatives(env表索引215) 绕开混淆

脚本: phone_probe_registernatives2(注册表)/phone_java_call_nativeentry(Java读getGUID)/phone_read_hdid(native hook)
      phone_dump_hydev(dump解密so)/phone_probe_login_frame(登录帧)/arm64_disasm(反汇编)
证据: hdid_regtable/java_nativeentry/hdid_read/login_frames/hdid_probe* + libhydeviceid_{original,dump}.so
2026-08-28 12:30:34 +08:00

4.6 KiB
Raw Blame History

虎牙 32hex hdid 生成逆向研究(真机 M2102J2SC)

目标:搞清楚 WUP 登录帧 t1.t0 / field1.tag0 的 32hex hdid(金样本 ed0db8334cadd236c00cadf7e11ab5a5) 如何生成、是否能在同一台设备上铸造新值(实现"每账号独立设备")。 时间:2026-08-28(真机稳定注入通道打通后进行)。已定位 32hex = NativeEntry.getGUID()。

一、最终结论(决定性)

32hex hdidWUP 登录帧 field1.tag0= com.huya.security.hydeviceid.NativeEntry.getGUID() 的返回值。

  • 金样本 ed0db8...金样本设备的 getGUID() 值;
  • 当前真机 M2102J2SC 的 getGUID() = 0a7dfaa882938a6ab502511452142c57(32hex,交叉验证稳定: 在 5 个设备上报帧中出现 + Java 主动调用一致)。

getGUID() 每台设备唯一且稳定(硬锚硬件,同 getCDID/getHDID 一样),可被 NativeEntry 的 native 方法直接可靠读取。

因此**"生成 32hex hdid" 无需逆向算法**:每台真机天然由 libhydeviceid.so 生成并提供它独有的 32hex getGUID()。多真机设备池 = 每台调 getGUID() 采集各自唯一值。

单机不可铸造(3 个决定性实验)

ro.serialno / ANDROID_ID / 删 files/hydevice/ 持久化后重启,设备标识(UUID 文件名 + getGUID 等)全部不变 —— 因 libhydeviceid.so 基于底层硬件(SoC/IMEI)稳定生成,刷不掉。

二、NativeEntry 全部字段(已可靠读取)

通过 dlopen 拦截 → JNI_OnLoad(0x22c4c8) → RegisterNatives(JNIEnv 函数表索引 215)dump 出 com.huya.security.hydeviceid.NativeEntry 的 19 个 native 方法,Java 主动调用实测值:

NativeEntry 方法 真机实测值 对应 WUP 登录帧字段
getGUID() 0a7dfaa882938a6ab502511452142c57 field1.tag0 = hdid32hex
getCDID() 02df398797432eadefcc12767119ad5e80999389 field2.tag2 = device_id40hex
getHDID() 7c5387e0539c023c31c4ff0e807e7256117385ee field2.tag8 = 另一设备ID40hex
getMID() 1e8bdf7d4f7a01d3 MID16hex
getSDID() PQwemAN9...base64 180B safedeviceid
  • Java 类 com.huya.security.hydeviceid.NativeEntry 注册 method 地址(libhydeviceid.so 偏移): getCDID@0x20eb08 / getSDID@0x20ede8 / getHDID@0x20f0c8 / init@0x20e6e4 / getGUID@0x20f8c8 / getMID@0x20fba8 / linkId@0x20fe88(其余 setPhoneInfo/setMotionEvent/上报类略)。
  • 另有 com.huyaudb.HuyaAuthCore 注册 init/unInit/sendMsg/receiveNetWUP 消息收发)。

hdid 名称混淆(已彻底澄清)

项目里"hdid"对应多个不同值

  1. 32hexWUP 登录帧 field1.tag0= getGUID() —— 设备 GUID,硬锚,每台唯一;
  2. 40hex 7c5387 = getHDID() = huyaudb 上报里被命名为 "hdid"同名不同值);
  3. 40hex 02df39 = getCDID() = device_id
  4. 16hex 1e8bdf7d4f7a01d3 = getMID()
  5. 凭据文件 files/hydevice/<UUID>64a33427...)持久化加密凭据。

三、libhydeviceid.so 加壳(静态逆向极难,但已绕过)

  • OLLVM 混淆based on Obfuscator-LLVM 9.0.9svn+ datadiv 运行时壳~70 个 .datadiv_decode* 解密函数 + hy_fopen64/hy_read/hy_syscall/hy_write/hyftell 文件 I/O 壳 + 读 TracerPid 反调试)。
  • dlopen→JNI_OnLoad→RegisterNatives 动态拿到方法表,绕开混淆,无需反汇编。
  • 已 dump 解密后代码:evidence/diag_phone/libhydeviceid_dump.so(内存态)+ 原始 libhydeviceid_original.so

四、可行方向(服务于"每账号独立设备")——推荐,已具备采集能力

多真机采集设备池:每台真机用真机注入通道(spawn+art_callsite bypass)读 {getGUID(32hex), getCDID(40hex), getHDID(40hex), getMID(16hex), getSDID(base64)}, 这些值每台唯一、稳定、被服务端认可。采集多台建设备池轮换,即可实现每账号独立设备。 无需逆向铸造算法,直接读 NativeEntry 即可。

五、实验脚本(真机)

  • scripts/phone_probe_registernatives2.pyv7dlopen→JNI_OnLoad→RegisterNatives 注册表 + get* 地址)
  • scripts/phone_java_call_nativeentry.pyJava 主动调 NativeEntry get 全家桶,读取 32hex GUID*
  • scripts/phone_read_hdid.pyv8native hook get* 用原生 JNI GetStringUTFChars 解码)
  • scripts/phone_dump_hydev.pydump 解密后的 libhydeviceid.so
  • scripts/phone_probe_hdid*.py(早期符号 / 属性 / 文件探针,过程记录)
  • 证据:evidence/diag_phone/{hdid_regtable,hdid_read,java_nativeentry,login_frames}.json / libhydeviceid_{original,dump}.so