TikTok X-Dynosaur5.3.1 AI补环境

TikTok Web 端签名AI逆向分析复盘:JSVMP webmssdk 补环境生成 X-Dynosaur/X-Gnarly/X-Bogus

【使用的GLM-5.2 ,只给了提示词: tiktok网页版(https://www.tiktok.com/@ainbag9)接口中的 /api/repost/item_list/ 接口有签名参数 X-Dynosaur 、X-Bogus、X-Gnarly。 已知加密参数生成在webmssdk.js,已保存在本地文件夹。 你结合 js-reverse mcp 和 已知信息,分析加密参数如何生成的。】 【等AI分析后,选择补环境,本文内容由AI整理,未做算法还原】(耗时约20分钟)

file

前言

TikTok 网页版所有列表类接口(/api/repost/item_list//api/post/item_list//api/comment/list/)强制携带三组加密签名参数:

  • X-Dynosaur:URL Query 附加超长签名,浏览器实测长度 388~444 字符,固定以M开头
  • X-Gnarly:配套校验签名,标准长度 332 字符,同样首字符为M
  • X-Bogus:固定常量1,由 SDK 内部计数器硬编码输出

签名逻辑全部封装在混淆度极高的webmssdk.js(版本 1.0.0.388,JSVMP 字节码虚拟机)中,参数名、哈希常量、加密逻辑全部藏在 VMP 常量池,无法直接全局检索明文。 本文完整记录浏览器动态调试定位签名链路 → Node.js 补环境本地复现生成签名 → 线上接口兼容性踩坑排查全流程,不涉及纯算法底层还原,聚焦可落地的签名调用方案。

免责声明:本文仅用于前端安全逆向学习研究,禁止用于大规模爬虫、批量采集 TikTok 平台数据,遵守平台用户协议与各国网络法规。

一、环境与基础信息梳理

1. 目标文件与版本对照

文件 本地版本 线上版本 核心特征
webmssdk.js 1.0.0.388 / sdkVersion=5.3.1 1.0.0.388 / sdkVersion=5.3.1 JSVMP 全混淆,内置 SHA256/MD5/FNV/ChaCha 变体
webmssdk_ex.js 配套扩展包 扩展签名证明逻辑

2. 触发接口前置条件

  1. 用户作品列表接口:访问用户主页 https://www.tiktok.com/@ainbag9 自动触发 /api/repost/item_list//api/post/item_list/
  2. 评论列表接口:访问视频详情页 https://www.tiktok.com/@ainbag9/video/7661673152652561680,点击评论区才会发起 /api/comment/list/
  3. 签名白名单:接口路径写入window._mssdk._enablePathListRegex,未在白名单的请求不会附加三组签名

3. 签名通用特征(浏览器抓包验证)

  1. X-Bogus 恒等于1,无动态计算逻辑;
  2. X-Dynosaur/X-Gnarly 加密外壳同源,均使用自定义 Base64 表,魔数 0x4B 映射首字符M
  3. 签名内容绑定完整原始 URL,不同接口、不同 Query 参数生成的签名不互通。

二、浏览器动态调试:定位签名完整调用链路

2.1 抓包断点捕获带签名请求

调试工具:Chrome DevTools XHR/Fetch 断点 + js-reverse MCP 栈追踪

  1. 配置 XHR 断点匹配/api/*/item_list//api/comment/list/
  2. 导航至目标页面刷新,滚动页面加载分页数据,捕获完整带签名 URL;
  3. 截取请求调用栈,梳理执行层级:
业务侧user-prefetch.js fetchData
    ↓ 原生fetch调用
secsdk_runtime_bundler 拦截原始fetch
    ↓ 转入webmssdk外层VMP入口(45279行)
VMP主循环L()(235212行)字节码分发
    ↓ opcode handler表b[]分派
内层VMP执行签名TLV字段组装、哈希、ChaCha加密、自定义Base64编码
    ↓ 拼装带X-Dynosaur/X-Gnarly/X-Bogus的完整URL
webmssdk fetch wrapper发起真实网络请求

2.2 JSVMP 核心结构静态分析

webmssdk.js是完整自研字节码虚拟机,所有加密逻辑不暴露顶层函数:

  1. VMP 主循环函数 L ():读取 16 位 opcode,分派至对应 handler 执行;
  2. opcode 处理表 b []:内置寄存器读写、循环、哈希、异或、Base64 等微操作;
  3. 常量池 I []:存储加密所需全部常量,包括:
    • SHA256 初始哈希值、K 常量数组
    • FNV-1a 哈希固定值0x811C9DC5、乘数65599
    • MD5、SHA1 哈希初始向量
    • 混淆 Base64 字符串(X-Dynosaur/X-Gnarly参数名加密存储于此)
  4. 关键哈希 handler(137045 行):FNV-1a 实现,用于 TLV 字段基础哈希计算。

2.3 全局签名状态与对外入口

  1. 全局状态容器t.u[860].v:存储msTokenttwid、签名时间戳fetchSignTimebogusIndex计数器;
  2. 挂载全局对象window.byted_acrawler,暴露方法:frontierSign、init、report
  3. 关键结论:
    • frontierSign仅用于 WebSocket 签名,HTTP 接口签名不对外暴露独立同步方法
    • HTTP 签名依靠 SDK 自动劫持fetch/XHR拦截器,匹配白名单后内部 VMP 自动生成参数;
    • 必须执行byted_acrawler.init()注入 aid、白名单、区域配置,拦截器才会生效。

2.4 签名生成分层逻辑(逆向交叉验证)

  1. 输入层:原始 URL、UA、浏览器指纹、ttwid/msToken、时间戳、随机数;
  2. TLV 字段组装层:25 段标签长度值结构(X-Dynosaur)/16 段(X-Gnarly),嵌入 FNV、SHA256 哈希;
  3. ChaCha 变体加密层:48 字节随机密钥生成动态轮次流加密;
  4. 编码输出层:魔数头 0x4B 拼接密文,自定义 Base64 编码输出最终签名串。

三、Node.js 补环境本地复现签名(可运行落地方案)

3.1 补环境核心难点

webmssdk.js强依赖浏览器宿主 API,直接在 Node.js 运行会大量报错,需最小化模拟浏览器全局对象:

表格

缺失对象 模拟实现要点 作用
window/self 全局顶层沙箱对象 SDK UMD 挂载byted_acrawler/_mssdk
document 模拟 cookie、referrer、DOM 基础方法 读取 ttwid、msToken 等身份指纹
navigator userAgent、hardwareConcurrency、deviceMemory 设备指纹向量参与签名计算
Headers/Request/fetch/XMLHttpRequest 完整 mock 拦截器 SDK 劫持原生请求实现自动签名
performance/crypto now ()、getRandomValues、SHA256 摘要 时间戳、加密随机数生成
location/screen 页面地址、屏幕分辨率 环境指纹数据

3.2 完整执行流程

  1. 搭建 Node.js vm 沙箱,注入全部模拟浏览器环境;
  2. 读取本地webmssdk.js源码,在隔离上下文执行加载;
  3. 调用byted_acrawler.init()注入 TikTok Web 官方 aid 配置:

byted_acrawler.init({
  aid: 1988,
  dfp: false,
  boe: false,
  intercept: true, // 开启fetch自动拦截签名
  enablePathList: ['/api/repost/item_list/', '/api/post/item_list/', '/api/comment/list/'],
  region: 'sg-tiktok',
  mode: 516,
  custom: ['ttwid']
})

  1. 在沙箱内调用目标接口原始 URL 发起 mock fetch;
  2. 捕获拦截器处理后的完整 URL,提取X-DynosaurX-GnarlyX-Bogus参数。

3.3 落地结果:成功本地生成签名

X-Bogus = 1
X-Dynosaur(本地): 384字符,M开头
X-Gnarly(本地): 324字符,M开头

核心验证:将本地生成的带签名 URL 粘贴至浏览器控制台发起请求,/api/post/item_list//api/comment/list/均可正常返回完整业务数据,服务端校验通过。

四、两大核心问题深度排查与解决方案

问题 1:本地生成签名长度比浏览器短 8 字符,是否失效?

  1. 现象汇总
    • 浏览器:X-Dynosaur 388~444 字符,X-Gnarly 固定 332 字符;
    • Node 补环境:X-Dynosaur 固定 384 字符,X-Gnarly 固定 324 字符,统一少 8 位;
  2. 根因分析
    • 差值来源于浏览器完整设备指纹字段缺失:硬件并发数、内存、WebGL 画布哈希、时区等环境向量;
    • 本地仅最小化补环境,省略部分非强制指纹字段,TLV 字段长度缩短;
  3. 关键结论:长度差异不影响服务端校验 TikTok 签名校验逻辑只验证加密外壳完整性、哈希校验和,不会强匹配固定长度;实测长短签名均可正常请求接口,仅极端风控场景可能触发限流。

问题 2:为 A 接口生成的签名,无法用于 B 接口

  1. 现象:用/api/post/item_list/URL 生成的签名,请求/api/comment/list/直接返回空 / 403;
  2. 底层原理 X-Dynosaur/X-Gnarly 的 TLV 载荷中嵌入完整原始 URL 的哈希值,签名与请求路径、Query 参数强绑定;
  3. 正确使用规范 每一条独立接口请求,必须使用当前请求完整原始 URL重新调用 fetch 生成专属签名,不可复用其他接口签名。

五、后续迭代路线(分阶段)

阶段 1:当前成熟方案(优先落地)

维持 Node.js vm+webmssdk 补环境方案,优势:

  1. 跟随 TikTok SDK 版本自动适配,无需手动更新加密算法;
  2. 兼容全部白名单 API,仅需更新enablePathList路径;
  3. 开发成本低,无需逆向拆解 VMP 字节码与底层 ChaCha、TLV 逻辑。

阶段 2:中期优化(按需推进)

完善浏览器指纹模拟,补齐缺失设备字段,消除 8 字符长度差,让本地签名与浏览器长度完全对齐:

  1. 补全 canvas 画布哈希、webgl 指纹、时区、硬件信息;
  2. 对比浏览器 VMP 运行时寄存器 dump,补全缺失全局状态t.u[860]字段。

阶段 3:长期纯算法还原(高成本,非必需)

  1. 对 VMP 主循环插桩,逐条记录 opcode 输入输出,映射字节码序列与加密步骤;
  2. 分别剥离 FNV 哈希、TLV 序列化、48 字节随机密钥生成、ChaCha 变体加密、自定义 Base64 五大模块;
  3. 完全脱离webmssdk.js,实现原生 JS/Python 纯算生成签名,消除 vm 沙箱依赖。

六、逆向实战经验总结

  1. JSVMP 逆向优先动态调试抓栈,静态读混淆单行 JS 效率极低,参数名、常量全部加密存储;
  2. 不要迷信固定签名长度,平台校验核心是加密校验和,长度仅由环境指纹字段多少决定;
  3. HTTP 签名无独立导出函数,核心入口是 fetch/XHR 拦截器,必须通过发起请求自动触发生成;
  4. 签名强绑定请求 URL,多接口采集场景需动态传入对应路径实时生成,无法全局复用;
  5. 补环境遵循「最小可用原则」,只模拟 SDK 运行时报错的 API,无需完整复刻浏览器;
  6. 签名生成、网络请求分层解耦,规避 IP、TLS 指纹风控带来的调试干扰。

附录:关键调试工具与定位命令

  1. XHR 条件断点:匹配/api/*/item_list捕获带签名请求;
  2. VMP 主循环断点:webmssdk.js:235212 L () 函数,追踪字节码执行;
  3. 环境校验:浏览器控制台打印_mssdk._enablePathListRegex查看签名白名单;
  4. 签名状态查看:暂停时读取 frame 内t.u[860].v,查看 msToken、ttwid 等指纹缓存;
  5. 沙箱调试:vm 上下文注入日志,拦截 mock fetch 输出拼装完成的签名 URL。
点赞

发表回复