# 虎牙 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/时间戳等字段。