前段时间有粉丝联系我,叫我做一个某音刷播放量的脚本,我抓包分析了一下某音的接口,发现使用的也是抖系的a_bogus参数加密。
对于这个参数,我们之前在《JS逆向实战:某茄小说a_bogus参数逆向实战与bdms.js算法还原》和《JS逆向实战系列一:某头条a_bogus参数逆向分析与算法还原》中都有接触过,也算是不陌生了。但是某音是抖系的核心产品,相比之前提到的两个产品,难度要略微复杂一些。
本文目录
- 抓包分析:锁定视频推荐接口
- 启动器追踪:定位bdms.js调用链
- 循环断点困境:从普通断点到日志断点
- 精准定位:条件断点锁定a_bogus生成
- 堆栈回溯:分析函数调用顺序
- 代码扣取:改造D函数导出加密入口
- 环境补全:适配Node.js运行环境
- 本地验证:生成192位a_bogus参数
- 实战应用:Python调用实现刷播放量
- 常见问题与解决方案
1. 抓包分析:锁定视频推荐接口
既然都是一个公司的产品,根据我们以往的逆向经验,加密算法都是放在bdms.js文件里面的。那我们就不使用XHR断点和全局搜索了,直接在某音视频播放页面打开开发者控制台进行抓包,随便找一个带有a_bogus参数的请求接口。
这里就以视频推荐接口/aweme/v1/web/aweme/related/做演示,如下图:

2. 启动器追踪:定位bdms.js调用链
然后点击/aweme/v1/web/aweme/related/这个请求接口,查看这个接口的启动器(调用堆栈)。从这里可以观察到JS的一个调用顺序,其中就包含了在bdms.js中调用d、X、n这几个函数,如下图:

3. 循环断点困境:从普通断点到日志断点
那我们就按顺序来,先进入函数d,可以看到这里是一个标准压栈调用apply。打下断点后,发现断点一直卡在这里,一直在断,是一个循环调用,如下图:
else {
var m = n.apply(d, e);
v[++p] = m
}

那就改换成日志断点,输出一下这些值,看看有没有生成的a_bogus值,如下图:

4. 精准定位:条件断点锁定a_bogus生成
切换到控制台,可以看到一直在输出日志,有点卡。清空控制台,随便在界面划拉一下,然后再控制台搜索a_bogus,可以看到搜索出来1条。这个是在e参数里面搜索到的,这是一个数组:
[
"a_bogus",
"xX4jkwSiQoWcFdKb8OpZe3NUMqyANPuyvpiKbHoPer/oPqzONmNpuxCWbxzUhlcp7mpTiHKHhxGAannb8zX0ZexkLmkfSTtS10VAV0sL0qq4TMvQLHD8ewuFKw0rUcGql/54iIW6MUJo6fVAkHQm/B-99KLCQb8BPpORk/YcY9BhZzLAEZnaPBSDNXPY0fOR"
]

既然找到了生成的值,那我们就来下一个精准的条件断点。断下来以后从调用堆栈看一下d函数是何时压栈、传递了哪些值。通过观察调用堆栈,调用顺序是Cn → n → X → d,在i.apply(this, t)这段代码进行的压栈调用。
function Cn(i, u) {
var a = In();
return function() {
for (var n, r, o, t = [], e = 0; e < arguments.length; e++)
t[e] = arguments[e];
return this._start = $(),
this._data = null == t ? void 0 : t[0],
a(this._url) || (n = u([this._method, this._url, this._start, this]),
o = n,
s(r = this, "onreadystatechange", function(e) {
return function() {
for (var n = [], t = 0; t < arguments.length; t++)
n[t] = arguments[t];
return 4 === this.readyState && o(r),
e && e.apply(this, n)
}
})()),
i.apply(this, t)
}
}

5. 堆栈回溯:分析函数调用顺序
现在在控制台打印一下i,可以看到i输出的是D函数里面的ƒ (){return X(e,this,arguments,r)},而this和t是a_bogus生成所需要的参数。

6. 代码扣取:改造D函数导出加密入口
现在跟进D函数,我们需要把这个n函数导出去。但是因为这是一个压栈的,我们不知道是何时调用X生成a_bogus的,因此我们需要查看一下e,来判断是何时执行的a_bogus生成。
function D(t, r) {
var e = z[t];
Y.has(t) && V.delete(Y.get(t));
var n = function() {
return X(e, this, arguments, r)
};
return Y.set(t, n),
V.set(n, [e, r]),
n
}
在调用堆栈点到n函数,在控制台打印一下e参数。在生成a_bogus值时,X函数e指向的是一个数组。因此我们在扣代码时,需要在这个D函数写个判断,如果e函数等于下面的值,就可以把n函数赋值给我们的全局变量:

