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

213 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 虎牙 App 登录逆向笔记
> 抓包来源:`/Users/yml/Desktop/抓包/hy/8.23/`(登录-1.har / 登录-5.har
> 分析工具:`tools/taf_decode.py`TAF/JCE 纯字节流解码)、`tools/hy_har_parse.py`HAR 批量解析)
> 状态:App 登录**前置风控链路**已完整解明;**登录提交(最后一跳)走 cdn.wup.huya.com 长连接 + SSL Pinning,尚未解出明文**。
---
## 一、结论速览
1. **App 登录 ≠ Web 登录**,是两套完全不同的协议栈:
- **Web 登录(旧,appId=5002**`udblgn.huya.com/web/v2/passwordLogin` + `udbrtt.huya.com/auth/pt/*` 滑块 + `df.huya.com/web/df/token|collect`。已在 `core/huya/login.py``e语言/huya/登录/虎牙操作.txt` 实现。
- **App 登录(新,appId=5008**`wsapi.huya.com`servant=`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` 与裸 IP`211.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.com``metric.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.t5``dfpReport 响应 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 | `60576904`md5 取前 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 unpin**hook OkHttp `CertificatePinner.check` + conscrypt `TrustManagerImpl.verifyChain`,让 Reqable 能解 cdn.wup.huya.com。
---
## 九、工具使用
```bash
# 解码某个 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
```