虎牙网页Cookie来源驱动合并与WSS传输门禁
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
>
|
||||
> 关联文档:`docs/dfpReport破解进度.md`(破解细节)、`docs/dfpReport生成流程.md`(零设备注册链)、
|
||||
> `docs/模拟器存活闪退诊断报告.md`(Frida 存活实验,模拟器)、`docs/虎牙纯Python全自动登录-总结与交接.md`。
|
||||
> Cookie 来源审计:`docs/HUYA_COOKIE合并审计与来源矩阵.md`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# 虎牙网页 Cookie 合并审计与来源矩阵
|
||||
|
||||
更新时间:2026-09-01
|
||||
|
||||
## 结论
|
||||
|
||||
`core/huya/app_login.py::_merge_web_cookie` 只应合并当前登录流程实际得到的认证值和已有网页 Cookie。Cookie 在抓包中出现,不代表可以凭长度或字符集在本地生成。
|
||||
|
||||
9.1 抓包中,活动 WSS 的角色查询和积分查询均成功,说明活动接口当前主要依赖核心认证字段;这不能证明随机设备字段、商城接口和支付链路会接受伪造值。
|
||||
|
||||
## 字段来源
|
||||
|
||||
| 字段 | 真实来源 | 当前策略 |
|
||||
| --- | --- | --- |
|
||||
| `udb_cred` | App WUP 登录响应的 114B `cred`,Base64URL 后 152 字符 | 本次值覆盖旧值 |
|
||||
| `udb_uid`、`yyuid` | WUP 登录响应解析出的真实 UID | 本次值覆盖旧值 |
|
||||
| `udb_biztoken` | `tryQrLogin(stage=2)` 响应 | 本次值覆盖旧值 |
|
||||
| `udb_passport`、`username` | 网页 Cookie/tryQrLogin/App 登录态中的真实 passport | 只有输入本身是 `hy_` 账号时才写入,否则保留已有值 |
|
||||
| `udb_version`、`udb_origin`、`udb_status` | 网页登录协议固定字段 | 写入协议固定值 |
|
||||
| `sdid`、`hdid` | `df/collect` 的 `Set-Cookie`/响应 | 本次指纹响应覆盖旧值 |
|
||||
| `guid` | 网页/launch 会话的 GUID32 | 只接受显式 `device_info["guid"]` 或 Session 值 |
|
||||
| `udb_guiddata` | 浏览器设备状态字段,和 `guid` 是不同值 | 只接受显式值,不再与 `guid32` 复制 |
|
||||
| `game_did`、`__yamid_*`、QIMEI、`udb_anobiztoken` | 浏览器/设备 SDK 状态,部分具有关联或签名 | 无真实值时省略 |
|
||||
| `udb_deviceid` | 浏览器设备 Cookie | 只接受格式正确且显式提供的值 |
|
||||
| `Hm_*`、`HMACCOUNT`、`*_rep_cnt`、`_yasids` | 页面统计与访问计数状态 | 只保留已有或显式提供值 |
|
||||
|
||||
## 9.1 抓包事实
|
||||
|
||||
同一网页会话内这些值保持稳定:
|
||||
|
||||
- `_qimei_uuid42`:42 位;`_qimei_h38`:38 位;`_qimei_fingerprint`:32 位。三者是 QIMEI 设备标识,不能用随机十六进制替代。
|
||||
- `udb_anobiztoken`:344 位 URL-safe 字符串,和 `udb_anouid` 共同属于匿名设备状态,不能用重复 UUID 字节拼接。
|
||||
- `guid` 与 `udb_guiddata` 均为 32 位,但抓包值不同,不能复用同一画像字段。
|
||||
- `udb_deviceid` 为 `w_` 加 19 位数字;格式可以校验,但仅凭格式无法得到真实值。
|
||||
- `huya_flash_rep_cnt`、`huyasp_rep_cnt`、`huya_hd_rep_cnt`、`huya_web_rep_cnt` 随访问过程变化,不能硬编码抓包历史值。
|
||||
|
||||
抓包中还存在 `huya_ua`,它是 WSS/HTTP RPC 的业务 UserId 字段,不属于持久化 Cookie;由 `wss_client.py` 和 `http_client.py` 按通道注入。
|
||||
|
||||
## 旧实现的问题
|
||||
|
||||
旧实现曾在空 Session 中生成 `__yamid_new`、`game_did`、三个 QIMEI 字段、`udb_anobiztoken`、`HMACCOUNT`、`_yasids` 和多个固定计数器。它们只能满足长度测试,不能证明值具有协议语义。
|
||||
|
||||
旧实现还把 `device_info["guid32"][:32]` 同时写入 `guid` 和 `udb_guiddata`,并把手机号直接写入 `udb_passport`/`username`。这两点会造成设备身份和账号映射偏差。
|
||||
|
||||
## 验证要求
|
||||
|
||||
1. 登录后必须检查 `udb_cred`、`udb_biztoken`、UID、`sdid`、`hdid` 的来源和值覆盖关系。
|
||||
2. 活动 WSS、商城 WSS、下单和支付分别验证,不能用角色查询成功替代全流程验证。
|
||||
3. 新增 Cookie 字段前,必须有同一会话的响应、页面状态或设备 SDK 输出作为来源证据;只有长度/正则证据时保持省略。
|
||||
### 9.1 抓包新增证据
|
||||
|
||||
`705-meta.json` 的 `GET www.huya.com/30596253` 在请求发出时已经携带 43 个网页 Cookie,
|
||||
包括 `guid`、`game_did`、三个 QIMEI、`udb_guiddata`、`udb_deviceid`、`udb_anobiztoken`
|
||||
和统计字段;该响应没有 `Set-Cookie`。随后 `714-meta.json` 的
|
||||
`POST df.huya.com/web/df/collect` 只返回 `sdid`、`hdid` 两个 `Set-Cookie`。
|
||||
|
||||
因此 App 的 `/web/cookie/verify` 不能兑换出完整网页 CK。网页设备字段来自浏览器此前
|
||||
持久化的 Cookie/Storage 和页面脚本运行态;当前 Python App 登录链不应声称能凭空补齐它们,
|
||||
也不在脚本中外挂浏览器。缺失时返回 `COOKIE_INCOMPLETE`,精英宝典任务不再回退 HTTP。
|
||||
|
||||
### 运行时复核(2026-09-01)
|
||||
|
||||
当前 Node 指纹运行器已接入 `__COOKIES__` 输出桥:只有 hydevice 通过
|
||||
`document.cookie` 实际写入的值才会进入 `HuyaSdidResult.cookies`,并在合并阶段按
|
||||
显式值转发。对当前内置 `hydevice-prod-1.2.45-min.js` 的实际运行结果为:
|
||||
|
||||
- `__SDID__` 和 `__HDID__` 正常返回;
|
||||
- Cookie 桥只观察到脚本自身的 `udb_appid`/临时 `sdid`,没有生成 `guid`、QIMEI、
|
||||
`udb_guiddata`、`udb_deviceid` 或 `udb_anobiztoken`;
|
||||
- 因此这次改动不会改变 `COOKIE_INCOMPLETE` 判定,也不会用随机值补齐字段。
|
||||
|
||||
从线上下载的 `hydevice-prod-1.2.51-min.js` 在现有 Node 仿真环境中还依赖未实现的
|
||||
浏览器 API(首个错误为 `window[t(...)] is not a function`),没有将其替换进生产链。
|
||||
后续若升级仿真环境,必须先观察到真实 Cookie 写入,再纳入合并逻辑。
|
||||
|
||||
### 多抓包交叉核验
|
||||
|
||||
- `8.31/reqable完整抓包-1.har`:首次页面请求和 QR 请求均已携带全部 7 个字段;
|
||||
QR 成功响应只新增 `udb_cred`、`udb_uid`、`yyuid`、`udb_passport`、`username`、
|
||||
`udb_version`、`udb_biztoken`、`udb_origin`、`udb_status`。
|
||||
- `8.31/cahrles完整抓包-1.har`:第一个带网页设备态的请求早于 `df/collect` 和 QR
|
||||
成功响应;`df/collect` 只下发 `sdid`、`hdid`。
|
||||
- `7.5-短信登录-滑块.har`:`web/cookie/verify` 前后的请求也没有新增这 7 个字段。
|
||||
|
||||
因此当前日志中的缺失列表是协议事实,不是 Cookie 合并阶段丢失;下一步只有取得
|
||||
同一网页会话的真实持久化设备态,或找到服务端正式签发接口,才能继续补齐。
|
||||
|
||||
### 真实浏览器实测(2026-09-01,未登录)
|
||||
|
||||
用真实 Chrome(有头)通过 CDP 抓包复核,两轮验证:
|
||||
`core/huya/browser_harvest.py`(round1 全新 profile / round2 同 profile 重启),
|
||||
原始数据与报告在 `evidence/browser_cookie_harvest/`。
|
||||
|
||||
页面序列:`www.huya.com` 门户 → 直播间 `30596253`,全程不登录。
|
||||
|
||||
| 字段 | 首次出现位置 | 产生机制 | round2 重启后首请求携带 |
|
||||
| --- | --- | --- | --- |
|
||||
| `__yamid_new` | `liveapi.huya.com/ip/getIpLocation` (XHR) | 门户页脚本写 `document.cookie` | ✅ |
|
||||
| `game_did` | 同上 | 脚本写入 | ✅ |
|
||||
| `udb_guiddata` | `udblgn.huya.com/web/middle/2.4/...` (Document) | UDB SDK 脚本写入,与 `guid` 值不同 | ✅ |
|
||||
| `guid` | `udbres.huya.com/js/HyUDBWebSDK-Exchange-2.4.js` | UDB SDK 脚本写入 | ✅ |
|
||||
| `_qimei_uuid42` | 同上 | QIMEI SDK 脚本写入 | ✅ |
|
||||
| `udb_deviceid` | `udblgn.huya.com/web/middle/2.4/...` 与 `/web/anonymousLogin` 的 `Set-Cookie` | 服务端匿名接口下发,`w_`+19 位数字,10 年 | ✅ |
|
||||
| `udb_anobiztoken` | `udblog.huya.com/web/log/report` | UDB SDK 脚本写入 | ✅ |
|
||||
|
||||
结论:
|
||||
|
||||
1. 7 个必填字段全部由**浏览器页面运行态**产生(6 个脚本写 `document.cookie`,1 个
|
||||
`udb_deviceid` 由匿名服务端接口下发),与登录协议无关 —— `COOKIE_INCOMPLETE`
|
||||
判定是协议事实。
|
||||
2. round2 重启后门户首 Document 请求直接携带 16 个 Cookie(7 个必填字段全部在内),
|
||||
证明这些字段是**浏览器持久态**,跨会话稳定复用。
|
||||
3. 首次访问时序:门户首请求无 Cookie → 脚本热启动后 `__yamid_new`/`game_did`
|
||||
先出现 → UDB SDK 加载后 `guid`/`udb_guiddata`/QIMEI 出现 → `anonymousLogin`
|
||||
匿名 XHR 携全套字段并把 `udb_deviceid` 种回 10 年。
|
||||
4. 全会话服务端 `Set-Cookie` 仅 `PHPSESSID`、`udb_deviceid`、`sdid`、`HMACCOUNT`
|
||||
(百度统计)、mediav `v` 五类,其余全部来自脚本。
|
||||
|
||||
新突破口:`udblgn.huya.com/web/anonymousLogin` 与 `/web/middle/2.4/...` 是公开
|
||||
匿名接口(纯 HTTP 可调),服务端正式签发 `udb_deviceid` —— 这是“服务端正式签发
|
||||
接口”的第一个实例;`guid`/QIMEI/`udb_anobiztoken` 仍无服务端下发,只能靠复刻
|
||||
页面 SDK(`HyUDBWebSDK-Exchange-2.4.js` 等)的脚本语义。
|
||||
|
||||
### 匿名流程解密(2026-09-01,纯 HTTP 复放验证)
|
||||
|
||||
浏览器实际匿名链(未登录):
|
||||
|
||||
1. `GET udblgn.huya.com/web/middle/2.4/<8位随机>/https/<32hex>` →
|
||||
`Set-Cookie: udb_deviceid=w_<19位>; Max-Age=315360000; Domain=huya.com`,
|
||||
响应是一个加载 `HyUDBWebSDK-Exchange-2.4.js` 并 `init()` 的引导 HTML。
|
||||
2. SDK 随后发 `POST udblgn.huya.com/web/anonymousLogin`
|
||||
(`Content-Type: application/json`,`Referer` 必须为 middle URL,
|
||||
否则返回 `error!`):
|
||||
- 请求体 `{"uri":"10013","version":"2.4","context":"WB-<hex32>-<hex32>-<hex32>",
|
||||
"appId":5002,"sdid":"csid_<hex32>","lcid":2052,"byPass":3,
|
||||
"requestId":<8位>,"authId":"","data":{"domainList":""}}`
|
||||
- 响应 `returnCode:0`:`data.uid`(12 位匿名 uid)、`data.biztoken`(344 位
|
||||
URL-safe)、`Set-Cookie: udb_anouid`(7 天)+ `udb_deviceid`。
|
||||
3. 页面 SDK 把 `biztoken` 写入 `udb_anobiztoken`、`uid` 写入 `udb_anouid`,
|
||||
与 middle URL 的 32hex(= `udb_guiddata`)、`guid`、QIMEI、`__yamid_new`、
|
||||
`game_did` 一起构成网页设备态。
|
||||
|
||||
对照验证(真实浏览器最终 Cookie 与响应值一一对应):
|
||||
|
||||
| Cookie | 值形态 | 来源 |
|
||||
| --- | --- | --- |
|
||||
| `udb_deviceid` | `w_`+19 位数字 | middle/anonymousLogin `Set-Cookie`(10 年) |
|
||||
| `udb_anobiztoken` | 344 位,`AQA…` 开头 | anonymousLogin 响应 `data.biztoken` |
|
||||
| `udb_anouid` | 12 位数字 | anonymousLogin 响应 `data.uid`/`Set-Cookie`(7 天) |
|
||||
| `udb_guiddata` | 32hex | middle URL 的第三段参数(客户端自选) |
|
||||
| `guid` | 32hex | 页面 SDK 脚本写入 `document.cookie` |
|
||||
| `_qimei_uuid42` | 42 位 | QIMEI SDK 脚本写入 |
|
||||
| `__yamid_new` | 32hex | 门户业务脚本写入 |
|
||||
| `game_did` | 35 位 | 门户业务脚本写入 |
|
||||
|
||||
复放实验结论(2026-09-01):
|
||||
|
||||
- middle 的 8 位随机数与 32hex 均**不校验**:任意随机值 GET 即签发新的
|
||||
`udb_deviceid`(`returnCode 200`);
|
||||
- anonymousLogin 的 `context`/`csid`/`requestId` 均为全新随机值时,无任何
|
||||
Cookie 直接 POST 也返回 `returnCode:0` + 新 `biztoken` 与 `Set-Cookie`;
|
||||
- 因此 **`udb_deviceid`、`udb_anobiztoken`、`udb_anouid`、`udb_guiddata`
|
||||
四个字段可由纯协议生成**,不再依赖真实浏览器脚本;
|
||||
- 剩余 `guid`、`_qimei_uuid42`、`__yamid_new`、`game_did` 没有服务端签发,
|
||||
服务端是否校验其“真实性/关联性”未知 —— `200` 只代表接口接受,不代表
|
||||
风控认可。必须用协议生成的设备态跑通线上 活动 WSS/商城/支付 全链路
|
||||
才能作为最终结论,不能只凭长度测试(与本文开头结论一致)。
|
||||
@@ -0,0 +1,73 @@
|
||||
# 虎牙精英宝典联调指南
|
||||
|
||||
更新时间:2026-09-01
|
||||
|
||||
## 本次联调结论
|
||||
|
||||
当前联调阶段只验收 App 登录后的网页 Cookie 完整性,精英宝典任务统一使用 WSS:
|
||||
|
||||
| 阶段 | 结果 | 证据 |
|
||||
| --- | --- | --- |
|
||||
| WUP 登录 | 通过 | `cred=114B`,返回真实 UID |
|
||||
| hydevice 指纹 | 通过 | 返回 `sdid`、`hdid` |
|
||||
| QR 绑定 | 通过 | `getQrId`、`bindQrLoginUser`、`tryQrLogin(stage=2)` |
|
||||
| 网页 Cookie | 待重新登录验证 | 必须包含 9.1 网页设备态字段 |
|
||||
| 精英宝典任务 | 待验证 | 仅允许 WSS,会话失败即任务失败 |
|
||||
|
||||
旧登录结果中的 Cookie 缺少网页设备态,已不再作为成功登录或任务链路使用。
|
||||
|
||||
纯 App/HTTP 登录不会由 `web/cookie/verify` 下发网页设备 Cookie。浏览器只用于
|
||||
人工调试和抓取来源,未集成到脚本;当前 App 登录缺少这些字段时直接返回
|
||||
`COOKIE_INCOMPLETE`,不会伪造或自动启动浏览器。
|
||||
|
||||
## 推荐联调顺序
|
||||
|
||||
1. 在 `http://localhost:5174/` 登录一个测试账号,确认日志出现 `App协议登录成功`。
|
||||
2. 配置精英宝典 `SID`。9.1 抓包对应活动 SID 为 `2203`;以页面“精英宝典设置”中的当前值为准。
|
||||
3. 按下面顺序在精英宝典工作台执行任务:
|
||||
- `query_game_name`:验证角色绑定状态。
|
||||
- `query_points`:验证积分响应和数据库写回。
|
||||
- `query_exchange_records`:验证记录解码和页面展示。
|
||||
- `refresh_goods`:拉取商品快照。
|
||||
- 选一个明确可兑换、价格低于余额的商品,再执行 `exchange_goods`。
|
||||
4. 兑换成功后再次执行 `query_points` 和 `query_exchange_records`,核对余额扣减与新增记录。
|
||||
5. 充值/开通链路单独联调:先 `refresh_recharge_goods`,再只生成支付码 `create_recharge_order`。支付确认不要和查询任务混在同一批次。
|
||||
|
||||
## 如何判定传输通道
|
||||
|
||||
执行器只检查字段是否存在,不记录字段值:
|
||||
|
||||
```text
|
||||
必要字段:udb_cred、udb_biztoken、udb_uid、yyuid、sdid、guid、udb_guiddata、udb_deviceid、game_did、_qimei_uuid42、udb_anobiztoken、__yamid_new
|
||||
```
|
||||
|
||||
- 字段齐全:尝试活动或商城 WSS。
|
||||
- 缺少字段:App 登录返回 `COOKIE_INCOMPLETE`,任务日志列出缺失字段并停止,不创建 HTTP 客户端,例如:
|
||||
|
||||
```text
|
||||
Cookie 缺少网页设备态(guid, udb_guiddata),已停止活动 HTTP 回退
|
||||
```
|
||||
|
||||
- WSS 握手、`wsLaunch`、`getConfig` 或业务 RPC 任一步失败:任务直接失败并记录具体阶段,便于修复 Cookie 或协议参数。
|
||||
|
||||
## WSS 专项验收
|
||||
|
||||
只有从同一网页会话取得真实 `guid` 后,才做 WSS 专项联调。验收顺序为:
|
||||
|
||||
1. 活动 WSS 初始化:`wsLaunch -> AUTH -> mobileui.getConfig -> CONFIRM`。
|
||||
2. 活动 RPC:`checkUserBindGameAccount -> getUserScore -> getUserPrizeRecords`。
|
||||
3. 商城 WSS 初始化:`wsLaunch -> getConfig -> register -> CONFIRM`。
|
||||
4. 商城只读 RPC:`getGoodsInfoV5 -> listPayChannelV5 -> checkHyProtocolV5`。
|
||||
5. 最后才验证 `createOrder -> payOrderSubmit -> orderDetail`。
|
||||
|
||||
网页设备字段必须来自同一网页/设备会话;`_merge_web_cookie` 不会根据长度、正则或 UID 生成 `guid`、QIMEI、`udb_anobiztoken` 等字段。
|
||||
|
||||
## 本次测试命令
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest -q tests/test_huya_app_login.py tests/test_huya_runner_transport.py
|
||||
.venv/bin/python -m ruff check core/huya web/backend/services/huya_runner_core.py tests/test_huya_runner_transport.py
|
||||
git diff --check
|
||||
```
|
||||
|
||||
预期:测试通过、Ruff 无错误、差异检查无输出。真实联调以 `logs/app-YYYY-MM-DD.log` 中每个 RPC 的状态码和任务最终状态为准。
|
||||
Reference in New Issue
Block a user