前言
a_bogus 相比于之前的 x-bogus,这家伙隐藏得更深,而且裹挟着 JSVMP 混淆,让很多想要抓取数据的同学头疼不已。
注意 :本文仅用于技术学习与交流,请勿用于非法爬取商业数据,所有后果自负。

一、什么是 a_bogus?
在抖音的 WEB 端请求中,a_bogus 是一个必不可少的请求签名参数 。
它的作用类似于一张"动态身份证",服务端会根据请求的 URL、参数、环境指纹等计算出签名,只有签名正确,服务器才返回真实数据。如果你直接复制浏览器里的请求链接,或者单纯注释掉 a_bogus,返回的数据要么是空的,要么直接是 403。
二、逆向分析环境准备
正式开始之前,你需要具备以下基础(如果你已经是大佬,请跳过):
- 熟悉浏览器的开发者工具 (Network 面板、Sources 面板)。
- 掌握 XHR 断点 、条件断点 、日志断点 的用法。
- 了解基础的 JavaScript 语法。
- 知道如何利用 curlconverter 将请求转换为 Python 代码。
三、实战拆解:追踪 a_bogus 的生成逻辑
1. 选取目标接口
以抖音的关键词搜索接口 为例:
https://www.douyin.com/aweme/v1/web/general/search/single/
这个接口用于搜索视频内容,参数复杂且加密严密,是逆向分析的绝佳对象。
2. 接口参数分析
打开浏览器开发者工具,刷新页面,在 Network 面板中找到上述接口。
复制它的 cURL(bash),使用 curlconverter.com 转换为 Python 代码,方便我们测试。
观察请求参数,你会发现除了常规的 keyword、offset 等参数外,还有两个让人头疼的变量:msToken 和 a_bogus 。
分别注释掉这两个参数进行请求,发现缺了任何一个都会导致请求失败。本文我们重点攻克 a_bogus 。
3. 定位加密位置
3.1 全局搜索失效
直接在 Sources 面板搜索 a_bogus,发现代码被混淆,根本搜不到明文。这说明抖音做了代码混淆,我们需要换一种思路------XHR 断点 。
3.2 使用 XHR 断点
在 Sources 面板右侧,找到 XHR/fetch Breakpoints ,点击 +,输入接口路径的关键词 general/search/single。
设置好后刷新页面,页面会自动断在发送请求的代码处。 3.3 分析堆栈调用
断下来后,查看右侧的 Call Stack (调用堆栈)。
堆栈是从下往上执行的,我们需要找到 生成 a_bogus 的那个堆栈帧。
- 往下翻堆栈,找到 请求参数中还没有 a_bogus 的地方。
- 往上翻,找到 请求参数中已经有了 a_bogus 的地方。
这两个位置之间的某个函数,就是加密函数。关键点 :不要只找一个堆栈就停下,要逐个查看作用域(Scope)中的 params 或 data 对象,看是否有 a_bogus 字段。
4. 精细调试:日志断点与条件断点
最终,我们定位到了一个混淆很严重的 JS 文件(通常是 bdms.js 之类的),代码看起来像这样:
var result = s.apply(b, u);
这个地方很难一眼看出 s 是干什么的,但我们可以设置日志断点 。
在 s.apply(b, u) 这行右键,选择 Add logpoint ,输出内容设为:
返回值:{result}
刷新页面,看控制台打印的日志,你会发现有很多 true、false、数字以及字符串 。
观察那些字符串,你会惊奇地发现:
- 短的那一串看起来像是我们请求的 URL 参数拼接。
- 长的那一串长度大约在 160 位左右,看起来就是我们要找的 a_bogus。
为了精准定位,我们可以设置条件断点 :比如当返回值长度大于 150 时再断下来。
result.length > 150
最终,我们找到了生成 a_bogus 的核心函数,它在 e 函数内部。
四、补环境与代码封装
找到核心函数后,将 bdms.js 文件下载到本地。
1. 补环境
直接运行这段 JS 会报错,因为缺少浏览器环境(window, document, navigator 等)。我们需要模拟这些环境:
python
window = global;
delete global;
delete Buffer;
window.requestAnimationFrame = function(){};
XMLHttpRequest = function() {};
document = {}
navigator = {};
screen = {
availHeight: 816,
availLeft: 0,
availTop: 0,
availWidth: 1536,
colorDepth: 24,
height: 864
}
2. 导出加密函数
分析函数调用逻辑,发现 e 函数被调用时的参数 u 包含了很多环境信息(如 UA、屏幕宽高等)和请求参数。
我们可以将其封装成一个标准的 getSign 函数,只接收 params 字符串作为入参:
python
function getSign(params) {
u = [
0,
1,
14,
params,
"",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0"
]
var r = window.sign_z._v;
return (0,window.sign_z._u)(r[0], u, r[1], r[2], this)
}
这样,在 Python 中调用 execjs 执行这段代码,输入参数字符串,就能得到 a_bogus 了。
五、最终测试
# 伪代码示例`
`import execjs`
`import requests`
`with` `open('douyin.js',` `'r', encoding='utf-8')` `as f:`
` ctx = execjs.compile(f.read())`
`params_str =` `"device_platform=webapp&aid=6383&keyword=飞驰人生2..."`
`a_bogus = ctx.call('getSign', params_str)`
`print(a_bogus)` `# 输出类似:mXmZMdzgdDfiDDWX5VcLfY3q6WB3Y/R30CPYMD2f7dVr...
将生成的 a_bogus 塞回请求参数中,你会发现数据正常返回了。
写在最后
看到这你已经对抖音 WEB 端的加密参数 a_bogus 有了比较深的理解。
- 但是,抖音的反爬体系是动态更新的:msToken 和 a_bogus 的算法是否会变?
- 补环境的时候是否需要更多指纹?
- 如果被封 IP 了怎么办?
逆向是一场无止境的博弈。
想直接拿到稳定、高并发、低延迟的解决方案,可以查看我的主页,有封装好的 API 接口和完整的 SDK。
如果你有定制需求,也欢迎直接私信我。