Files
live-hub-py/docs/虎牙App登录逆向笔记.md
T
yml2213 ff99c036f8 虎牙App密码登录: 逆向WUP协议并实现纯Python登录编码器
- hook WUP序列化层抓取TLS加密前明文,确认密码登录走
  servant=huyaudbwebui / func=hypasswordLogin (msgType=0x1001)
- 逆向JCE RequestPacket帧格式(1015字节TAF二进制),定稿三层结构:
  WUP header(tag1-10) -> sBuffer Map -> _wup_data业务struct
- 澄清两个疑点字节: 0x66/0x7d 是TAF字段头(非分隔符)
- 实现 tools/huya_wup_encoder.py,逐字节复现金标准
- 实测纯Python动态构造登录成功(返回uid+token),TLS指纹不阻断
- userAction随机坐标/时间戳同样通过,内置make_user_action辅助函数
- 新增证据金标准 + 协议文档
2026-08-25 02:59:20 +08:00

9.7 KiB
Raw Blame History

虎牙 App 登录逆向笔记

抓包来源:/Users/yml/Desktop/抓包/hy/8.23/(登录-1.har / 登录-5.har 分析工具:tools/taf_decode.pyTAF/JCE 纯字节流解码)、tools/hy_har_parse.py(HAR 批量解析) 状态:App 登录前置风控链路已完整解明;登录提交(最后一跳)走 cdn.wup.huya.com 长连接 + SSL Pinning,尚未解出明文


