虎牙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辅助函数
- 新增证据金标准 + 协议文档
This commit is contained in:
yml2213
2026-08-25 02:59:20 +08:00
parent 06a0a1e7d3
commit ff99c036f8
14 changed files with 2185 additions and 0 deletions
@@ -0,0 +1,309 @@
# 虎牙 App 密码登录 — WUP 明文抓包实证(最终)
> 状态:**密码登录完整 WUP 请求已抓到(TLS 加密前明文)**
> 证据:`/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/evidence/derived/huya-wup-full3.jsonl`
> 方法:hook `createWupRequestData<AppLgnPasswordLoginReq>` onLeave 拿到序列化后的 WUP 字节
---
## 一、密码登录 WUP 请求(核心结论)
| 项 | 值 |
|---|---|
| **servant** | `huyaudbwebui` |
| **func** | `hypasswordLogin` |
| **协议** | WUP / TAF(JCE) 二进制序列化 |
| **msgType** | 4097 (0x1001) = `MsgLogin.mMsgId` |
| **请求大小** | 1356 字节 |
| **密码** | SHA1(明文) hex,无盐(`772ed992b0e161276f44ec63671e60155c506294` |
| **name** | `hy_300023887``hy_` + 虎牙号) |
---
## 二、WUP 帧完整结构(已 100% 逆向定稿)
> 金标准文件:`evidence/wup_passwordlogin_taf.bin`1015 字节 TAF 二进制)+ `evidence/wup_passwordlogin_base64.bin`1356 字节 base64,即 HTTP body
> HTTP`POST https://udblgn.huya.com/open/hy/passwordLogin``Content-Type: application/x-wup`
### 2.1 外层:WUP RequestPacket(标准 JCE/TAF 序列化)
```
[0:4] uint32 BE 总长 = 1015
JCE RequestPacket 字段(head = (tag<<4)|type:
tag1 iVersion = 3 (INT8: 10 03)
tag2 cPacketType = 0 (ZERO: 2c)
tag3 iMessageType = 0 (ZERO: 3c)
tag4 iRequestId = session(3251699) (INT32: 42 00 31 9d f3)
tag5 sServantName = "huyaudbwebui" (STR1: 56 0c + 12B)
tag6 sFuncName = "hypasswordLogin" (STR1: 66 0f + 15B)
tag7 sBuffer = MAP{...} (SIMPLE_LIST: 7d 00 01 03 c1 + 961B)
tag8 iTimeout = 0 (ZERO: 8c)
tag9 context = {} (空map) (MAP: 98 0c)
tag10 status = {} (空map) (MAP: a8 0c)
```
> 关键澄清:之前误认为 `'f'(0x66)` 和 `'}' (0x7d)` 是分隔符,实为 JCE 字段头:
> - `0x56` = 字段5(sServantName)+STRING1
> - `0x66` = 字段6(sFuncName)+STRING1
> - `0x7d` = 字段7(sBuffer)+SIMPLE_LIST
### 2.2 中层:sBuffer961 字节)是一个 MAP
```
MAP (2条目):
"_wup_data" → 915 字节 (内层登录请求 struct)
"wupudbrequest_v0" → 5 字节 = 02 00 31 9d f3 (版本2 + session INT32)
```
### 2.3 内层:_wup_data915 字节)登录请求 struct(完整字段映射)
```
_wup_data = STRUCT {
field0 = STRUCT { # 元数据+风控
tag0 = 0
tag1 = "1.0" # 协议版本
tag2 = 207B JSON (associationId/funcName/group/id/session/step/stillLogin/traceId/type/uid/userContext)
tag3 = "5008" # bizAppid
tag4 = 3 (INT8) # = type
tag5 = 180B safedeviceid # 设备指纹(base64)
tag6 = ""
tag7 = ""
tag8 = 210B userAction JSON # 风控行为轨迹
tag9 = ""
}
field1 = STRUCT { # 设备基础信息
tag0 = "ed0db8334cadd236c00cadf7e11ab5a5" # hdid (32 hex)
tag1 = "13.4.22" # App版本
tag2 = "1.0.80138" # SDK版本
tag3 = ""
tag4 = "127.0.0.1" # 占位
tag5 = "xiaomi" # 厂商
tag6 = ""
}
field2 = STRUCT { # 设备详情
tag0 = 1 (INT8)
tag1 = "M2102J2SC" # 型号 thyme
tag2 = "02df398797432eadefcc12767119ad5e80999389" # 设备唯一ID(40 hex)
tag3 = "android"
tag4 = "M2102J2SC,30,11" # 型号,SDK,版本
tag6 = "1080" # 屏宽 (tag5缺失)
tag7 = "2120" # 屏高
tag8 = "7c5387e0539c023c31c4ff0e807e7256117385ee" # 另一设备ID(40 hex)
}
field3 = STRUCT { # 登录凭证
tag3 = "hy_300023887" # name (hy_虎牙号)
tag4 = "772ed992b0e161276f44ec63671e60155c506294" # password = SHA1(明文)
tag5 = LIST["5008"] # appid列表
tag6 = 1 (INT8)
tag7 = {} (空map)
tag8 = [] (空simplelist)
}
}
```
### 2.4 原始可读字符串(完整)
TAF 序列化,可读字符串全量提取如下(关键字段用换行标出):
```
AAAD9xADLDxCADGd81YMaHV5YXVkYndlYnVpZg9oeXBhc3N3b3JkTG9naW59... ← WUP 头
└─ "huyaudbwebui" ← servant
└─ "hypasswordLogin" ← func
_wup_data 字段(关联元数据 JSON:
{"associationId":8193,
"funcName":"hypasswordLogin",
"group":1,
"id":4097, ← msgType
"session":3251699,
"step":1,
"stillLogin":false,
"traceId":"0b8f098ff64a5bdc-23473-94783833787595172451",
"type":3,
"uid":0,
"userContext":""}
业务数据:
5008 ← bizAppid (appId)
PQwemAN9NHkZKoMqVT... ← safedeviceid (设备指纹, base64)
userAction JSON:
{"curl":"登录页",
"furl":"我的",
"latitude":"-1.0",
"longitude":"-1.0",
"ssid":"",
"user_action":[
{"id":"24","time":"1787595171950","x":"277","y":"1057"},
{"id":"11","time":"1787595172444","x":"296","y":"952"}]}
设备信息字段:
ed0db8334cadd236c00cadf7e11ab5a5 ← hdid (硬件ID)
13.4.22 ← App 版本
1.0.801386 ← 某版本号
127.0.0.1 ← 回环地址(占位)
xiaomi ← 厂商
M2102J2SC ← 设备型号 (thyme)
M2102J2SC,30,11 ← 型号,SDK版本,版本
android ← 系统
1080 / 2120 ← 分辨率
7c5387e0539c023c31c4ff0e807e7256117385ee ← 另一个设备ID
登录凭证:
hy_300023887 ← name
772ed992b0e161276f44ec63671e60155c506294 ← password (SHA1)
结尾:
wupudbrequest_v0 ← WUP 请求类型标记
AAFAgAxnfOMmAyoDA== ← base64 尾巴(签名/校验)
```
---
## 三、WUP 请求的三层结构
```
┌──────────────────────────────────────────────────────┐
│ 第1层: WUP/TAF 外层 │
│ servant = "huyaudbwebui" │
│ func = "hypasswordLogin" │
│ _wup_data = { associationId, funcName, group, id, │
│ session, step, stillLogin, traceId, │
│ type, uid, userContext } │
├──────────────────────────────────────────────────────┤
│ 第2层: 业务数据 (AppLgnPasswordLoginReq) │
│ bizAppid = 5008 │
│ name = "hy_300023887" │
│ password = SHA1(明文) │
│ userAction = 风控行为 JSON │
│ safedeviceid / hdid / 设备信息 │
├──────────────────────────────────────────────────────┤
│ 第3层: 网络传输 │
│ TLS 1.3 → 116.162.209.x:443 (udblgn.huya.com) │
│ (自定义 BoringSSL / hyssl) │
└──────────────────────────────────────────────────────┘
```
---
## 四、网络层(实证)
- **密码登录走 TLS 443**(不是之前看到的 80 端口图片 CDN)
- 目标 IP`116.162.209.83-86``udblgn.huya.com`kunluncan CDN
- TLS 层:libhycrypto.so = 定制 BoringSSLhyssl),符号已 strip
- 连接池:App 启动时预建立,登录时复用
- **HTTP body = 原始 TAF 二进制(1015字节),NOT base64**base64 是 hook 层返回值的表示形式,发送前已解码)
---
## 四·五、纯 Python 重放验证(✅ 成功)
**关键结论:纯 Python 可以复刻密码登录请求,服务端返回完整登录响应。**
```
POST https://udblgn.huya.com/open/hy/passwordLogin
Content-Type: application/x-wup
Body: 原始 TAF 二进制 (1015字节, 非base64)
```
重放金标准请求(`wup_passwordlogin_taf.bin`)后,服务端返回 **1514 字节登录响应**
| 字段 | 值 |
|---|---|
| **uid** | `1199666914671` |
| **name** | `hy_300023887` |
| **手机号掩码** | `001******7524` |
| **token** | 346 字节 base64(已存 `evidence/huya_token.txt` |
| **uc_states** | `1`(成功标记) |
这证明:
1. ✅ 请求格式 100% 正确(服务端无 BAD_REQUEST
2. ✅ 设备指纹(safedeviceid/hdid)可复用(重放旧值仍通过)
3. ✅ userAction 可复用(旧轨迹未被风控拒绝)
4. ✅ TLS 指纹不阻断
> 注:此验证是「重放」,即复用已抓到的完整请求。要批量登录新账号,还需编码器动态构造新请求(替换 name/password/session/traceId/userAction)。
---
## 四·六、编码器 + 动态构造验证(✅ 完整成功)
**编码器已实现,逐字节复现金标准,且能动态构造全新请求。**
1. **`tools/huya_wup_encoder.py`** — 自检通过:用已知字段构造,输出与金标准逐字节一致(1015 字节)
2. **动态构造测试** — 用新 session3251799)、新 traceId 构造全新请求(1009 字节),发送后返回:
| 字段 | 值 |
|---|---|
| **uid** | `1199666914671` |
| **name** | `hy_300023887` |
| **手机号掩码** | `001******7524` |
| **token** | 全新 token`XAQBRJ3H...` |
这证明 **纯 Python 可以完整复刻虎牙密码登录**
1. ✅ 帧格式(JCE/WUP)逆向定稿
2. ✅ 编码器逐字节复现
3. ✅ session/traceId 可自由生成(服务端接受新值)
4. ✅ 设备指纹可复用
5. ✅ 动态构造请求返回有效登录响应(uid + token)
### userAction 随机化(✅ 已实测)
`userAction` 只是请求里的一个普通 JSON 字符串字段,编码器已暴露 `user_action_json` 参数。实测**随机坐标 + 随机时间戳**同样能通过登录(返回 uid + token),服务端对该字段不做严格校验。
编码器提供辅助函数:
- `make_user_action(now_ms=None)` — 生成随机 userAction JSON(坐标/时间戳/点击间隔随机)
- `make_trace_id(pid=0)` — 生成 traceId
> 未实测定论:批量高频登录时,服务端是否按**账号/设备/IP 维度**限流或风控。这需要实测才能确认,与 userAction 无关。
---
## 五、对「纯 Python 复刻」的启示(已实证更新)
**TLS 指纹已实测:不阻断。** 普通 Python `urllib` 直连 `udblgn.huya.com` 能正常收到业务响应。
要脱离 App 用 Python 发密码登录请求,需要:
1.**密码**`sha1(明文)` —— 已破解,纯 Python 可做
2.**name**`hy_虎牙号` —— 已知
3.**servant/func**`huyaudbwebui` / `hypasswordLogin` —— 已知
4.**WUP 帧格式**:标准 JCE RequestPacket,已 100% 定稿(见第二节)
5.**Content-Type**`application/x-wup`body = 原始 TAF 二进制
6.**重放验证**:服务端返回 uid + token(纯 Python 可行)
7.**TAF/JCE 编码器**`tools/huya_wup_encoder.py`(自检通过,逐字节复现)
8.**_wup_data 元数据**`session`/`traceId` 可自由生成(服务端接受新值)
9.**设备指纹(safedeviceid/hdid**:可复用旧值(缓存一次即可)
10.**userAction**:随机坐标/时间戳即可通过(编码器内置 `make_user_action()`
> 唯一待实测项:批量高频登录时的账号/设备/IP 维度限流(与 userAction 无关)。
---
## 六、关联证据文件
- `huya-wup-plain.jsonl` — 第一次明文抓包(sendMsg JSON 层,305 事件)
- `huya-wup-full3.jsonl` — 完整 WUP 序列化(含本次密码登录 1356 字节)
- `wup_passwordlogin_taf.bin` — 金标准:1015 字节 TAF 二进制(= HTTP body,编码器自检依赖)
- `wup_passwordlogin_base64.bin` — 金标准:1356 字节 base64hook 返回值)
- `wup_response_badreq.bin` — 服务端响应帧(383 字节,returnCode=104
- `tools/huya_wup_encoder.py` — WUP 帧编码器(自检通过,逐字节复现金标准,含 `make_user_action`/`make_trace_id`
- hook 脚本:`hook_huya_wup_full.js`
- runner`run_huya_netcapture.py`
> 含有效登录 token 的响应文件(`wup_response_login.bin`、`wup_response_new_session.bin`、`huya_token.txt`)已加入 `.gitignore`,不提交。
---
## 七、结论与下一步
已完成并验证:
1.**TLS 指纹**:实测不阻断(Python urllib 正常收到业务响应)
2.**TAF 编码器**`tools/huya_wup_encoder.py`,逐字节复现金标准
3.**纯 Python 登录**:重放 + 动态构造均返回 uid + token
4.**userAction 随机化**:随机坐标/时间戳通过
待实测(唯一未定论项):
1. **批量高频登录的限流/风控**:需用编码器连续发请求,观察账号/设备/IP 维度是否触发限制(错误码、频率阈值)
2. **集成进 `core/huya/login.py`**:把 APP 密码登录(`/open/hy/passwordLogin`)作为 Web 登录被风控阻断时的替代路径