iv8:Python V8 原生扩展,重新定义 JS 逆向的玩法
一、引言
在js逆向领域,"前端加密逆向"、"反爬虫绕过"、"补环境"是经久不衰的话题。无论是瑞数、Akamai、Cloudflare,还是自定义的 JS 签名校验,核心思路都是:在浏览器环境里跑 JS → 拿到动态参数 → 回放请求。
传统方案依赖 Node.js + jsdom + proxy 魔改,或者 Headless Chrome CDP。前者补环境补到崩溃,后者太重且容易被检测。
今天介绍一个更简洁的方案:iv8 —— 一个 Python 原生的 V8 运行时扩展。pip 一键安装,纯 Python 进程搞定。这篇文章将围绕 iv8 的用法、原理和实战展开。
项目地址https://github.com/HanZzzzz000/iv8
二、iv8 是什么
iv8 是一个 Python C/C++ 扩展,在 C++ 层嵌入了 V8 引擎(Chromium 同款 JS 引擎),并在 V8 之上实现了大量浏览器 API 的模拟。Community 版免费闭源,pip 直接安装。
pip install iv8
核心 API
| |
iv8.JSContext(environment={...}, time_mode="logical") | 创建 V8 隔离上下文,注入浏览器环境(navigator、screen、location 等) |
ctx.expose(data, name) | 向 JS 上下文注入任意数据,通过 window.__iv8__.data.<name> 访问 |
window.__iv8__.page.load(...) | 加载 HTML + JS 资源,解析 DOM,按顺序执行 <script> 标签,触发 DOMContentLoaded |
window.__iv8__.eventLoop.advance(ms) | |
window.__iv8__.netLog.entries | 捕获 JS 执行期间所有 XHR/fetch 请求(url + method + headers + body) |
与传统方案的本质区别
一句话总结:iv8 相当于把 Chromium 的 V8 引擎单独拆出来,用 Python 直接控制,同时保留了关键浏览器 API 的模拟层。
三、iv8 的内部机制
3.1 time_mode="logical" —— 逻辑时间加速
iv8 支持两种时间模式:
- • real(真实时间):
setTimeout(fn, 5000) 真的等 5 秒。适合需要观察真实时序的场景。 - • logical(逻辑时间):
eventLoop.advance(5000) 瞬间推进定时器队列 5000ms。所有到期回调立即执行,不等待真实时间。
在逆向场景中,我们通常不需要真的等 5 秒让 Akamai 采集指纹。advance(5000) 一次性跑完所有排队的定时器,整个过程不到一秒。这叫 事件循环加速,是 iv8 在逆向场景中最关键的特性。
3.2 page.load() —— 模拟完整页面加载
ctx.expose({"baseURL": page_url,"html": html_content,"resources": {"/path/to/script.js": script_content, ...},}, "s1")ctx.eval("window.__iv8__.page.load(window.__iv8__.data.s1)")
page.load() 内部做了这几件事:
- 1. 解析 HTML → 构建 DOM 树(document、body、head、script 标签等)
- 2. 按文档顺序执行
<script> → 每个 script 按其 src 从 resources 字典取内容,用 V8 编译执行 - 3. 触发 DOMContentLoaded 事件 → 页面脚本中注册的 DOMContentLoaded 回调开始运行
- 4. 脚本内部的定时器注册 →
setTimeout、setInterval 被捕获到事件循环队列中
注意:page.load() 是同步的,它只执行 script 标签和触发事件,不会自动推进事件循环。定时器回调要等到调用 eventLoop.advance() 才执行。
3.3 netLog —— XHR/fetch 透明捕获
iv8 在 C++ 层劫持了 XMLHttpRequest 和 fetch。JS 中任何网络请求都会被拦截:
- • GET 请求:URL、headers、响应内容全部记录
- • POST 请求:URL、headers、body(sensor_data)、响应全部记录
捕获的数据通过 window.__iv8__.netLog.entries 暴露为 JSON 数组:
[{"method":"POST","url":"/h8u_e/.../2F0LGcB","body":"...sensor_data...","status":200},{"method":"POST","url":"/h8u_e/.../2F0LGcB","body":"...second_post...","status":200}]
对于 Akamai 绕过,sensor_data 的 POST body 就是通过 netLog 捕获的,无需手动 Hook XHR。
3.4 environment —— 可控的浏览器指纹
iv8 在创建 JSContext 时接受一个 environment 字典:
environment = {"navigator": {"userAgent": "Mozilla/5.0 ...","platform": "Win32","language": "zh-CN","languages": ["zh-CN", "en-US"],"hardwareConcurrency": 8,"deviceMemory": 8,"webdriver": False, # 关键:声明不是自动化工具 },"screen": {"width": 1920, "height": 1080,"colorDepth": 24, "pixelDepth": 24, },"location": {"href": "https://target.com/page","origin": "https://target.com","hostname": "target.com", },}
这些值在 JS 中通过 navigator.userAgent、screen.width、location.href 读取,与真实浏览器行为一致。对于目标网站的指纹采集脚本来说,iv8 环境与真实 Chrome 没有区别(在 Akamai 关心的维度上)。
3.5 C++ 层日志控制
iv8 的 C++ 层遇到未实现的浏览器 API 时会通过 stderr 输出 ERROR 日志:
[97m... ERROR: Navigator.plugins not implemented ...[0m
四、与传统逆向常见方案的对比
4.1 jsdom 补环境方案
传统逆向最常见的思路:在 Node.js 中用 jsdom 模拟浏览器,手动补 navigator、screen、document 等对象。
优点:灵活,可深度定制。
痛点:
- • Akamai v3 会检测 100+ 个属性,漏一个就过不去
- • 属性名往往是混淆过的,通过报错才能发现少了什么(报一个补一个,循环)
- • jsdom 的 DOM API 实现与浏览器有细微差异,可能被检测到
- • Node.js 的
globalThis、process、Buffer 等残留可能暴露运行环境
iv8 的改进:C++ 层预置了常用浏览器 API,不用手动补。遇到未实现的 API,看 iv8 的 stderr ERROR 日志就知道缺了什么,针对性添加。
4.2 Headless Chrome CDP 方案
通过 Playwright / Puppeteer / Selenium 控制真实浏览器。
优点:环境最真实。
痛点:
- • CDP 协议本身可能被检测(
navigator.webdriver、window.chrome.runtime 等)
iv8 的改进:无头、无浏览器进程、逻辑时间可加速。并发 100 个 JSContext 只有 Python 进程开销。
五、实战:通用 5 步 Akamai 绕过
以下流程适用于绝大多数 Akamai v2/v3 保护的网站。
Step 1:GET 页面,种子 Cookie
from curl_cffi import requests as cffi_requestssession = cffi_requests.Session(impersonate="chrome")resp = session.get("https://target.com/page", headers={...})html = resp.text # 拿到 _abck, ak_bmsc, bm_sz 初始 Cookie
curl_cffi 负责 TLS 指纹伪装。impersonate="chrome" 模拟 Chrome 的 JA3/JA4 指纹,避免在 TLS 层被 Akamai 识别。
Step 2:动态提取 Akamai 脚本路径
scripts = {}for src in re.findall(r'src="(/[A-Za-z0-9_\-]+/[A-Za-z0-9_\-]+/[A-Za-z0-9_\-/]+)"', html):if"/akam/"in src: scripts["bootstrap"] = BASE_URL + srcelif"h8u_e"in src: scripts["sensor"] = BASE_URL + src
为什么不硬编码? Akamai 定期轮换脚本路径(大概每周变化),硬编码 = 一周后失效。
Step 3:同一 Session 下载脚本(关键坑点)
headers = {"sec-fetch-dest": "script", "referer": PAGE_URL, ...}for _ inrange(3): # 3 轮for name, url in scripts.items(): r = session.get(url, headers=headers) script_contents[name] = r.text
必须用同一个 session!服务端将 _abck Cookie 与 sensor_data 绑定校验。换 session 会导致 _abck 与 sensor_data 不匹配,永远无法通过验证。
Step 4:iv8 运行 Akamai JS
resources = {surl: content for ...} # URL → 脚本内容映射with iv8.JSContext(environment=env, time_mode="logical") as ctx: ctx.expose({"baseURL": PAGE_URL, "html": html, "resources": resources}, "s1") ctx.eval("window.__iv8__.page.load(window.__iv8__.data.s1)") # 加载页面 ctx.eval("window.__iv8__.eventLoop.advance(5000)") # 加速执行 entries = json.loads(ctx.eval("JSON.stringify(window.__iv8__.netLog.entries)"))
Step 5:回传 sensor_data,验证通过
for e in entries:if e["method"] == "POST"and"h8u_e"in e["url"]: session.post(e["url"], data=e["body"], headers={...})# 验证resp = session.get("https://target.com/api/protected")print("绕过成功"if resp.status_code == 200else"仍在拦截")
POST 回服务端后,_abck Cookie 转为已验证状态,后续请求畅通无阻。
六、iv8 的适用场景
| | |
| | sensor_data 生成可直接用 netLog 捕获 |
| | JS 动态参数在 V8 中跑出来,netLog 抓 XHR |
| | Turnstile 涉及更多浏览器 API,可能需要补环境 |
| | 把签名函数或整个 bundle 丢给 iv8,调用后取结果 |
| | Community 版 netLog 主要覆盖 XHR/fetch |
| | |
| | 绕过一次拿到 cookie,后续 curl_cffi 直接发请求 |
七、iv8 的局限性
- 1. Community 版缺少部分浏览器 API:WebGL(Canvas 指纹)、
window.chrome RuntimeContext、Service Worker 等。对于基础 Akamai 绕过足够,但高对抗场景可能需要额外补环境。 - 2. 闭源:不像 jsdom 可以看源码调试。遇到问题只能看 C++ 层的 ERROR 日志推断。
- 3. Windows 支持可能有坑:GBK 终端下 C++ 日志乱码、ANSI 转义序列残留。
- 4. 真实网络请求:Community 版不做真实 HTTP,需要配合 curl_cffi 使用。netLog 只是"记录"JS 中发起的请求,实际收发由外部 session 完成。
八、总结
| | |
| | |
| | |
| | navigator、screen、location 等 |
| | 拦截 XHR/fetch 拿 sensor_data 或签名参数 |
| | 全流程同一 Session,保证 _abck 一致性 |
| | |
iv8 的价值在于把浏览器 JS 执行这一环节单独抽出来,用 Python 原生控制,不要浏览器、不要 Node.js、不要双进程通信。V8 引擎的性能 + 逻辑时间加速 + netLog 透明捕获,这三者组合构成了新一代 JS 逆向工具链的核心。
对于逆向爱好者来说,iv8 提供了一种比 jsdom 补环境更稳定、比 CDP 更轻量的方案。如果你经常面对 Akamai、瑞数、Cloudflare 或者各种自定义 JS 签名,iv8 值得一试。