glizzykingdreko/new-datadome-deobfuscator

GitHub: glizzykingdreko/new-datadome-deobfuscator

基于 Babel AST 的 DataDome WAF JavaScript 反混淆工具,能够清理混淆代码并提取动态挑战和 WASM payload。

Stars: 14 | Forks: 0

# new-datadome-deobfuscator
Status: Active Type: Reverse Engineering License: MIT GitHub stars GitHub repo
npm package Live App Read my DataDome studies Buy me a coffee

基于 Babel AST 的反混淆工具,用于处理 DataDome 的 WAF(过渡页和验证码)。保持最新版本并持续维护。能够清理文件、提取动态挑战 (dynamic challenges) 以及提取内嵌的 WASM payload。 ![仓库名称是向超级马里奥致敬](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/fff619af84222025.png) **开始之前** 请注意,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 + 辅助代码**。 处理前后的对比: ![处理前后对比](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/d8cb94f736222027.png) ## 核心逻辑概述 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://repository-images.githubusercontent.com/769273753/f6d31bca-c19b-4b61-894d-4b4452e44293) 在 [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, CMS安全, DataDome, JavaScript, MITM代理, npm, WAF, WASM, WebAssembly, 云资产清单, 人机识别, 代码分析, 凭证管理, 反混淆, 安全防护绕过, 暗色界面, 爬虫对抗, 网络安全, 脱壳, 自定义脚本, 逆向工程, 隐私保护, 验证码, 验证码绕过