Files
live-hub-py/docs/HUYA_WEB_FIELD_TRACE.md
T

10 KiB
Raw Blame History

虎牙网页设备字段溯源(历史证据,2026-09-01 浏览器实测)

当前实现状态以 HUYA_COOKIE路线状态.md 为准;本文保留原始浏览器证据与算法推导。

一、来源判定方法(有头 Chrome + 打点探针)

  • Page.addScriptToEvaluateOnNewDocument 拦截 Document.prototype.cookie setter, 记录每条写入的 值/时间/调用栈(栈中含脚本 URL + 行号)。
  • Network.responseReceivedExtraInfo 捕获服务端 Set-Cookie 头。
  • 全新 profile,导航 www.huya.com + 直播间页,静置后导出。

二、决定性结论:哪些字段服务端下发,哪些浏览器生成

字段 实测
PHPSESSID l.web.huya.com / l.huya.com
udb_deviceid udblgn.huya.com(w_ + 19 位数字,匿名设备)
sdid udblgn.huya.com(0UnHUgv0_qmfD4KA... 设备指纹)
v 页面版本
HMACCOUNT 百度统计

✅ 浏览器脚本 document.cookie 写入(铁证:调用栈)

字段 生成脚本 关键位置
guid a.msstatic.com/huya/main3/npm..pnpm-d84ecac2_850fe68b.js Object.setCookie 写入 5 次;位置 12-16 固定 966a(跨会话一致)
_qimei_fingerprint staticfile.qq.com/qimei/qimeisdk-web/0.6.3/index.js 写入 1 次
udb_passdata a.msstatic.com/huya/main3/common/headerfunction_d8b3ad0c.js writeCookie 固定值 3
__yasmid / __yamid_tt1 页面脚本 同值 17 位小数
字段 证据 说明
__yamid_new 88 条写入中无此 key,但最终 cookie 有值 CBCC 前缀 + 递增种子(见下)
game_did 同上 30-35 位字母数字
_qimei_uuid42 同上 14 位时间前缀 + 8 位 CRC32 替换值 + 20 位随机半字节 + 4 位 CRC32 尾段

三、设备字段结构规律(真实样本)

guid = 0a + 10hex + 956a/966a + 16hex —— 有固定段,非纯随机

0a7dfa5fc9ee966aab02a2eaa3ea1de1   ← live 窗口
0a7dfaa2d7af966a2101f370f470ce50   ← round2 fresh
0a7de265986f956a24015b505c504506   ← reqable 8.31
0a89d5f6614b956aa6020bbc0cbc18fe   ← cahrles 8.31

位置 12-16 恒为 956a / 966a(产品段?),8.31 为 956a、我们机器为 966a —— 可能与 SDK/页面版本有关。

__yamid_new = CBCC/CBCB + 种子6hex + 计数4hex + 0001 + 随机12hex

CBCC 6479D9 B000 0001 52EC19001D2017B2   ← live 窗口
CBCC 551C11 E000 0001 783D8F5530901BCC   ← round2 fresh
CBCC 06ECB5 0000 0001 B63016D6162BC990   ← reqable 8.31
  • 前缀 CBCC/CBCB 恒定(产品标识)
  • 种子段递增且与 _yasids(rootsid)同源共享时钟: yamid=CBCC6479D9B0... ↔ rootsid=CBCC647A8A20...(同会话,种子差 0xB1≈177)
  • 0001 固定,尾部大段随机

_qimei_uuid42 = 14 位时间前缀 + CRC32 替换段 + 20 位随机半字节 + CRC32 尾段

17b0f060d14100037f4444444444444444444437df   ← 固定时间/随机 .25
17b0f060d1410053db888888888888888888883ff6   ← 固定时间/随机 .5
17b0f060d141003174ffffffffffffffffffffd85d   ← 固定时间/随机 .999

时间前缀的编码为:年份末两位十六进制(补两位)+ 月份十六进制 + 日/时/分/秒各两位十六进制 + cookieEnabled 标志(100/000)。模板 prefix + "----" + random_nibbles + "----" 的标准 CRC32 小写结果会拆成前后四位填回两个占位;random_nibbles 由 20 次独立的 floor(Math.random() * 16) 依次拼接,因此每位合法范围为 0 到 f。

_qimei_fingerprint = x64hash128(feature_string, 31) 32hex