思路有了,开始扣代码。先把bdms.js函数复制到本地,再改一下D函数,当e参数的数组等于上面的值时,就把n赋值给window.CreateAbogus,这是我们自己创建的一个全局变量。代码如下:
function D(t, r) {
var e = z[t];
Y.has(t) && V.delete(Y.get(t));
var n = function() {
return X(e, this, arguments, r)
};
if(Array.isArray(e))
{
if(JSON.stringify(e) == '[[34,54,0,3,34,30,214,41,212,34,30,214,30,70,54,0,4,74,0,4,30,218,54,0,5,74,0,5,30,72,54,0,6,33,74,2,33,74,0,6,0,1,54,0,7,74,0,7,41,5,74,0,6,53,11,60,161,74,0,6,60,216,30,178,59,2,54,0,8,74,0,8,30,162,18,30,219,73,165,0,1,29,17,5,74,2,3,30,150,41,18,74,0,8,30,162,18,30,163,73,165,74,2,3,30,150,0,2,26,74,0,8,30,162,18,30,219,73,220,0,1,29,41,45,33,74,3,14,0,0,26,33,74,2,37,74,0,8,30,162,18,30,9,0,0,74,0,2,0,2,54,0,9,74,0,8,30,162,18,30,163,73,220,74,0,9,0,2,26,74,0,7,29,41,10,74,0,5,74,0,8,30,178,20,72,34,30,214,18,30,51,63,108,0,1,26,33,74,2,36,74,0,8,30,215,0,1,41,7,33,74,2,5,0,0,26,34,73,214,25,26,74,1,4,18,30,126,34,74,0,2,39,1,0,2,26,33,76],1,true,[]]')
window.CreateAbogus = n
}
return Y.set(t, n),
V.set(n, [e, r]),
n
}
7. 环境补全:适配Node.js运行环境
代码扣好了,开始补充基础环境。创建一个env.js,把下面的代码放进去保存,代码如下:
window = global
window.requestAnimationFrame = function () { }
window.XMLHttpRequest = function () { }
window.outerWidth = 1920
window.outerHeight = 1032
window.innerWidth = 1920
window.innerHeight = 487
window.onwheelx = { "_Ax": "0X21" }
window.EventSource = function(){return X(e,this,arguments,r)}
document = {
all: {},
addEventListener: function () { },
createEvent: function () { },
}
documentElement = {}
setInterval = function () { }
setTimeout = function () { }
navigator =
{
storage: {},
platform: 'Win32',
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
}
navigator.permissions = {
query: function() {
return Promise.resolve({ state: 'granted' })
}
}
location =
{
"ancestorOrigins": {},
"href": "https://www.douyin.com/video/761890243432432",
"origin": "https://www.douyin.com",
"protocol": "https:",
"host": "www.douyin.com",
"hostname": "www.douyin.com",
"port": "",
"pathname": "/video/7618902948386573594",
"search": "",
"hash": ""
}
screen = {
availHeight: 1032,
availWidth: 1920,
colorDepth: 32,
height: 1080,
pixelDepth: 32,
width: 1920,
}
8. 本地验证:生成192位a_bogus参数
全部弄好以后,创建一个test.js用来组装调用。通过window.CreateAbogus.apply来调用我们赋值的函数n(也就是X),下面的xhr就是我们上面Cn函数里面输出的this,另一个参数t传递空就行了。
require('./env');
require('./bdms');
function get_a_bogus(args) {
xhr = new XMLHttpRequest()
xhr.bdmsInvokeList = [
{
"args": [
"POST",
args,
true
],
"func": function(){}
},
{
"args": [
"Accept",
"application/json, text/plain, */*"
]
},
{
"args": [
"uifid",
"ed3eadd74fe8fd7fe8cc39b2f8425a87324d41d3f6a0cfdc014da4c26c6540515265656e5f7c1bd3f0268b0b0b0607c36830303e84ec90e1e569a1bc6965a19863f80cf3a0ad8e004e3b315fc8eada61d752f8833365fc1f52311829ea2a09971e66d8ec06aa51da144ccafc44c9ea14444ec1b32748606195d5fddfc5b3e1709cdca693c2500bf9ae798d2b3ea17902a0944485451c64a8d38ded336a38e8fdd84ba24a96340c4bd0d2fe83c39f24e2147cc1a7b3cbdfa401e2c7eb3b7a7691"
]
}
];
return window.CreateAbogus.apply(xhr, {"0": null});
}
url = 'https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id=7614467988905934585&request_source=600&origin_type=video_page&update_version_code=170400&pc_client_type=1&pc_libra_divert=Windows&support_h265=1&support_dash=1&cpu_core_num=8&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=146.0.0.0&browser_online=true&engine_name=Blink&engine_version=146.0.0.0&os_name=Windows&os_version=10&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=150&webid=7622670628450141730&uifid=ed3eadd74fe8fd7fe8cc39b2f8425a87324d41d3f6a0cfdc014da4c26c6540515265656e5f7c1bd3f0268b0b0b0607c36830303e84ec90e1e569a1bc6965a19863f80cf3a0ad8e004e3b315fc8eada61d752f8833365fc1f52311829ea2a09971e66d8ec06aa51da144ccafc44c9ea14444ec1b32748606195d5fddfc5b3e1709cdca693c2500bf9ae798d2b3ea17902a0944485451c64a8d38ded336a38e8fdd84ba24a96340c4bd0d2fe83c39f24e2147cc1a7b3cbdfa401e2c7eb3b7a7691&verifyFp=verify_mnbsxfzo_qHLOgkFq_FSkQ_4L97_8AbL_Od6pRv7x0CV9&fp=verify_mnbsxfzo_qHLOgkFq_FSkQ_4L97_8AbL_Od6pRv7x0CV9&msToken=ub0_h-rFSuJTA_PMaW9-r-L2EpNwnIZhbrdTUDc56iJB2gve8cUnkx2HZ5c_row3_6PLc0dzgySaqJrkrgJE--2-AZ33aKu02PPnQozktjZVqpNJ0M5fSsxJFdvEr_Xt62ht2BiNbmJqzCGTWwtEM9QQprRJdXj1THWkoWETGmACGbP2yXEE_6M%3D'
a_bogus = get_a_bogus(url)
console.log('值:', a_bogus, '长度', a_bogus.length)
直接在控制台运行node test.js,可以看到能正常生成a_bogus值,而a_bogus值的长度是192位,和网页生成的长度一致。如果运行时报错误异常,去bdms.js搜索throw l,把这句注释掉就行了。

