文章总结: 本文详细记录了利用GLM-5.2辅助分析和语义级插桩技术,逆向还原TikTokWeb端X-Gnarly签名算法(332位)的完整过程。算法分为TLV序列化、ChaCha变种加密、W字节拼装和自定义Base64编码四个阶段。通过动态插桩突破JSVMP混淆,成功复现了纯JavaScript算法,并验证了其与浏览器端生成结果的一致性。该方法为分析类似混淆保护提供了高效路径。 综合评分: 92 文章分类: 逆向分析,安全工具,实战经验,WEB安全
基于 GLM-5.2 的 X-Gnarly (332位) 纯算还原分析
红皮西瓜 红皮西瓜
看雪学苑
2026年7月28日 17:59 上海
在小说阅读器读本章
去阅读
目标接口:/api/post/item_list 签名参数:X-Gnarly(332 字符) SDK 版本:webmssdk 5.2.1 方法与工具:fast-spider skills 工作流 + GLM-5.2 辅助分析 + 语义级插桩
一、背景
在 TikTok Web 端中,访问 /api/post/item_list 等核心接口通常需要两个关键的签名参数:
-
X-Bogus长度为 28 字符,一般非强制校验。
-
X-Gnarly长度为 332 字符,必须有效,服务端会对其进行多重深度校验。
X-Gnarly 参数由 webmssdk.js(byted_acrawler SDK)动态计算生成。由于该 SDK 采用了 JSVMP(JavaScript VM Protection)混淆技术,其实际执行算法被编译为约 44KB 的字节码,源码中仅包含解释器内核,静态分析难以直观还原其明文逻辑。
本篇报告记录了如何利用 fast-spider skills 工作流,在 GLM-5.2 的辅助推理下,从 trace 日志中逐步还原出 X-Gnarly 的完整生成链路,并完成纯 JavaScript 算法复现的过程。
#
二、方法:fast-spider skills + 语义插桩
2.1 基础分析工作流
初始化环境 (init) → 捕获 SDK (capture) → 制定运行策略 (runbook) → 采集 trace → 逻辑分析 (analyze) → 制定方案 (plan) → 补全算法 (fill) → 对拍校验 (verify)
2.2 针对 JSVMP 混淆的增强策略
传统的补环境方案(如 FakeEnv 或 Node vm 模拟)在面对该级别的混淆时,往往因为大量未知的浏览器特征检测而难以推进。因此,本方案采用语义级插桩思路:
-
动态插桩脚本
(基于工作流中的
patch_sdk.js):通过源码级特征匹配,定位webmssdk.js中的核心执行位置,在每个.call()、.apply()以及关键二元/三元运算处注入__tk_log记录器,自动输出函数对象、调用参数、返回值及运算轨迹。 -
Playwright 动态替换
使用拦截代理,将页面请求的线上 CDN 脚本无缝替换为本地生成的插桩版 SDK。
通过这一方法,单次流程可稳定产出约 28 万行语义日志,为后续算法推导提供了坚实的数据支撑。
#
三、算法全链路设计
X-Gnarly 的生成可以划分为以下 4 个阶段:
(query, UA, ts)
│
▼ 阶段 1:TLV 序列化
200 字节明文数据
│
▼ 阶段 2:ChaCha 变种流加密
200 字节密文数据
│
▼ 阶段 3:W 字节拼装 (嵌入 48 字节 Key)
248 字节拼装数据 (W)
│
▼ 阶段 4:自定义 Base64 编码
332 字符 X-Gnarly 签名
#
四、阶段 1:TLV 序列化(→ 200 字节明文)
4.1 构建 17 元素原始数组
算法首先在内存中收集并构建一个包含 17 个元素的混合数组:
const arr = [
0, // tag0: num0 (全局数字校验和,待后续计算)
73, // tag1: 固定常量
14, // tag2: 固定常量
qsHash, // tag3: md5(queryString) 结果
md5(''), // tag4: md5(空字符串) = 'd41d8cd98f00b204e9800998ecf8427e'
md5(UA), // tag5: md5(User-Agent) 结果
ts, // tag6: 秒级时间戳(10位整数)
A, // tag7: 固定常量
B, // tag8: 固定常量
'5.2.1', // tag9: SDK 版本号
'2.0.0.493', // tag10: webmssdk 内部版本号
1, // tag11: 固定常量
T12, // tag12: 固定常量
T13, // tag13: 固定常量
M1, // tag14: 固定常量
M2, // tag15: 固定常量
num15, // tag16: 内部校验和
];
4.2 校验和算法
在进行洗牌前,需要计算数组中的两个校验和:
4.3 LCG 洗牌机制 (Fisher-Yates Shuffle)
基于线性同余生成器(LCG)生成的伪随机数对上述数组进行倒序洗牌:
let seed = B; // 初始种子来源于 tag8 对应的固定常量
const multiplier = 1664525;
const increment = 1013904223;
const modulus = Math.pow(2, 32);
for (let i = arr.length - 1; i > 0; i--) {
seed = (multiplier * seed + increment) % modulus;
const targetIndex = Math.floor((seed / modulus) * (i + 1));
// 交换位置
const temp = arr[i];
arr[i] = arr[targetIndex];
arr[targetIndex] = temp;
}
4.4 TLV 序列化打包
洗牌完成后的数组将被转化为 TLV(Type-Length-Value)二进制流:
- 结构布局:
[Tag (1B)] [Length_Hi (1B)] [Length_Lo (1B)] [Value (NB)] - 编码规则:对于整数类型,若 ≤0xFFFF\le 0xFFFF≤0xFFFF 采用 2 字节大端表示,若 >0xFFFF> 0xFFFF>0xFFFF 则采用 4 字节大端表示;字符串统一采用 UTF-8 编码。
- 头部标识:整个二进制流的第 1 字节记录了字段总数(即固定的
0x11/ 17 个字段)。
最终输出:200 字节明文字节数组。
五、阶段 2:ChaCha 变种加密(→ 200 字节密文)
5.1 矩阵状态初始化 (16 Words / 64 Bytes)
#
加密状态矩阵的 16 个 32位 Word 布局如下:
[0..3] = OT = [1196819126, 600974999, 3863347763, 1451689750] // 4 个固定状态字
[4..15] = key48 的 12 个 Little-Endian Word // 48 字节固定 Key
5.2 动态加密轮数计算
轮数由初始 key48 决定。由于本方案中 key48 采用提取自真机环境的静态常量,其推导轮数亦维持不变:
5.3 核心 Quarter Round (QR) 实现
QR 结构与标准 ChaCha 保持一致,保留了经典移位常数:
function quarterRound(x, a, b,c, d){
x[a]=(x[a]+ x[b])|0; x[d]= rotl32(x[d]^ x[a],16);
x[c]=(x[c]+ x[d])|0; x[b]= rotl32(x[b]^ x[c],12);
x[a]=(x[a]+ x[b])|0; x[d]= rotl32(x[d]^ x[a],8);
x[c]=(x[c]+ x[d])|0; x[b]= rotl32(x[b]^ x[c],7);
}
5.4 魔改对角线变换 (Diagonal Round)
这是 webmssdk 对经典 ChaCha 算法做出的核心修改。对比标准对角线变换索引,本版本进行了如下替换:
该修改打破了标准 ChaCha 算法原有的对称扩散特性,需在重构时严格遵循变种索引。
5.5 块流处理与 Feed-Forward
分块对 200 字节明文执行流加密(每块 64 字节):
- 拷贝当前
state矩阵副本。 - 依次进行列变换与变种对角线变换,交替执行
rounds轮。 - 加密完毕后,执行 Feed-Forward 累加操作以生成最终密钥流块: keystream[j]=(transformed[j]+initial[j])∣0\text{keystream}[j] = (\text{transformed}[j] + \text{initial}[j]) \mid 0keystream[j]=(transformed[j]+initial[j])∣0
- 初始状态矩阵中的
state[12](counter) 递增,用于下一块计算。 - 将得到的密钥流与明文块按字节进行异或。
最终输出:200 字节密文数组。
#
六、阶段 3:W 字节拼装(→ 248 字节)
6.1 分割点计算
#
分割索引依据密文及 key48 数据之和求模动态计算:
6.2 拼接融合
将 48 字节的 key48 插入到密文字节流的分割点处,构建合成数组 W:
最终输出:248 字节的数据块。
#
七、阶段 4:自定义 Base64 编码(→ 332 字符)
7.1 添加协议前缀
在数据块头部附加固定标识 0x4B(字符 ‘K’):
7.2 变种 Base64 转换
使用指定的自定义字母表进行标准的 3 字节至 4 字符转换(转换过程中无填充字符 =):
- 自定义字母表(64 字符):
u09tbS3UvgDEe6r-ZVMXzLpsAohTn7mdINQlW412GqBjfYiyk8JORCF5/xKHwacP=
-
编码转换逻辑:
最终输出:332 字符长度的有效 X-Gnarly 签名。
#
八、常量与动态变量对照
为保持长期调用的稳定性,算法实现中明确区分了动态变量与静态常量:
| 参数类型 | 字段明细 | 处置策略 |
| — | — | — |
| 动态输入 | queryString (URL 查询参数), userAgent (浏览器 UA), ts (当前时间戳) | 随每次请求实时生成,用于计算数组中的 Tag 字段 |
| 固定参数 | 状态矩阵 OT, 加密密钥 key48, rounds, 变量 A, B, M1, M2, T12, T13 | 采用从真机 trace 日志中分析固定的静态数值,降低运行时的环境依赖风险 |
#
九、对拍验证流程
9.1 校验路径
- Playwright 加载目标页面,利用代理机制将
webmssdk.js重定向至本地插桩版。 - 触发 API 请求,捕获签名过程中的 trace 日志。
- 从控制台捕获的最终
X-Gnarly反解出 249 字节,恢复出相应的密文与key48,直至还原明文 TLV。 - 运行纯 JS 复现程序,对比复现产物与浏览器端生成的结果是否完全对齐。
9.2 对拍结果
| 测试批次 | key48 摘要 | 加密轮数 | 变量 B | splitIndex | 复现结果一致性 |
| — | — | — | — | — | — |
| trace4 | e0671fe6... | 17 | 354945180 | 38 | ✅ 一致 |
| trace6 | 098bf137... | 11 | 1316161044 | 194 | ✅ 一致 |
#
十、复现实现与调用
10.1 本地调用示例 (Node.js)
#
const { generateXGnarly } = require('./registry/tiktok/xgnarly521.js');
const query = 'WebIdLastTime...msToken..';
const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...';
const timestamp = Math.floor(Date.now() / 1000);
const xGnarly = generateXGnarly(query, ua, timestamp);
console.log('Generated X-Gnarly:', xGnarly); // 输出 332 字符的签名
10.2 发包验证测试 (Python)
from curl_cffi import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
# ... 其他必要请求头
}
url = f'https://www.tiktok.com/api/post/item_list/?{query}&X-Bogus=&X-Gnarly={x_gnarly}'
resp = requests.get(url, headers=headers, impersonate='chrome120')
print("Status:", resp.status_code)
# 数据成功解包说明签名通过深度校验
#
十一、逆向方法论总结
11.1 突破 JSVMP 的静态阻碍
面对包含数百个虚拟机操作码分支及复杂分发循环的 JSVMP 保护,完全依赖静态反汇编分析极易陷入迷局。此时,借助 GLM-5.2 的代码分析与逻辑整合能力,配合同步插桩方案,是定位真实解密链路的高效途径。
11.2 语义日志的解构价值
通过自动向执行单元添加插桩,能够避开虚拟机内部繁琐的操作码转换,直接将寄存器数值与函数交互以自然语言级别记录下来。这不仅极大地缩短了逆向周期,而且保证了底层逻辑还原的准确性。
看雪ID:红皮西瓜
https://bbs.kanxue.com/user-home-1046416.htm
*本文为看雪论坛优秀文章,由 红皮西瓜 原创,转载请注明来自看雪社区
第十届安全开发者峰会【议题征集】-欢迎投稿
往期推荐
ISCC 2026 题目 [house of apple 2] 利用方法
Android 内核无痕 Hook 框架设计思路和避坑经验
从零手写 ARM64 自定义 Linker
从2026CCB决赛堆题CreditMarket学习Glibc2.39后ptmalloc的机制更新
2019-SUCTF-SUDriver 学习笔记
球分享
球点赞
球在看
点击阅读原文查看更多
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:看雪学苑 红皮西瓜 红皮西瓜《基于 GLM-5.2 的 X-Gnarly (332位) 纯算还原分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










![深信服[X-STAR]2027届顶尖人才校招引进提前批正式启动](/images/random/titlepic/5.jpg)
评论