一、结论速览

  1. App 登录 ≠ Web 登录,是两套完全不同的协议栈:

    • Web 登录(旧,appId=5002udblgn.huya.com/web/v2/passwordLogin + udbrtt.huya.com/auth/pt/* 滑块 + df.huya.com/web/df/token|collect。已在 core/huya/login.pye语言/huya/登录/虎牙操作.txt 实现。
    • App 登录(新,appId=5008wsapi.huya.comservant=huyaudbwebui)走 getDfpConfig → selectOperator → dfpReport 设备指纹,udbdf.huya.com/dckey/check 拿 dckey,运营商取号,最后登录提交进 cdn.wup.huya.com 长连接。
  2. Web 登录"风控加强"的本质Web 纯脚本无法伪造 App 侧的设备态dckey + dfp 指纹 + appId=5008),被风控拦下。登录接口本身没变,是前置设备校验变严

  3. 抓包抓不到登录的原因(两层锁):

    • TLS 层:cdn.wup.huya.com 与裸 IP211.91.243.102 等)有 SSL PinningReqable 中间人证书被拒,只留 CONNECT 隧道。
    • 应用层:statwup.huya.com 的部分 body 是 877de5c4... 纯二进制(应用层加密),即使破 TLS 也还要再解一层。

二、App 登录完整链路(已解明的普通 HTTP 部分)

按时间顺序(登录-5.har):

① 数美安全 SDK 初始化(App 启动)
   cfgc.zztfly.com/v6/gcf · cdn-api-auth.zztfly.com/api/initSecCdn · upc.flysleep.cn/v6/gcl
② 设备指纹配置
   wsapi.huya.com  huyaudbwebui.getDfpConfig      ← 只传 appId=5008,返回 80 字节加密配置
③ 选运营商(一键登录前置)
   wsapi.huya.com  huyaudbwebui.selectOperator    ← 传设备信息 + RSA 加密段,返回运营商索引
④ 设备指纹上报 ×4(循环)
   wsapi.huya.com  huyaudbwebui.dfpReport         ← 上报加密设备指纹,返回 32 位指纹 + action
   udbdf.huya.com/dckey/check                     ← 加密 body,返回 dckey
⑤ 运营商取号(联通)
   auth.wosms.cn/dro/netm/v1.0/qc
   nisportal.10010.com:9001/api?appid=2074469246116
⑥ 登录提交(❌ 未抓取)
   → 进入 cdn.wup.huya.com / 裸 IP 长连接(被 SSL Pinning 挡住)

wup.huya.com 上看到的 mobileui.getDynamicConfigHotFixUpdateInfo / reportMobileUpdateResult 只是 App 热更配置,与登录无关。statwup.huya.commetric.reportDetailV2 是埋点。


三、各接口字段级结构

1. getDfpConfigwsapiservant=huyaudbwebui

  • 请求 tReq{"t0": {"t0": "5008"}} —— 只传 appId。
  • 响应 tRsp{"t0": {"t0": {"5008": "<base64>"}}} —— 按 appId 返回配置。
  • base64 配置解码后 = 80 字节64+16 结构,疑似密钥材料):
hex: 34b946b716f1481d8584980b79cf756b962e79c999d752016904c1fc07d689
     694a148ef872206826745498088e61be3f062f7d8d1a0276e29c0048062ac8e2
     b58fe0d1ee376c412472d8b9124d0bbf7d

2. selectOperatorwsapiservant=huyaudbwebui

请求 key 是 _wup_data(注意不是 tReq),三层嵌套结构:

_wup_data.t0.t0 = {                      # 会话上下文
  t0: 0,
  t1: "1.0",                              # 协议版本
  t2: "hyudb_1787583730402",              # 会话 id = "hyudb_" + 毫秒时间戳
  t3: "5008",                             # appId
  t4: 0,
  t5: "PQwemAN9NHkZKoMqV...",             # RSA 公钥加密段(与 dfpReport 响应 t2 同前缀)
  t6:"", t7:"", t8:"", t9:""
}
_wup_data.t1 = {                          # SDK 信息
  t0:"", t1:"13.4.22"(App版本), t2:"2.0.8"(SDK版本), t3:"",t4:"",t5:"",t6:""
}
_wup_data.t2 = {                          # 设备信息
  t0: 1,
  t1: "M2102J2SC",                        # 机型
  t2: "02df398797432eadefcc12767119ad5e80999389",  # 40 hex = SHA1 设备指纹
  t3: "android",
  t4: "M2102J2SC,30,11",                  # 机型,Android版本,SDK版本
  t6:"", t7:""
}
_wup_data.t3 = [0, 1, 3]                  # 运营商列表
  • 响应_wup_data.t0.t1 = 1(选中运营商索引 1)。

3. dfpReportwsapiservant=huyaudbwebui

  • 请求 tReq
{t0: {t0:"", t1:"android", t2:"<加密体>", t3:"", t4:12}}
  • t2 加密体结构:10 字节固定魔数 + 变长加密数据

  • 魔数(跨 6 次请求完全一致):571882cf664bb39401ee

  • 加密数据长度 3300~4100 字节(每次递增,说明是累积式采集上报,每次追加新采集数据)

  • 响应 tRsp

{t0: {
  t0: "10",
  t1: "865a4924a40897ac1fcfe6b4c2cbb045",   # 32 hex = 稳定设备指纹(4 次响应一致)
  t2: "PQwemAN9NHkZKoMqV...",              # base64 = RSA 公钥加密的 action(每次变)
  t3: 43200,                                # 过期时间(秒)=12h
  t4: "action",
  t5: "7c5387e0539c023c31c4ff0e807e7256117385ee"  # 16 hex = 稳定 session hash4 次一致)
}}

4. dckey/checkudbdf.huya.comokhttp/3.14.9application/octet-stream

  • 请求 body 结构100 字节固定头 + 变长加密体(总长 543B 或 547B,两种长度差 4 字节)
    • 100 字节固定头(跨 5 个样本、两个会话完全一致):
32f1990eb8f46c503c3b5db81be58ae84dd3e961b991dd9e9c593aba8c78881c
f1281c555198710a1009f1ec897c46be8b26d81e8eab98f27242de3444315cab8
d617eae189d597e296099f0e5bc817a7554456c1af99515254ab7665a2a3f861a
7d941b
  • 变长加密体(543/547B 减 100B = 443/447B)每次变化。

  • 同一长度下,末尾 32 字节可复现(同设备+同 session 重复调用时)。

  • 响应{"dckey":"AAAAAMC1eP4iV43WYoI57ZOu0...","status":"success"}

    • dckey 固定前缀 AAAAAMC1eP4iV43WYoI57ZOu0(base64 前 16 字节),后续每次变。

5. 运营商取号(联通一键登录)

  • POST auth.wosms.cn/dro/netm/v1.0/qc
  • POST nisportal.10010.com:9001/api?appid=2074469246116&ret_url=<base64>

四、加密体分析汇总

加密体 位置 结构 猜测算法
dfpReport t2 wsapi 10B 魔数 571882cf664bb39401ee + 变长加密 自定义格式,魔数疑似协议版本/类型标识
dckey/check body udbdf 100B 固定头 + 443/447B 变长 固定头疑似 RSA/ECDH 公钥或设备态加密快照
getDfpConfig 配置 wsapi 响应 80B = 64B+16B 疑似 dfpReport 加密密钥 + IV/nonce
selectOperator t5 / dfpReport t2(action) wsapi base64,同前缀 PQwemAN9NHkZKoMqV RSA 公钥加密(同公钥 → 密文前缀相同)
statwup #7 statwup 400B 纯二进制 应用层加密(session-key

关键判断selectOperator.t5dfpReport 响应 t2 前缀相同 → 同一个 RSA 公钥加密,明文大概率是「设备信息 + 会话 key」的协商包。拿到这个公钥(APK 内嵌)就能解密。


五、稳定字段清单(可直接复用的常量)

字段 用途
appIdApp 场景) 5008 替代 web 的 5002
App 版本 13.4.22 versionCode=115315
SDK 版本 2.0.8 selectOperator.t1.t2
dfpReport 魔数 571882cf664bb39401ee t2 加密体前缀
dckey 固定头 100B(见上) dckey/check body 前缀
dckey 响应前缀 AAAAAMC1eP4iV43WYoI57ZOu0 dckey 前 16B
设备指纹(selectOperator) 02df398797432eadefcc12767119ad5e80999389 40 hex = SHA1
设备指纹(dfpReport) 865a4924a40897ac1fcfe6b4c2cbb045 32 hex,服务端下发
session hash(dfpReport) 7c5387e0539c023c31c4ff0e807e7256117385ee t5
会话 id 格式 hyudb_<毫秒时间戳> selectOperator.t2
appSign 60576904md5 取前 8,见 login.py 注释) App 场景签名

六、与现有 Web 登录(appId=5002)的差异

维度 Web(5002) App(5008)
登录入口 udblgn.huya.com/web/v2/passwordLogin cdn.wup.huya.com 长连接
设备指纹 df.huya.com/web/df/token+collect wsapi getDfpConfig+dfpReport + dckey/check
验证码 udbrtt pt/get3+verify3(滑块/点选) 运营商取号 + (未解明)
传输 JSON over HTTP TAF/WUP + 部分应用层加密

七、待破解项(按优先级)

  1. [最硬] dckey/check 的加密算法:100B 固定头 + 变长体。需 hook APK 里 dckey 生成方法。
  2. [核心] dfpReport t2 加密体:魔数 + 累积式采集数据。需 hook dfp 采集模块。
  3. [核心] 登录提交的 WUP servant/func + 参数:被 SSL Pinning 挡住,需 Frida unpin 或 hook 登录方法。
  4. [可解] RSA 公钥APK 内嵌,解出 selectOperator.t5 / dfpReport action 明文。
  5. [可解] getDfpConfig 80B 配置:确定是否 dfpReport 加密密钥。

八、Frida hook 点建议(供 hook 阶段参考)

  1. dckey 生成hook udbdf 请求发出前,dump 加密前明文。搜 com/duowan/kiwi 下 dckey / dcKey 相关类。
  2. dfp 采集hook dfpReport 的 t2 字节流写入点(writeTo/toByteArray)。
  3. 登录提交hook com.duowan.kiwi.base.login.UdbNetHelper(appSign 已逆向自此类),以及登录 servant 的 writeStruct("tReq", ...) 前的参数。
  4. SSL unpinhook OkHttp CertificatePinner.check + conscrypt TrustManagerImpl.verifyChain,让 Reqable 能解 cdn.wup.huya.com。

九、工具使用

# 解码某个 HAR 里的所有 WUP(TAF) 请求/响应字段
.venv/bin/python tools/hy_har_parse.py "/Users/yml/Desktop/抓包/hy/8.23/登录-5.har"

# 底层 TAF 解码器(可单独 import)
from tools.taf_decode import Taf, decode_struct_bytes