9. 实战应用:Python调用实现刷播放量
最后写个Python代码,直接调用test.js文件里面的get_a_bogus函数生成a_bogus,组装请求去访问一下视频播放接口,可以看到下面的代码能够正常请求,视频播放量成功+1。
import execjs
import requests
# 加载JS文件
with open('bdms.js', 'r', encoding='utf-8') as f:
js_code = f.read()
with open('env.js', 'r', encoding='utf-8') as f:
env_code = f.read()
# 编译JS上下文
ctx = execjs.compile(env_code + js_code)
def get_a_bogus(url):
return ctx.call('get_a_bogus', url)
# 构造请求
video_url = "https://www.douyin.com/aweme/v1/web/aweme/detail/"
params = {
"device_platform": "webapp",
"aid": "6383",
"aweme_id": "7614467988905934585",
}
# 生成加密参数
full_url = video_url + "?" + "&".join([f"{k}={v}" for k, v in params.items()])
a_bogus = get_a_bogus(full_url)
params["a_bogus"] = a_bogus
# 发送请求
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": "https://www.douyin.com/",
}
response = requests.get(video_url, params=params, headers=headers)
if response.status_code == 200:
print("请求成功,播放量+1")
print(response.json())
else:
print(f"请求失败:{response.status_code}")

10. 常见问题与解决方案
- Q:配置数组匹配不上怎么办?
A:在控制台打印出当前命中的e值,复制完整数组替换判断条件。不同版本的某音配置数组可能不同。 - Q:运行时报
throw l错误如何解决?
A:在bdms.js中搜索throw l,直接注释掉该行。这是某音的反调试代码,注释后不影响加密结果。 - Q:生成的a_bogus长度不对?
A:检查环境补全是否完整,特别是navigator.userAgent和screen对象的值。长度异常通常是环境检测导致的。 - Q:Python调用execjs报编码错误?
A:确保JS文件以utf-8编码保存,读取时加上encoding='utf-8'参数。 - Q:刷播放量有限制吗?
A:同一个IP、同一个视频,短时间内频繁请求可能被限流。建议合理控制请求频率,配合代理池使用。
11. 技术总结
通过本次逆向,我们完成了某音a_bogus参数的完整分析。与之前两个产品相比,某音的防护强度明显更高,主要体现在参数长度更长(192位)、加密位置更难定位、存在反调试代码等方面。
核心技巧总结:
- 日志断点解决循环调用无法调试的问题
- 条件断点精准过滤目标参数
- 配置数组匹配定位加密入口
- 最小化扣代码,只导出需要的函数
12. 获取完整代码
关注公众号:孤狼网络科技,回复:某音Python代码,获取完整代码课件。
温馨提示:本文仅作技术研究用途,请勿用于非法刷量等违规行为。逆向技术的意义在于学习防护机制,而非破坏平台规则。

从知乎来的,写的不错,自己定位到的了D函数也打日志断点看到了有生成a_bogus,但是不知道怎么把这个函数暴露给外部使用,你知乎上的文章让我眼前一亮思路豁然开朗。知乎上的文章思路写的很清晰,比这篇写的还好些,是重写了一份吗
一样的格式不一样