bestV8 使用教程:纯净 V8 环境跑 JS,做补环境调试
本文最后更新于:2026年8月14日 下午
本文为整理性内容,基于官方开源仓库的公开资料编写
- 官方 Release 仓库:https://github.com/BestToYou/bestV8_release(作者公众号:二进制科学)
- Python 封装库 PyBestV8:https://github.com/wangluozhe/pybestV8(作者 wangluozhe)
bestV8 是一个开源免费、可以直接跑 JS 的逆向工具,本质是把纯净的 V8 引擎编译成跨平台动态库,供外部(如 Python 通过 ctypes)调用来执行 JavaScript。本文中的代码示例均来自上述两个官方仓库,可直接验证。文末附免责声明,请务必阅读。
为什么要用 bestV8,而不是直接用 Node
做 JS 逆向(尤其是补环境)时,很多人第一反应是拿 Node 跑目标脚本。但 Node 不是一个”干净”的执行环境:
- Node 里有
process、global、__dirname、__filename、require等一堆 Node 特有对象,而真实浏览器里没有这些。 - 有些反爬 JS 会专门检测这些 Node 特征,一旦发现
process之类的东西就判定为”非浏览器环境”,直接返回假值或报错。 - Node 内置的某些属性(比如新版本内置的
navigator)你想删都删不干净,反而干扰补环境。
而 bestV8 提供的是一个纯净的 V8 环境:里面只有 ECMAScript 标准对象(Date、Array、JSON……),没有任何 Node 特征,也没有任何浏览器 BOM/DOM 对象。这正是补环境想要的起点——一张白纸,浏览器里有什么、检测点用到什么,你就往里补什么,补得干净、可控。
一句话:bestV8 = 纯净 V8 执行器;补环境 = 你在这张白纸上手工画出一个”浏览器”。
bestV8 的组成
从官方 Release 仓库 BestToYou/bestV8_release 看,核心就几个文件:
1 | |
也就是说,bestV8 本体是编译好的 V8 动态库,对外暴露一个 runJs 导出函数。你用任何能调 C 动态库的语言都能驱动它,官方给的是 Python + ctypes 的方式。
用法一:官方原生调用(Python + ctypes)
这是官方 main.py 的原始代码,直接加载对应平台的动态库、调用 runJs:
1 | |
配套的 demo.js:
1 | |
运行逻辑很直白:
- 按操作系统选择对应的动态库加载(macOS 上 Intel / M 芯片要换对应的
.dylib,官方注释说”通常一个可以另一个不可以”,两个都试一下)。 - 声明
runJs的参数类型:第一个参数是要执行的 JS 源码(c_char_p),第二个参数是一个预分配的接收缓冲区(这里是 20000 字节的bytes)。 - 调用
runJs,JS 脚本里最后一个表达式的值会被写回到result缓冲区里。 result.rstrip(b"\x00")去掉尾部填充的空字节,得到真正的返回值。
⚠️ 注意:接收缓冲区大小(示例是 20000 字节)需要 ≥ 你的返回值长度,返回结果很大时要调大,否则会被截断。用封装库 PyBestV8 时对应的是 recv_size 参数。
用法二:PyBestV8(pip 封装,推荐)
每次手写 ctypes 太麻烦。wangluozhe/pybestV8 把上面这套动态库封装成了 pip 包 PyBestV8,动态库已经内置在包里,装完直接 import 即可。
安装
1 | |
1. eval:直接执行一段 JS
最简单的用法——把 JS 字符串丢进去,拿最后一个表达式的值:
1 | |
recv_size 就是接收缓冲区大小(对应原生调用里的那个 20000 字节缓冲区),返回值大就调大。
2. compile + call:编译后按函数名调用(支持上下文)
这是逆向里最常用的模式:先把整段 JS 编译成一个上下文 ctx,之后可以多次调用里面的函数,而且上下文里的全局变量状态会保留。
1 | |
留意最后一次 bcd 的结果:因为前面调过一次 abc(),全局的 a、b 已经被改过,所以第二次 bcd() 返回的是 [2, 4] 而不是 [1, 2]。这个”上下文状态保持”的特性,正是补环境跑加密函数时需要的——很多加密逻辑依赖初始化时设置的全局状态。
3. bestv8_import:把补好的环境拆成独立文件导入
补环境代码往往很长(window、document、navigator、location……一大堆)。PyBestV8 支持用 ctx.bestv8_import() 把这些环境代码放到单独的 .js 文件里导入,让主逻辑更清爽:
1 | |
配套的 import_test.js(一个手工补的 location 对象示例):
1 | |
这样你就能把”环境”和”业务逻辑”分离:import_test.js 专门放补好的 BOM/DOM 对象,主脚本只管调用加密函数。
结合补环境的典型工作流
bestV8 只是”纯净执行器”,真正过检测靠的是你补的环境。完整流程一般是:
抠出加密 JS:从目标站点定位到加密函数(比如
window.get_signature()之类),把相关代码抠下来。丢进 bestV8 直接跑:
bestv8.eval/compile跑一遍,看报什么错——通常是xxx is not defined(缺window、document、navigator等)。用 Proxy 自吐环境:给
window等对象套一层 Proxy,打印所有被get/set的属性,摸清脚本到底用了哪些浏览器 API。参考补环境通用写法:1
2
3
4
5
6
7
8
9
10window = new Proxy(window, {
get(target, property, receiver) {
console.log("get window", property, typeof target[property]);
return target[property];
},
set(target, property, value, receiver) {
console.log("set window", property, typeof value);
return Reflect.set(...arguments);
}
});针对性补:根据自吐日志,把缺的对象/属性一个个补上(放到
import_test.js里用bestv8_import导入)。补到不报错、结果和浏览器一致:反复迭代,直到 bestV8 里跑出的加密结果和真实浏览器里一致,补环境就成功了。
小结与选型
- bestV8:纯净 V8 执行器,无 Node 特征、无浏览器对象,是补环境的干净起点;跨平台动态库,Python 通过 ctypes 或 PyBestV8 驱动。
- PyBestV8:把 bestV8 封装成 pip 包,提供
eval/compile/ctx.call/bestv8_import,动态库已内置,开箱即用。 - 和 jsdom / happy-dom 的区别:后两者自带一套现成的 DOM 模拟(省事但特征明显、偏测试向),bestV8 则是完全干净、全靠自己补(更可控、更贴近真实浏览器、也更费手工)。实战里两条路线常按站点检测强度取舍。
免责声明
引用自官方仓库声明:该调试工具仅供技术人员进行内部测试和分析使用。请勿将其用于非法用途或以任何方式窃取第三方数据。使用该工具产生的所有风险均由用户自行承担,作者不对用户因使用该工具而造成的任何损失或损害承担任何责任。
本文仅作技术学习记录,请在合法合规、获得授权的前提下使用相关技术。
参考
- bestV8 官方 Release:https://github.com/BestToYou/bestV8_release
- PyBestV8:https://github.com/wangluozhe/pybestV8