qimeisdk-web 0.6.3 源码已定位:

  • 画 "qimei" 文本(Times New Roman 11pt)+ #f60 背景 + 透明度矩形
  • 三圆叠加(#f2f/#2ff/#ff2 40 半径)+ 环形 #f9c evenodd
  • Canvas id/geometry 是 SDK 特征对象的独立字段;Cookie 指纹实际使用浏览器能力特征串 → SDK 内置 x64hash128(seed=31)→ 32hex。

四、溯源结论与建议

字段 来源 风控关联性 建议
guid 活动 wsLaunch 响应 sGuid;页面 SDK 负责持久化 高(设备与会话绑定) 优先复用当前 wsLaunch 会话值;956a/966a/976a 等中间段随服务端返回变化,不能固化为版本常量
__yamid_new 浏览器脚本(CBCC 前缀+种子) 高(与 rootsid 同源) room_normal.js generate32 已复刻;精确 rootsid 共享计数仍待确认
_qimei_uuid42 QIMEI SDK 0.6.3 VM 生成器 e=6064 中 Python 已按时间编码、CRC32 和随机半字节逐项复刻
_qimei_fingerprint QIMEI SDK 0.6.3 x64hash128(feature_string, 31) 中 Python 已复刻,输入字段见路线状态文档
_qimei_h38 当前页面不写入同名 Cookie;SDK 内部 h38 = UUID42 低 积分链路仅检查键存在,缺失时复用 _qimei_uuid42
game_did 浏览器脚本 中 长度需对齐(35 变体含 -)
统计字段(SoundValue 等) 页面脚本 低 随机 OK

当时的下一步记录(部分已完成,保留作历史):

  1. _qimei_fingerprint 已改为浏览器能力特征串的 x64hash128,见路线状态文档。
  2. guid 已统一使用 wsLaunch 会话值;活动 AUTH、getConfig 与后续业务 Cookie 均复用同一返回值。
  3. __yamid_new 已定位 room_normal.js generate32 并完成 Python 复刻。

六、核心字段生成算法逆向结果(2026-09-01 晚,浏览器实测 + 源码定位)

✅ __yamid_new = room_normal.js generate32(UUID-v1 风格,已 Python 复刻验证)

// 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;
}
  • 浏览器实测 epoch(GMT+8): -12216643543000;Python 复刻 12hex 时间戳段与浏览器逐位一致
  • CBCC 前缀是时间戳自然结果(1582-epoch 当前值的高位),非固定魔数;随生成时刻缓慢递增
  • _yasids(rootsid)同源同刻生成(浏览器实测 yamid=CBCC653E4B... / rootsid=CBCC647A8A... 共享种子时钟)

✅ game_did = vplayerUI.js 35 位随机(已复刻)

// 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 = QIMEI SDK VM 生成器(已 Python 复刻)

清理 localStorage/cookie 后对 QIMEI SDK 0.6.3 做固定时间、固定随机值追踪,定位到 VM 生成器 e=6064。它先拼出:

14 位时间前缀 + "----" + 20 次独立抽样组成的随机半字节串 + "----"

随后计算模板 ASCII 字节的标准 CRC32,将 8 位小写结果的前 4 位和后 4 位分别填入占位,最后调用 toLocaleLowerCase()。例如固定每次 random=0.25 时,模板是 17b0f060d14100----44444444444444444444----,替换值是 037f37df,结果为 17b0f060d14100037f4444444444444444444437df;递增序列探针则得到随机段 0123456789abcdef0123,证明 SDK 会独立抽样 20 次。navigator.cookieEnabled 只影响时间前缀末三位(100/000)。

  • 由 qimeisdk-web 0.6.3 内部 VM 生成(_qimei_ssid/_qimei_last_get_id_time_<16hex> 同源);字段本身为 42 位小写十六进制。
  • Python 实现按 SDK 时间、CRC32 和随机上界复刻,固定向量覆盖 0、.25、.5、.75、.999。
  • SDK 对该内部值使用 h38 属性名,但它的长度是 42;_qimei_h38 只是活动积分链路的 Cookie 兼容键,不存在另一套 38 位 SDK 生成算法。

✅ _qimei_fingerprint = x64hash128(feature_string,31),已完成 Python 复刻

  • Cookie 输入由屏幕分辨率、浏览器能力、WebGL 和时区字段按 ### 拼接。
  • Python 实现位于 core/huya/web_cookie_fields.py,测试向量与 SDK 输出一致。
  • 没有实时浏览器能力对象时,生成器使用稳定的默认 Web 特征档案。
  • __yamid_new → _yamid_new_generate32()(UUID-v1 时间戳复刻)
  • game_did → _game_did_generate()(35 位 63 字符集)
  • _yasids → 与 __yamid_new 同源同刻生成
  • _qimei_uuid42 → _qimei_uuid42_generate()(14 位时间前缀 + CRC32 替换段 + 20 位随机半字节 + CRC32 尾段)
  • guid(无会话值回退)→ 0a + 10hex + 966a + 16hex 结构

七、逆向生成器端到端验证(2026-09-01 晚,账号 18397482576 重新登录后)

新版生成器(web_cookie_fields.py commit 8fef04d)产出字段:

  • guid=0a43b01a366c966a7fe26a281e1d3c88(0a 前缀 + 966a 固定段)
  • __yamid_new=CBCC65D6C5200001F176114BCF181E25(UUIDv1 时间戳)
  • _qimei_uuid42=42 位小写十六进制(SDK VM 时间/CRC32 结构)
  • game_did=35 位
  • _yasids 与 yamid 同源(CBCC65D6C5 前缀一致)

webActUI 全链路 status=200:

  1. checkUserBindGameAccount → 和平精英/痛仰之吻
  2. getUserScore → gain=5580 available=280
  3. getActPrizeList → 29 商品
  4. scoreExchangePrize → status=200 真实兑换成功 (荣耀勋章礼包(小)20 分,订单 sOrderId_2203_12855_1199667330433_...)
  5. getUserPrizeRecords → 4 条

↑ 之前 getConfig 超时是旧 cookie 过期(id58 已清空),非生成器问题。