glizzykingdreko/new-datadome-deobfuscator
GitHub: glizzykingdreko/new-datadome-deobfuscator
基于 Babel AST 的 DataDome WAF JavaScript 反混淆工具,能够清理混淆代码并提取动态挑战和 WASM payload。
Stars: 14 | Forks: 0
# new-datadome-deobfuscator
基于 Babel AST 的反混淆工具,用于处理 DataDome 的 WAF(过渡页和验证码)。保持最新版本并持续维护。能够清理文件、提取动态挑战 (dynamic challenges) 以及提取内嵌的 WASM payload。  **开始之前** 请注意,DataDome 每天都会更新其挑战文件(重新编译、新的函数/标识符名称 + 动态挑战会被重排)。 本仓库得益于数月来对其更新模式和结构的不断研究与分析,旨在妥善处理这些内容。请点个 Star 并阅读我的其他作品来支持我。 [在线演示](https://new-datadome-deobfuscator.glizzykingdreko.com) ## 目录 - [反混淆了哪些内容](#what-gets-deobfuscated) - [核心逻辑概述](#the-logic-condensed) - [安装模块](#installing-the-module) - [作为 Node 模块使用](#use-it-as-a-node-module) - [提取动态挑战](#extracting-the-dynamic-challenge-from-the-module) - [提取 WASM payload](#extracting-the-wasm-payload-from-the-module) - [CLI 用法](#cli-usage) - [通过 CLI 提取动态挑战和 WASM](#extracting-dynamic-challenge-and-wasm-via-cli) - [Web 界面](#web-ui) - [输出结构](#output-shape) - [项目结构](#project-structure) - [不支持的功能](#whats-not-supported) - [相关工作](#related-work) - [延伸阅读](#further-reading) - [需要 DataDome 绕过解决方案?](#need-datadome-bypass-solutions) - [许可证与作者](#license--author) ## 反混淆了哪些内容 两个脚本,一条处理流水线: - **captcha.js**,DataDome 在验证码页面提供的 Webpack/Browserify 样式打包文件 - **interstitial.js**,在过渡 WAF 页面提供的匿名 IIFE 打包文件 两者都将输出为干净的、按模块划分的文件。此外,后处理程序还会提取每个会话的**动态挑战 (dynamic challenge)** 表达式,以及打包文件用于计算其检测签名的内嵌 **WASM payload + 辅助代码**。 处理前后的对比:  ## 核心逻辑概述 DataDome 仍在使用相同的旧混淆技术,他们有时推送的唯一更新只是在先前技术之上叠加了另一层或操作。通常,更新反混淆器意味着只需处理新的层或函数拼接。 该处理流水线分为八个阶段运行: ### 1. **十六进制和括号清理** `\x48\x65\x6c\x6c\x6f` → `"Hello"` `obj[['key']]` → `obj['key']` ### 2. **模块分离** 检测位于 `program.body[0]` 的打包文件结构,并将 IIFE / 函数体拆分为每个模块的 AST。Captcha 是 browserify 样式的 `!function(e,B,s){...}({...})`,interstitial 是 `(function(){ var e={...}; ... })()`。 ``` ! function A(Q, t, C) { // Modules handler function }({ 1: [function(A, Q, t) { /* Module with exports */ }, {}], 2: [function(A, Q, t) { /* Module with exports */ }, {}], ... }, {}, [6]); // Entry point (Module 6 in this case) ``` 分离后,你会得到类似这样的结果: ``` output/captcha/ captcha.js vm-obf.js bean.js hash.js helpers.js main.js mouseMaths.js picasso.js slidercaptcha.js ``` ### 3. **T-matrix 解析** DataDome 预先计算了一个二维数组,其单元格是对行数组的引用,然后在所有地方使用 `[x][y]` 作为 case 值和表键。该阶段会提取构建它的 `var e = (function(){...})()` IIFE,在 Node `vm` 上下文中运行该 IIFE,并将每个 `e[x][y]` 查找重写为它实际代表的小整数。我将其称为 TMatrix。我已经在[之前的文章和仓库](https://github.com/glizzykingdreko)中分析过此逻辑。
### 4. **字符串清理**
像 `let x = window.Number(-92)` 这样的变量会被执行并在正确的作用域内替换。然后我们会遇到像 `fn(numLit, numLit)` 这样的函数调用(实际的字符串混淆器),它们的参数现在已显示为字面量,因此我们可以将它们折叠成结果字符串。
### 5. **执行与替换**
T-matrix 替换、条件和表达式简化、if 语句折叠、不透明谓词折叠。最多 10 轮,直到没有任何变化为止。
### 6. **算术表达式移除**
移除诸如 `-3*(F&-881) + 1*~(F&o) + ... > -388` 这类的“混合布尔算术”表达式。该阶段会在 20 多个随机整数分配中对每个表达式进行采样。如果是不变量,则替换为字面量。如果是线性的,则拟合为 `a*x + b*y + c`。(这些随机操作旨在“使代码难以理解”,我们只需通过采样即可解决它们。)
### 7. **Switch-case 语句反扁平化(两次)**
`for (r = N; true;) { switch (r) { case X: ...; r = Y; continue; ... } }` 会被还原回线性代码。第一次运行处理简单的情况。然后,未使用的变量移除会剥离 DataDome 填充的伪装 for-init(例如 `for (var eA = 544; true;)`,其中 `eA` 从未使用,真实状态保存在 `r` 中),第二次运行将处理隐藏在这些伪装背后的内容。
### 8. **代码生成 + 后处理提取器**
每个分离的模块生成一个文件。然后 `extractDynamicChallenge`、`extractWasm` 和 `extractWasmChallengeFields` 会遍历清理后的 AST,并提取出每个会话的签名表达式、内嵌的 WASM 字节以及 WASM 散列的有序字段列表。这些内容将直接输出到结果对象上(参见 [作为 Node 模块使用](#use-it-as-a-node-module))。
有关每个阶段的详细步骤、每个保护机制背后的原理以及历史修复日志,请参阅 [LEARN.md](./LEARN.md)。
## 安装模块
```
npm install new-datadome-deobfuscator
```
Node 18 或更新版本。无本地依赖。
## 作为 Node 模块使用
```
const fs = require('fs');
const { deobfuscate } = require('new-datadome-deobfuscator');
const source = fs.readFileSync('captcha.js', 'utf8');
const result = deobfuscate(source, { logLevel: 'INFO' });
console.log(result.bundleType); // 'captcha' | 'interstitial' | 'unknown'
console.log(result.moduleOrder); // ['captcha', 'vm-obf', 'bean', ...]
console.log(result.stats.reductionPercent); // '12.33%'
for (const [name, code] of Object.entries(result.modules)) {
fs.writeFileSync(`out/${name}.js`, code);
}
```
结果对象:
| 字段 | 类型 | 说明 |
|---|---|---|
| `bundleType` | 字符串 | `'captcha'`、`'interstitial'` 或 `'unknown'` |
| `modules` | 对象 | `{ moduleName: deobfuscatedSource }` |
| `moduleOrder` | 字符串数组 | 原始输出顺序 |
| `dynamic_challenge` | 字符串 \| null | DataDome 为其请求签名按会话计算的位操作表达式 |
| `wasm` | 对象 \| null | 提取的 WASM payload + 辅助元数据 + 散列字段列表 |
| `stats` | 对象 | `{ original, deobfuscated, reduction, reductionPercent }` |
| `warnings` | 数组 | 非致命问题 |
| `errors` | 数组 | 带有 `code` 和 `message` 的致命问题 |
| `report` | 对象 | 用于下游工具的结构化报告 |
选项:
| 选项 | 默认值 | 含义 |
|---|---|---|
| `logLevel` | `'INFO'` | `'DEBUG' \| 'INFO' \| 'WARN' \| 'ERROR' \| 'NONE'` |
| `logger` | (内置) | 自定义 Logger 实例,覆盖 `logLevel` |
| `maxPasses` | `10` | 迭代次数上限 |
| `generatorOptions` | (无) | 转发给 `@babel/generator` |
### 从模块中提取动态挑战
`result.dynamic_challenge` 是一个字符串,包含 DataDome 在每个会话中对 `(build-sig, br_ow, br_oh, hardwareConcurrency)` 运行以生成其请求签名的精确位操作表达式。表达式内部的 Decoder 调用被预先解析为字面量,并且本地命名的源数组被重命名为一致的标识符,因此你得到的内容是可移植的:直接将其放入你自己的求解器中运行即可。
```
const { deobfuscate } = require('new-datadome-deobfuscator');
const fs = require('fs');
const source = fs.readFileSync('captcha.js', 'utf8');
const result = deobfuscate(source);
if (result.dynamic_challenge) {
// The expression as JS source. Plug into your solver / VM.
console.log(result.dynamic_challenge);
// Or persist for later replay
fs.writeFileSync('out/dynamic_challenge.js', result.dynamic_challenge);
} else {
console.warn('no dynamic challenge found, check warnings:', result.warnings);
}
```
当提取器找不到调用时返回 `null`(这很罕见,通常意味着是一个新的变体结构,请检查 `result.warnings`)。
### 从模块中提取 WASM payload
`result.wasm` 是完整的 WASM 数据块:原始的 Base64 字节、imports-provider 源码(以便你可以重建导入对象)、provider 访问的 `window.*` 属性列表、provider 传递调用的每个外部辅助函数,以及编译后的 WASM 实际散列的有序字段列表。
```
const { deobfuscate } = require('new-datadome-deobfuscator');
const fs = require('fs');
const source = fs.readFileSync('captcha.js', 'utf8');
const result = deobfuscate(source);
if (result.wasm) {
const { wasm, instance, providerName, windowAttributes, helpers, fields } = result.wasm;
// 1. Save the raw WASM bytes
fs.writeFileSync('out/datadome.wasm', Buffer.from(wasm, 'base64'));
// 2. Save the imports-provider so you can rebuild the import object
fs.writeFileSync('out/imports-provider.js', instance);
console.log('provider name:', providerName);
console.log('window attrs touched:', windowAttributes);
console.log('external helpers:', helpers.map(h => h.name));
console.log('hashed fields:', fields); // e.g. ['hardwareConcurrency', 'br_oh', 'maxTouchPoints', ...]
}
```
`wasm` 对象结构:
| 字段 | 类型 | 说明 |
|---|---|---|
| `wasm` | 字符串 | 原始 WASM 模块,Base64 编码(以 `AGFzbQ` 开头) |
| `instance` | 字符串 | imports-provider 函数的源代码 |
| `providerName` | 字符串 | 原始打包文件中 imports-provider 的名称 |
| `windowAttributes` | 字符串数组 | provider 访问的 `window.*` 属性(`DataView`、`Uint32Array`、`Error` 等) |
| `helpers` | 数组 | provider 调用的每个外部辅助函数的 `{ name, source }` |
| `fields` | 字符串数组 | 由 `wasm_b` 散列的字段名称的有序列表(例如 `hardwareConcurrency`、`br_oh`、`maxTouchPoints`) |
当打包文件没有内嵌 WASM 或提取器遇到无法识别的结构时,返回 `null`。
**注意** `wasm_boring` 挑战的字段每天都会变化,这就是为什么我们在提取动态辅助函数、实例和窗口属性时也会将它们一并提取出来。
## CLI 用法
```
# direct
npx datadome-deobfuscate input.js out/
# 附带结构化报告
npx datadome-deobfuscate input.js out/ --report out/report.json
```
标志:
| 标志 | 含义 |
|---|---|
| `--input ` | 位置输入参数的替代选项 |
| `--output ` | 位置 outputPrefix 参数的替代选项 |
| `--report ` | 写入完整的结构化报告(时间戳、每个模块的状态、统计数据、错误、警告、dynamic_challenge、wasm) |
| `--log-level ` | `DEBUG \| INFO \| WARN \| ERROR \| NONE`,默认为 `INFO` |
| `--no-delimiter` | 禁止在标准输出中显示 `===DEOBFUSCATOR_RESULT===` 行 |
| `-h`, `--help` | 用法说明 |
退出代码:
| 代码 | 含义 |
|---|---|
| 0 | 干净运行,无错误(允许警告) |
| 1 | 致命错误:输入错误、解析失败或意外崩溃 |
| 2 | 已完成,但至少有一个模块记录了错误 |
标准输出中 `===DEOBFUSCATOR_RESULT===` 之后的一行包含与 `--report` 标志写入磁盘的相同 JSON。这是为希望在不写入文件的情况下抓取输出的下游工具提供的约定:
```
report = json.loads(
out.split('===DEOBFUSCATOR_RESULT===', 1)[1].strip().splitlines()[0]
)
if report['status'] != 'success':
for e in report['errors']:
print(e['message'])
```
如果你不希望标准输出流中包含分隔符,请使用 `--no-delimiter` 禁用它。
### 通过 CLI 提取动态挑战和 WASM
后处理提取器在每次 CLI 调用时都会自动运行。动态挑战表达式和完整的 WASM 数据块将输出到磁盘上的结构化报告中(以及标准输出的分隔符行上)。不需要额外的标志。
```
npx datadome-deobfuscate input/captcha.js out/captcha/ --report out/captcha/report.json
```
然后读取你关心的部分:
```
# 仅动态 challenge 表达式
jq -r '.dynamic_challenge' out/captcha/report.json > out/dynamic_challenge.js
# 原始 WASM 字节(base64-decoded)
jq -r '.wasm.wasm' out/captcha/report.json | base64 --decode > out/datadome.wasm
# imports-provider 源代码
jq -r '.wasm.instance' out/captcha/report.json > out/imports-provider.js
# WASM 哈希的字段有序列表
jq -r '.wasm.fields[]' out/captcha/report.json
```
或者,如果你在不写入磁盘的情况下通过管道传输,可以抓取标准输出的分隔符内容:
```
npx datadome-deobfuscate input/captcha.js out/captcha/ \
| sed -n '/===DEOBFUSCATOR_RESULT===/{n;p;}' \
| jq '{ dc: .dynamic_challenge, wasm_fields: .wasm.fields }'
```
`dynamic_challenge` 和 `wasm` 始终出现在报告负载的顶层,无论它们是从哪个模块中提取的。
## Web 界面
本仓库还可作为可部署到 Vercel 的控制面板。在浏览器中拖放验证码或过渡页文件,观看流水线运行过程,并将模块作为 zip 文件打包返回。
本地运行:
```
git clone https://github.com/glizzykingdreko/new-datadome-deobfuscator
cd new-datadome-deobfuscator
npm install
npm run dev
# → http://localhost:3000
```
生产环境:
```
npm i -g vercel
vercel deploy
```
## 输出结构
| 打包文件 | 输出的模块 |
|---|---|
| **captcha** | `captcha`、`vm-obf`、`bean`、`hash`、`helpers`、`main`、`mouseMaths`、`picasso`、`slidercaptcha` |
| **interstitial** | `reloader`、`interstitial`、`obfuscate`、`helpers`、`vm-obf`、`localstorage`、`main` |
此外,当存在时:
- `dynamic_challenge`:DataDome 为其请求签名按会话计算的位操作表达式。
- `wasm`:内嵌的 WASM 模块字节以及提取的辅助元数据(provider 名称、访问的 window 属性、外部辅助函数、散列字段列表)。
## 项目结构
```
new-datadome-deobfuscator/
lib/index.js Public Node.js API. deobfuscate(source, options).
bin/cli.js CLI entry point.
api/deobfuscate.js Vercel serverless function. Streaming + non-streaming.
api/deobfuscate-worker.js Worker thread that runs the pipeline and streams logs.
server.js Local dev server. Serves public/, routes /api/deobfuscate.
public/ Web UI. index.html, styles.css, app.js.
transformers/
index.js Pipeline runner.
captcha.js Bundle-shape detection + module separation.
preprocessing/index.js Phases 1 through 7 + recursive harvest.
transformations/ One file per pass: t-matrix, switch-case, opaque-predicates, ...
extractDynamicChallenge.js Pulls out the per-session bit-twiddle expression.
extractWasm.js Extracts WASM bytes + imports provider + helpers.
extractWasmChallengeFields.js Reads the ordered field list fed into wasm_b().
input/ Reference obfuscated bundles.
output/ Reference deobfuscated output.
LEARN.md Per-phase walkthrough + historical fixes log.
CLAUDE.md Agent instructions for this codebase.
```
## 不支持的功能
- **DataDome `tags.js`**,DataDome 发布的第三个客户端脚本。不同的打包结构,不同的解码器位置。不在本工具范围内。
- **带有非数字参数的 Decoder 调用**,如果 `C(sVar)` 中的 `someVar` 不是字面量,则会保持原样。
- **具有动态初始状态的 for 循环状态机**,如果初始状态表达式包含无法解析的 Decoder 调用,反扁平化器将跳过该循环。目前在参考输入中 0 次跳过。
## 相关工作
我发布的其他 DataDome 相关作品,全部开源:
- [datadome-wasm](https://github.com/glizzykingdreko/datadome-wasm):对内嵌 WASM 模块的独立逆向工程。
- [datadome-encryption](https://github.com/glizzykingdreko/datadome-encryption):DataDome 的请求 payload 加密逻辑,Node 版本。
- [datadome-encryption-python](https://github.com/glizzykingdreko/datadome-encryption-python):相同的加密逻辑移植到 Python 版本。
- [Datadome-GeeTest-Captcha-Solver](https://github.com/glizzykingdreko/Datadome-GeeTest-Captcha-Solver):针对 DataDome 验证码 GeeTest 变体的求解器。
- [Datadome-Movements-Display](https://github.com/glizzykingdreko/Datadome-Movements-Display):DataDome 收集的鼠标移动信号可视化工具。
较早的仓库,保留作为参考,但**已被本仓库取代**:
- [Datadome-Deobfuscator](https://github.com/glizzykingdreko/Datadome-Deobfuscator)(2023 年 10 月,已过时)
- [Datadome-Interstitial-Deobfuscator](https://github.com/glizzykingdreko/Datadome-Interstitial-Deobfuscator)(2024 年 1 月,已过时)
- [Datadome-Captcha-Deobfuscator](https://github.com/glizzykingdreko/Datadome-Captcha-Deobfuscator)(2024 年 1 月,已过时)
- [Datadome-Interstital-Encryptor](https://github.com/glizzykingdreko/Datadome-Interstital-Encryptor)(已过时)
其他反机器人供应商,相同模式:
- [akamai-script-patcher](https://github.com/glizzykingdreko/akamai-script-patcher):Akamai 机器人管理脚本反混淆器 + 完整性检查补丁工具。
- [akamai-v3-deobfuscator](https://github.com/glizzykingdreko/akamai-v3-deobfuscator):Akamai v3 sensor 数据追踪与重构。
- [akamai-v3-tools](https://github.com/glizzykingdreko/akamai-v3-tools):Akamai v3 sensor 数据加密/解密 Web 界面。
## 延伸阅读
- [LEARN.md](./LEARN.md):深入探讨。每个阶段的原理、DataDome 堆叠的混淆模式、第四阶段的损坏保护机制、递归收集、switch-case 伪装 for-init 处理、后处理提取器以及修复日志。
- [Analyzing DataDome's latest changes](https://medium.com/@glizzykingdreko/analyzing-datadome-latest-changes-424f385bcdd4):关于最新反混淆器更新的 Medium 文章。
- [Breaking down DataDome's captcha WAF](https://medium.com/@glizzykingdreko/breaking-down-datadome-captcha-waf-d7b68cef3e21):关于验证码打包文件结构的 Medium 演练。
## 需要 DataDome 绕过解决方案?
联系真正了解该技术的专家,避免每次 Datadome 推送更新时你的项目都面临宕机的风险。

在 [TakionAPI](https://takionapi.tech),我们提供这项服务。请务必查看,开始[免费试用](https://dashboard.takionapi.tech),然后继续查看[我们的文档](https://docs.takionapi.tech),只需一次 API 调用,DataDome 将不再是你需要担心的问题。
查看我们的 [datadome-bypass-examples](https://github.com/glizzykingdreko/datadome-bypass-examples),并确保[开始免费试用](https://dashboard.takionapi.tech)以进行测试。
## 许可证与作者
MIT。作者:**glizzykingdreko**。
- [GitHub](https://github.com/glizzykingdreko)
- [Twitter](https://mobile.twitter.com/glizzykingdreko)
- [Medium](https://medium.com/@glizzykingdreko)
- [Email](mailto:glizzykingdreko@protonmail.com)
- [请我喝杯咖啡 ❤️](https://www.buymeacoffee.com/glizzykingdreko)
- [TakionAPI](https://takionapi.tech)
本仓库是 **TakionAPI 的 DataDome 解决方案 API** 的开源配套项目。如果你不想自己编写完整的绕过方案,[TakionAPI](https://takionapi.tech) 提供了一个即插即用的求解器。
基于 Babel AST 的反混淆工具,用于处理 DataDome 的 WAF(过渡页和验证码)。保持最新版本并持续维护。能够清理文件、提取动态挑战 (dynamic challenges) 以及提取内嵌的 WASM payload。  **开始之前** 请注意,DataDome 每天都会更新其挑战文件(重新编译、新的函数/标识符名称 + 动态挑战会被重排)。 本仓库得益于数月来对其更新模式和结构的不断研究与分析,旨在妥善处理这些内容。请点个 Star 并阅读我的其他作品来支持我。 [在线演示](https://new-datadome-deobfuscator.glizzykingdreko.com) ## 目录 - [反混淆了哪些内容](#what-gets-deobfuscated) - [核心逻辑概述](#the-logic-condensed) - [安装模块](#installing-the-module) - [作为 Node 模块使用](#use-it-as-a-node-module) - [提取动态挑战](#extracting-the-dynamic-challenge-from-the-module) - [提取 WASM payload](#extracting-the-wasm-payload-from-the-module) - [CLI 用法](#cli-usage) - [通过 CLI 提取动态挑战和 WASM](#extracting-dynamic-challenge-and-wasm-via-cli) - [Web 界面](#web-ui) - [输出结构](#output-shape) - [项目结构](#project-structure) - [不支持的功能](#whats-not-supported) - [相关工作](#related-work) - [延伸阅读](#further-reading) - [需要 DataDome 绕过解决方案?](#need-datadome-bypass-solutions) - [许可证与作者](#license--author) ## 反混淆了哪些内容 两个脚本,一条处理流水线: - **captcha.js**,DataDome 在验证码页面提供的 Webpack/Browserify 样式打包文件 - **interstitial.js**,在过渡 WAF 页面提供的匿名 IIFE 打包文件 两者都将输出为干净的、按模块划分的文件。此外,后处理程序还会提取每个会话的**动态挑战 (dynamic challenge)** 表达式,以及打包文件用于计算其检测签名的内嵌 **WASM payload + 辅助代码**。 处理前后的对比:  ## 核心逻辑概述 DataDome 仍在使用相同的旧混淆技术,他们有时推送的唯一更新只是在先前技术之上叠加了另一层或操作。通常,更新反混淆器意味着只需处理新的层或函数拼接。 该处理流水线分为八个阶段运行: ### 1. **十六进制和括号清理** `\x48\x65\x6c\x6c\x6f` → `"Hello"` `obj[['key']]` → `obj['key']` ### 2. **模块分离** 检测位于 `program.body[0]` 的打包文件结构,并将 IIFE / 函数体拆分为每个模块的 AST。Captcha 是 browserify 样式的 `!function(e,B,s){...}({...})`,interstitial 是 `(function(){ var e={...}; ... })()`。 ``` ! function A(Q, t, C) { // Modules handler function }({ 1: [function(A, Q, t) { /* Module with exports */ }, {}], 2: [function(A, Q, t) { /* Module with exports */ }, {}], ... }, {}, [6]); // Entry point (Module 6 in this case) ``` 分离后,你会得到类似这样的结果: ``` output/captcha/ captcha.js vm-obf.js bean.js hash.js helpers.js main.js mouseMaths.js picasso.js slidercaptcha.js ``` ### 3. **T-matrix 解析** DataDome 预先计算了一个二维数组,其单元格是对行数组的引用,然后在所有地方使用 `
标签:Babel, CMS安全, DataDome, JavaScript, MITM代理, npm, WAF, WASM, WebAssembly, 云资产清单, 人机识别, 代码分析, 凭证管理, 反混淆, 安全防护绕过, 暗色界面, 爬虫对抗, 网络安全, 脱壳, 自定义脚本, 逆向工程, 隐私保护, 验证码, 验证码绕过