web_cookie_fields 用逆向算法替换随机:__yamid_new=UUIDv1时间戳复刻、game_did=35位63字符集、_yasids同源、_qimei_uuid42=1a前缀、guid回退加固定段

This commit is contained in:
yml2213
2026-09-01 23:48:00 +08:00
parent 8b24732c81
commit 8fef04d0af
2 changed files with 122 additions and 10 deletions
+77 -9
View File
@@ -17,8 +17,8 @@ status=200(和平精英已绑定)、getUserScore 返回 status=200(积分
from __future__ import annotations from __future__ import annotations
import random
import secrets import secrets
import string
import time import time
# 统计/会话字段生成规则;9.1 抓包值为样例,格式保持对齐 # 统计/会话字段生成规则;9.1 抓包值为样例,格式保持对齐
@@ -30,6 +30,71 @@ _FILL_MAP: tuple[tuple[str, str], ...] = (
("udb_accdata", "undefined"), ("udb_accdata", "undefined"),
) )
# 浏览器实测逆向(2026-09-01,见 docs/HUYA_WEB_FIELD_TRACE.md):
# - __yamid_new: room_normal.js generate32UUID-v1 风格,1582-epoch 本地时区时间戳
# + v1 版本位 + 时钟序列 + 48 位随机 node)。浏览器 epoch = new Date(1582,10,15,
# 0,0,0,0).getTime() 实测 -12216643543000GMT+8)。CBCC 前缀是时间戳自然结果。
# - game_did: vplayerUI.js 35 位随机,字符集 64 去掉末尾 '_'63 选 1)。
# - _qimei_uuid42: QIMEI SDK 生成,cookie 值首字节固定 '1a'(本地存储 44hex 截断)。
_SDK_EPOCH_1582 = -12216643543000 # 浏览器实测 JS Date(1582,10,15) 本地时区 ms
_BASE16 = "0123456789ABCDEF"
_GAME_DID_CHARS = (
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
)
def _return_base16(a: int) -> str:
"""JS returnBase(a,16):正整数转大写十六进制;a<=0 返回空串。"""
if a <= 0:
return ""
out = ""
while a > 0:
out = _BASE16[a % 16] + out
a //= 16
return out
def _generate_bits(a: int, b: int, c: int) -> str:
"""JS generateBits(a,b,c):取 a 的 16 进制串 [floor(b/4)..floor(c/4)] 字符,越界补0。"""
d = _return_base16(a)
out = ""
for g in range(b // 4, c // 4 + 1):
out += d[g] if g < len(d) and d[g] != "" else "0"
return out
def _yamid_new_generate32(rng=None) -> str:
"""逐字复刻 room_normal.js generate32__yamid_new 生成器)。"""
import random
r = rng if rng is not None else random.Random()
now_ms = int(time.time() * 1000)
c = now_ms - _SDK_EPOCH_1582
return "".join((
_generate_bits(c, 0, 31),
_generate_bits(c, 32, 47),
_generate_bits(c, 48, 59) + "1",
_generate_bits(r.randrange(4096), 0, 7),
_generate_bits(r.randrange(4096), 0, 7),
_generate_bits(r.randrange(8192), 0, 7)
+ _generate_bits(r.randrange(8192), 8, 15)
+ _generate_bits(r.randrange(8192), 0, 7)
+ _generate_bits(r.randrange(8192), 8, 15)
+ _generate_bits(r.randrange(8192), 0, 15),
))
def _game_did_generate(length: int = 35) -> str:
"""逐字复刻 vplayerUI.js game_did35 位随机,字符集 64 去掉末尾 '_'"""
import random
out = ""
for _ in range(length):
idx = int(random.random() * (len(_GAME_DID_CHARS) - 1)) # 63 → 排除 '_'
out += _GAME_DID_CHARS[idx]
return out
def _rand_float() -> str: def _rand_float() -> str:
import random import random
@@ -78,15 +143,17 @@ def fill_web_cookie_fields(
else: else:
add("guid", str(guid)) add("guid", str(guid))
elif "guid" not in pairs: elif "guid" not in pairs:
add("guid", secrets.token_hex(16)) # guid 结构: '0a' + 10hex + '956a'/'966a' + 16hex(浏览器实测固定段,见 trace 文档)
add("_qimei_uuid42", secrets.token_hex(21)) # 42 hex add("guid", "0a" + secrets.token_hex(5) + "966a" + secrets.token_hex(8))
add("__yamid_new", secrets.token_hex(16).upper()) # 32 hex 大写 # _qimei_uuid42: QIMEI SDK 产物,cookie 首字节 '1a' + UUID 布局 32hex + 8hex 附加
alnum = string.ascii_letters + string.digits add("_qimei_uuid42", "1a" + secrets.token_hex(20))
import random # __yamid_new: room_normal.js generate32 精确复刻(UUID-v1 时间戳,含 CBCC 前缀规律)
add("__yamid_new", _yamid_new_generate32())
add("game_did", "".join(random.choices(alnum, k=30))) # 30字母数字 add("game_did", _game_did_generate()) # 35随机(真实长度)
# qimei 皮肤字段 # qimei 皮肤字段
# _qimei_fingerprint: QIMEI SDK SpookyHashV2(canvas指纹,31) 32hex;设备稳定,
# 无真实 canvas 时可随机(格式 32hex 对齐),真实算法见 trace 文档
add("_qimei_fingerprint", secrets.token_hex(16)) add("_qimei_fingerprint", secrets.token_hex(16))
add("_qimei_h38", secrets.token_hex(19)) add("_qimei_h38", secrets.token_hex(19))
@@ -99,7 +166,8 @@ def fill_web_cookie_fields(
add("h_unt", str(int(time.time()))) add("h_unt", str(int(time.time())))
if uid: if uid:
add("__yaoldyyuid", str(uid)) add("__yaoldyyuid", str(uid))
add("_yasids", "__rootsid%3D" + secrets.token_hex(16).upper()) # _yasids 与 __yamid_new 同源(共享时间戳生成器,浏览器实测前缀一致)
add("_yasids", "__rootsid%3D" + _yamid_new_generate32())
add("Hm_lvt_51700b6c722f5bb4cf39906a596ea41f", str(int(time.time()))) add("Hm_lvt_51700b6c722f5bb4cf39906a596ea41f", str(int(time.time())))
add("Hm_lpvt_51700b6c722f5bb4cf39906a596ea41f", str(int(time.time()))) add("Hm_lpvt_51700b6c722f5bb4cf39906a596ea41f", str(int(time.time())))
add("HMACCOUNT", secrets.token_hex(8).upper()) add("HMACCOUNT", secrets.token_hex(8).upper())
+45 -1
View File
@@ -85,4 +85,48 @@ qimeisdk-web 0.6.3 源码已定位:
1. `_qimei_fingerprint` 直接用 SpookyHashV2 实现(canvas 指纹用标准值替代) 1. `_qimei_fingerprint` 直接用 SpookyHashV2 实现(canvas 指纹用标准值替代)
2. `guid` 采样更多样本确认固定段后按 `0a` + 结构仿造 2. `guid` 采样更多样本确认固定段后按 `0a` + 结构仿造
3. `__yamid_new` 需找到生成它的具体脚本(非 document.cookie 写入,可能在 3. `__yamid_new` 需找到生成它的具体脚本(非 document.cookie 写入,可能在
ubtk/埋点脚本里)做种子同步 ubtk/埋点脚本里)做种子同步
## 六、核心字段生成算法逆向结果(2026-09-01 晚,浏览器实测 + 源码定位)
### ✅ __yamid_new = room_normal.js generate32UUID-v1 风格,已 Python 复刻验证)
```js
// a.msstatic.com/huya/main3/app/room_normal_55c7cc4f.js
function u(e){var t="__yamid_new",n=getCookieVal(t);if(n)return n;n=e||d.generate32();...writeCookie(t,n,...)}
d.generate32(){
c = new Date().getTime() - new Date(1582,10,15,0,0,0,0).getTime(); // 本地时区 epoch
return generateBits(c,0,31) // time_low 8hex
+ generateBits(c,32,47) // time_mid 4hex
+ generateBits(c,48,59)+"1" // time_hi+版本位
+ generateBits(rand(4095),0,7) + generateBits(rand(4095),0,7)
+ generateBits(rand(8191),0,7)+... 48位随机node;
}
```
- 浏览器实测 epochGMT+8: `-12216643543000`Python 复刻 12hex 时间戳段与浏览器**逐位一致**
- **`CBCC` 前缀是时间戳自然结果**(1582-epoch 当前值的高位),非固定魔数;随生成时刻缓慢递增
- `_yasids`(rootsid)同源同刻生成(浏览器实测 yamid=`CBCC653E4B...` / rootsid=`CBCC647A8A...` 共享种子时钟)
### ✅ game_did = vplayerUI.js 35 位随机(已复刻)
```js
// a.msstatic.com/huya/h5player/room/2608311423/vplayerUI.js
B = (t=35) => { const e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
for(i="";i.length<t;) i += e.charAt(parseInt(Math.random()*(e.length-1))); return i }
```
- **长度 35**(旧随机 30 位是错的);字符集 64 去掉末尾 `_`63 选 1
- cookie 名 `game_did`,无 cookie 时生成并写入
### ⚠️ _qimei_uuid42 结构已定,算法待深挖
- cookie 值:首字节 `1a` 固定 + 40hexlocalStorage 是 44hexcookie 为截断子串)
- 由 qimeisdk-web 0.6.3 内部 VM 生成(`_qimei_ssid`/`_qimei_last_get_id_time_<16hex>` 同源)
- 当前实现对齐结构(1a 前缀 + 42hex 随机化),精确算法待继续
### ⚠️ _qimei_fingerprint = SpookyHashV2(canvas指纹,31),待 canvas 复现
- SDK 0.6.3 内置 x64hash128 与 canvas 指纹逻辑(文本+三圆+环形)
- 设备稳定(同 profile 跨会话不变);当前实现随机 32hex 格式对齐
- 复现需在真实环境执行 canvasheadless 下 canvas 指纹固定,可抽取一次固化为常量)
### 已修正生成器(core/huya/web_cookie_fields.py, commit 待定)
- `__yamid_new``_yamid_new_generate32()`UUID-v1 时间戳复刻)
- `game_did``_game_did_generate()`35 位 63 字符集)
- `_yasids` → 与 `__yamid_new` 同源同刻生成
- `_qimei_uuid42``1a` 前缀 + 42hex
- `guid`(无会话值回退)→ `0a` + 10hex + `966a` + 16hex 结构