- 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辅助函数 - 新增证据金标准 + 协议文档
132 lines
5.7 KiB
Markdown
132 lines
5.7 KiB
Markdown
# 虎牙 App 密码登录协议 — Frida 抓包实证
|
||
|
||
> 状态:**密码登录完整链路已抓包实证**
|
||
> 方法:spawn + libmsaoaidsec 绕过 + hook sendMsg/jbyteArray2str/str2jbyteArray(TLS 加密前的明文层)
|
||
> 证据文件:`/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/evidence/derived/huya-wup-plain.jsonl`
|
||
> Java 反编译:`/tmp/hy_jadx5/sources/com/huyaudbunify/`
|
||
|
||
---
|
||
|
||
## 一、密码登录请求(msgType = 0x1001 = 4097)
|
||
|
||
`MsgLogin.mMsgId = 4097 (0x1001)`,Java 层 `HuyaAuth.login()` → `sendMessage(4097, json)` → native `sendMsg(JNIEnv, jclass, jlong, jbyteArray)`。
|
||
|
||
**请求 JSON**(TLS 加密前明文,抓包实证):
|
||
```json
|
||
{
|
||
"bizAppids": [],
|
||
"isAuthLogin": false,
|
||
"lgnExtParam": {},
|
||
"name": "hy_300023887",
|
||
"password": "772ed992b0e161276f44ec63671e60155c506294",
|
||
"userAction": "{\"curl\":\"登录页\",\"furl\":\"我的\",\"latitude\":\"-1.0\",\"longitude\":\"-1.0\",\"ssid\":\"\",\"user_action\":[{\"id\":\"24\",\"time\":\"1787593636012\",\"x\":\"253\",\"y\":\"1573\"},...]}"
|
||
}
|
||
```
|
||
|
||
| 字段 | 值/规则 | 说明 |
|
||
|---|---|---|
|
||
| `name` | `"hy_" + 虎牙号` | 虎牙号 = 纯数字账号 |
|
||
| `password` | **标准 SHA1(明文密码) hex,无盐** | 40 字符小写 hex |
|
||
| `isAuthLogin` | false | 普通密码登录 |
|
||
| `bizAppids` | [] | 目标业务 appId 列表(空=主 app) |
|
||
| `lgnExtParam` | {} | 扩展参数 |
|
||
| `userAction` | 风控行为 JSON | 点击轨迹、时间戳、坐标、curl/furl、经纬度 |
|
||
|
||
**密码哈希**(用户确认 + 抓包实证):
|
||
```
|
||
password = sha1(明文密码).hexdigest()
|
||
例如 sha1("某密码") = "772ed992b0e161276f44ec63671e60155c506294"
|
||
```
|
||
> 无盐、无 HMAC、无 RSA,就是裸 SHA1。
|
||
|
||
---
|
||
|
||
## 二、登录响应(msgType=0x1001 返回)
|
||
|
||
```json
|
||
{
|
||
"lastLgnTime": 1787593640035,
|
||
"token": "9QDzAAEDAAAAb2W4URcBAAABAGNI4jSgAQQANTAwOCgAMDJkZjM5ODc5NzQzMmVhZGVmY2MxMjc2NzExOWFkNWU4MDk5OTM4OQQANTAwOKIAAhKJiMHWd/m1LAKSxxpg1cjDxJEIG+ooj8BGSC5NBXb23Trpcz85MOqpZxhLmSeQ9c2MGzBk0oU4iR2PR0Ak6/t50Ej9sdzHjDLa2wcDiRofNyEZzuU1KcIQqrGDwwwfouEwuagTPI0ZrP5vAFJe8uccW5BKRVoqDDsXc09nIW7HX5Yp+9cT0nr8t/8+/3IdTXhFOZYmDN6gHaQI+ToQivXIAAAAAA==",
|
||
"tokenType": 2,
|
||
"uid": 1199666914671
|
||
}
|
||
```
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| `token` | Base64,内含 TAF 二进制(`9QDzAAED...` 头),是后续凭据登录/接口调用的 token |
|
||
| `tokenType` | 2(密码登录产生的 token 类型) |
|
||
| `uid` | 1199666914671(该账号 uid) |
|
||
| `lastLgnTime` | 上次登录时间戳(ms) |
|
||
|
||
---
|
||
|
||
## 三、完整登录调用链(Java → native → 网络)
|
||
|
||
```
|
||
Java: HuyaAuth.login(name, password, isAuthLogin, bizAppids, callback)
|
||
└─ MsgLogin (mMsgId=4097=0x1001)
|
||
└─ ReqLogin.toString() → JSON(含 SHA1 密码)
|
||
└─ HuyaAuthMgr.sendMessage(4097, jsonBytes)
|
||
└─ HuyaAuthCore.sendMsg(jlong, byte[]) [native JNI]
|
||
└─ sendMsg(JNIEnv, jclass, jlong msgType, jbyteArray)
|
||
└─ jbyteArray2str → 明文 JSON
|
||
└─ UdbHandlerMgr.getPlatFormMsgHandler() 处理
|
||
└─ TAF/WUP 包装(servant + func)
|
||
└─ TLS (libhycrypto 自带 BoringSSL)
|
||
└─ 目标: udblgn.huya.com / udb3lgn.huya.com (443)
|
||
```
|
||
|
||
**关键符号地址**(libudbauthunify.so):
|
||
| 函数 | 地址 | 作用 |
|
||
|---|---|---|
|
||
| `sendMsg` | 0x26244c | Java→native 消息入口(msgType + jbyteArray) |
|
||
| `jbyteArray2str` | 0x262b00 | jbyteArray→std::string(请求明文) |
|
||
| `str2jbyteArray` | 0x262be8 | std::string→jbyteArray(响应明文) |
|
||
| `hyudb_otp_encrypt` | 0x32fa24 | OTP/dckey 加密(XXTEA+AES) |
|
||
|
||
---
|
||
|
||
## 四、msgType 观察(本次抓包 305 事件)
|
||
|
||
| msgType | 次数 | 含义 |
|
||
|---|---|---|
|
||
| `0xb000003` | 92 | getLoginData(读取登录态/token),高频轮询 |
|
||
| `0xb000021` | 4 | setDeviceInfo(hdid + safedeviceid) |
|
||
| **`0x1001` (4097)** | 1 | **密码登录**(MsgLogin) |
|
||
| `0xb000001` | 1 | 登录后 getLoginData |
|
||
| `0xb000024` | 1 | 登录后获取数据 |
|
||
| `0xb000013` | 1 | getConfig(TAF 二进制,servant=mobileuif) |
|
||
|
||
---
|
||
|
||
## 五、与 OTP 加密链的关系
|
||
|
||
密码登录时序(抓包时间线):
|
||
```
|
||
1787593639831 sendMsg msgType=0x1001 ← 密码登录 JSON 提交
|
||
1787593639988 crypto-otp b=2 ← 紧接着触发 OTP/dckey 加密(AESkeyMgr b=2)
|
||
1787593639996 sendMsg msgType=0xb000001 ← 登录成功后的凭据流程
|
||
```
|
||
|
||
即:**密码登录本身用 SHA1 密码 + WUP 明文 JSON;登录成功后,native 层用 OTP 加密链(XXTEA+AES)生成 dckey 凭据,供后续业务接口(token/cred)使用**。
|
||
|
||
之前破解的 OTP 加密链(`tools/huya_app_crypto.py`)对应的是**登录后的凭据续期/设备绑定**,不是密码本身。
|
||
|
||
---
|
||
|
||
## 六、servant / URL
|
||
|
||
- 密码登录 CGI:`/{servantName}/hypasswordLogin`
|
||
- URL:`https://udblgn.huya.com/open/hy/passwordLogin`(国内版)
|
||
- servant 名运行时传入,登录类走 `huyaudb`/`huyaudbwebui`
|
||
- 设备信息:`getCgi()` → `/{servantName}/setDeviceInfo`(hdid + safedeviceid)
|
||
|
||
---
|
||
|
||
## 七、待确认项(剩余,影响较小)
|
||
|
||
1. **userAction 是否必填/可复用**:风控行为采集(点击轨迹),批量登录时能否固定/复用同一份,还是每次都要真实模拟。
|
||
2. **登录接口是否校验 OTP dckey**:密码登录请求本身是否需要先拿到 dckey(本次抓包 OTP 在密码登录之后触发,说明密码登录可能不依赖 dckey)。
|
||
3. **token 的 TAF 结构解析**:响应 token 的 Base64 内是 TAF 二进制(`9QDzAAED...`),需解析出 uid/appid/时间戳等字段。
|