nstarke/egodeath
GitHub: nstarke/egodeath
一个基于密码学不可区分混淆研究理论的 JavaScript 混淆器,旨在使代码对人类和 LLM 均极难分析。
Stars: 27 | Forks: 1
# egodeath


一个旨在使代码对人类和 LLM 都极其难以阅读和分析的 JavaScript 混淆器。使用 TypeScript 编写。实现了源自同行评审的密码学混淆研究的技术。
## 安装
```
npm install
npm run build
```
## CLI 用法
```
# 基本用法
node dist/index.js input.js > output.js
# 带有 target token budget(默认:2,000,000)
node dist/index.js --target-tokens 500000 input.js > output.js
# 最小 obfuscation(较小的 output)
node dist/index.js --target-tokens 10000 input.js > output.js
# 最大 bloat(10M tokens)
node dist/index.js --target-tokens 10000000 input.js > output.js
# 使用环境变量
INPUT_FILE=input.js node dist/index.js > output.js
# 帮助
node dist/index.js --help
```
### 选项
| 选项 | 默认值 | 描述 |
|--------|---------|-------------|
| `--target-tokens ` | `2000000` | 以 token 为单位的目标输出大小。小输入会膨胀到此限制。大输入会产生较少的膨胀以保持在预算范围内。 |
| `--help, -h` | | 显示帮助信息 |
### npm scripts
```
npm run build # Compile TypeScript to dist/
npm run start # Run the obfuscator (reads input.js)
npm run test # Run the test suite
npm run obfuscate-package # Run compatibility tests against npm packages
```
## 编程式 API
```
const { obfuscate } = require('./dist/obfuscator');
const code = 'function add(a, b) { return a + b; }';
const obfuscated = obfuscate(code);
// With options
const obfuscated = obfuscate(code, { targetTokens: 500000 });
```
## 转换管道
混淆器分 4 个阶段应用 20 个转换。每个阶段都建立在前一个阶段的基础之上。
### 阶段 1:安全与反分析预转换
| 顺序 | 转换 | 文件 | 描述 |
|-------|-----------|------|-------------|
| 1 | **反调试陷阱** | `transforms/antiDebug.ts` | 注入 `eval("debugger")` 语句以及 10-20 个带有素数间隔(5秒-600秒)的 `setInterval` 循环,重复触发 debugger 断点。每个实例使用唯一的编码字符串。 |
| 2 | **穿孔程序触发器** | `transforms/tripwires.ts` | 嵌入将参数哈希值与秘密值进行比较的隐藏检查。5 种哈希模式(按位指纹、模运算、charCodeAt、数字哈希、typeof+length)。对秘密输入触发静默状态破坏、死循环或抛出异常。*[论文 4]* |
| 3 | **LPN 噪声注入** | `transforms/noiseInjection.ts` | 通过算术计算中的分叉路径添加和抵消随机噪声。6 种模式:加/减、XOR、乘/除、拆分双变量、计算哈希链、位旋转。如果不追踪完整的抵消过程,中间值将毫无意义。*[论文 7]* |
### 阶段 2:结构预转换
| 顺序 | 转换 | 文件 | 描述 |
|-------|-----------|------|-------------|
| 4 | **控制流平坦化** | `transforms/controlFlowFlattening.ts` | 将函数体转换为 `while(true) { switch((_s * P) % M) { ... } }` 状态机,并使用**模运算调度** —— case 值通过使用随机素数参数的 `(stateId * multiplier) % modulus` 进行编码。*[论文 3]* |
| 5 | **不透明谓词** | `transforms/opaquePredicates.ts` | 注入总是评估为 true 或 false,但在数学上难以证明的 `if` 条件(例如 `(x*x+x)%2===0`)。涵盖模运算、按位和类型检查类别的 15 个谓词公式。 |
| 6 | **代理函数** | `transforms/proxyFunctions.ts` | 通过两个调度器路由所有函数调用:用于简单调用的 `_fc(fn, ...args)`,以及用于方法调用的 `_mc(obj, prop, ...args)`。使用捕获在局部变量中的 `Function.prototype.apply` 以增强弹性。 |
| 7 | **上下文窗口耗尽** | `transforms/contextExhaustion.ts` | 将表达式包装在具有不透明条件、void 表达式链和条件 void 填充的深度嵌套的三元运算符中。迫使 LLM 将上下文窗口的 token 浪费在无意义信息上。 |
| 8 | **逗号表达式合并** | `transforms/commaExpressions.ts` | 将连续的表达式语句合并为单个逗号表达式:`a(); b(); return c()` 变为 `return a(), b(), c()`。 |
### 阶段 3:标识符处理遍
| 顺序 | 遍 | 文件 | 描述 |
|-------|------|------|-------------|
| 9 | **第 1 遍:编目** | `passes/firstPass.ts` | 遍历 AST 并编目每个标识符,构建一个全局映射表,从 16 个脚本范围(CJK、Hangul、希腊文、西里尔文、梵文、泰文、阿拉伯文、片假名等)中为每个标识符分配一个随机的 6-16 字符 Unicode 名称。 |
| 10 | **第 2 遍:替换** | `passes/secondPass.ts` | 将所有标识符名称替换为其混淆后的 Unicode 等价物。将 `require()` 参数编码为 `String.fromCharCode(...)`。将静态 `import`/`export` 源编码为 unicode 转义的字符串字面量。替换类 `superClass` 引用、模板字面量表达式、解构模式。 |
| 11 | **第 3 遍:虚假参数** | `passes/thirdPass.ts` | 向每个函数声明和表达式注入 0-15 个随机的未使用参数。跳过带有 rest 参数的函数。剥离所有注释。 |
### 阶段 4:后转换
| 顺序 | 转换 | 文件 | 描述 |
|-------|-----------|------|-------------|
| 12 | **全局变量编码** | `transforms/globalVariableEncoding.ts` | 将对全局变量的引用(从 `globalThis` + `window` 包中动态发现)替换为 `eval("Name".replace(new RegExp("$"), ""))`。两个字符串都流经字符串数组。 |
| 13 | **属性键编码** | `transforms/propertyKeyEncoding.ts` | 使用每个作用域的注册表将点访问转换为计算访问。跨作用域访问之所以有效,是因为所有后缀在运行时都会通过 `.replace()` 解析为相同的属性名。 |
| 14 | **数字编码** | `transforms/numberEncoding.ts` | 11 种编码策略:移位+加法、XOR 恒等式、补码、除法、嵌套移位、双重 NOT、模运算等。每个实例都是唯一生成的。跳过属性键和 switch case 值。 |
| 15 | **自完整性验证** | `transforms/selfIntegrity.ts` | 注入 2-4 个运行时检查:eval 原生代码验证、`Function.prototype.toString` 完整性、时间异常检测、代码结构验证。防篡改响应:忙等待、抛出异常、静默破坏。*[论文 10]* |
| 16 | **字符串数组提取** | `transforms/stringArrayExtraction.ts` | 将所有字符串收集到一个带有**链式 XOR 解密**(条目 N 的密钥取决于条目 N-1 的解码内容)和**稀疏的位置相关错误模式**(每个字符获得不同的 XOR 密钥,在选定位置具有受 LPN 启发的稀疏错误)的单个数组中。*[论文 2, 9]* |
| 17 | **Console 桩** | `obfuscator.ts` | 动态发现所有 `console` 方法,并将每个方法设置为无操作函数。 |
| 18 | **Terser 代码压缩** | `obfuscator.ts` | 通过 terser 剥离空白符/格式化(`mangle: false`,`compress: false`)。如果 terser 无法解析输出,则回退到基于正则表达式的剥离。 |
### 死代码注入
死代码在多个点注入,采用两种生成策略:
| 策略 | 来源 | 描述 |
|----------|--------|-------------|
| **基于模板** | `transforms/deadCodeInjection.ts` | 9 种模板类型:循环累加、数组构建、对象操作、字符串连接、嵌套条件、try/catch、while 倒计时、switch 计算、按位链。模板引用真实的作用域变量。 |
| **基于变异** | `transforms/deadCodeInjection.ts` | 克隆真实语句并对其进行变异:在等价组内交换运算符、扰动常量、重命名标识符。生成 AST 结构上完全相同的死代码,仅通过结构是无法与真实代码区分的。*[论文 3]* |
死代码注入点:
- CFF 死 switch case(每个函数额外增加约 30%,受预算缩放)
- 不透明谓词 else 分支
- 非 CFF 函数中的独立死代码块(受预算控制)
## 研究论文参考
几个转换的灵感来源于同行评审的密码学混淆研究:
| 论文 | 作者 | 实现的技术 |
|-------|---------|----------------------|
| **[论文 1]** [论混淆程序的(不)可能性](https://www.wisdom.weizmann.ac.il/~oded/p_obfuscate.pdf) | Barak, Goldreich, Impagliazzo, Rudich, Sahai, Vadhan, Yang | 不可混淆函数测试用例 —— 用于测试机密是否能在混淆中幸存下来的验证工具 |
| **[论文 2]** [面向所有电路的候选 iO 与函数式加密](https://eprint.iacr.org/2013/451.pdf) | Garg, Gentry, Halevi, Raykova, Sahai, Waters | 链式字符串解密 —— Kilian 风格的随机化,其中每个条目的密钥取决于前一个解码的字符串 |
| **[论文 3]** [基于多重线性子群消除假设的 iO](https://eprint.iacr.org/2014/309.pdf) | Gentry, Lewko, Sahai, Waters | 基于变异的死代码(在结构上与真实代码完全相同);CFF 中的模运算状态转换 |
| **[论文 4]** [如何使用 iO:可否认加密及更多](https://eprint.iacr.org/2013/454.pdf) | Sahai, Waters | 穿孔程序触发器 —— 在秘密输入时触发的隐藏检查 |
| **[论文 7]** [基于充分成立假设的 iO](https://eprint.iacr.org/2020/1003.pdf) | Jain, Lin, Sahai | 数字计算中受 LPN 启发的噪声注入 |
| **[论文 9]** [基于双线性映射和 LPN 变体的 iO](https://eprint.iacr.org/2024/856.pdf) | Ragavan, Vafa, Vaikuntanathan | 具有位置相关错误模式的稀疏 XOR 编码 |
| **[论文 10]** [Null 量子电路的 iO 及其应用](https://arxiv.org/pdf/2106.06094.pdf) | Bartusek, Malavolta | 用于死代码质量验证的 Null 电路测试;自完整性验证(双模式) |
## 验证工具
两个验证工具用于衡量混淆质量,位于 `src/verification/` 中:
### Null 电路测试 (`verification/nullCircuitTest.ts`)
混淆一个真实函数和一个“null”函数(形状相同,但不执行任何操作),然后比较 14 个结构指标,以评估它们的可区分程度。相似度越高 = 混淆越好。
```
import { runNullCircuitTest } from './verification/nullCircuitTest';
const result = runNullCircuitTest(realCode, paramCount, stmtCount, threshold, targetTokens);
console.log('Similarity:', result.similarity); // 0.0-1.0
```
### 不可混淆函数测试 (`verification/unobfuscatableTests.ts`)
来自论文 1 的不可能性证明的 7 个测试用例,尝试从混淆后的代码中提取机密:
```
import { runAllTests, printSummary } from './verification/unobfuscatableTests';
console.log(printSummary(runAllTests(10000)));
```
测试:点函数(密码)、魔数、金丝雀字符串、嵌入密钥、URL、正则表达式模式、控制流签名。
## 输出大小预算
`--target-tokens` 选项通过膨胀预算控制输出大小,该预算用于缩放死代码注入(主要的体积杠杆)。预算控制的转换:
| 预算比率 | 启用的转换 |
|-------------|-------------------|
| > 3 | 反调试、触发器、CFF、不透明谓词、逗号合并 |
| > 5 | 代理函数、属性键编码、噪声注入、自完整性 |
| > 8 | 上下文窗口耗尽 |
| > 10 | 全局变量编码 |
死代码乘数从 1x(比率 30)到 150x(比率 1500+)缩放,控制死 switch case 和不透明谓词分支的数量与大小。
## 项目结构
```
src/
index.ts CLI entry point
obfuscator.ts Main pipeline orchestrator (20 transforms)
options.ts Budget system and options
types.ts AST type definitions
random.ts Random Unicode name generation (6-16 chars, 16 script ranges)
ast.ts AST node factory functions
keywords.ts Dynamic keyword discovery (globalThis + window package)
globals.ts Global state management (null-prototype maps)
substitute.ts Identifier substitution utilities
declarations.d.ts Module type declarations
passes/
firstPass.ts Identifier cataloging
secondPass.ts Identifier substitution + string encoding
thirdPass.ts Dummy parameter injection
transforms/
antiDebug.ts eval("debugger") traps + setInterval loops
tripwires.ts Punctured program secret-input checks [Paper 4]
noiseInjection.ts LPN-inspired arithmetic noise [Paper 7]
controlFlowFlattening.ts while/switch + modular arithmetic dispatch [Paper 3]
opaquePredicates.ts 15 always-true/false math predicates
proxyFunctions.ts Call graph flattening dispatchers
contextExhaustion.ts Ternary/void noise for LLM context filling
commaExpressions.ts Statement merging via comma operator
globalVariableEncoding.ts eval+replace for globals
propertyKeyEncoding.ts Computed property access with per-scope registries
numberEncoding.ts 11 bitwise/arithmetic encoding strategies
selfIntegrity.ts Anti-tamper runtime checks [Paper 10]
stringArrayExtraction.ts Chained XOR + sparse position errors [Papers 2, 9]
deadCodeInjection.ts Template + mutation-based dead code [Paper 3]
verification/
nullCircuitTest.ts Dead code quality scoring [Paper 10]
unobfuscatableTests.ts Secret extraction test cases [Paper 1]
__tests__/ 300+ unit tests across 21 suites
tools/
obfuscate-package.ts Webpack-based npm package compatibility testing
tests/
input*.js Original test input files
```
## 测试
```
# 运行所有测试
npm test
# 运行特定的 test suite
npx jest controlFlowFlattening
npx jest tripwires
npx jest noiseInjection
# 针对 npm packages 进行测试(clones repos、webpack-bundles、obfuscates、runs tests)
npm run obfuscate-package # All 10 packages
npm run obfuscate-package -- minimist semver # Specific packages
```
## 兼容性测试工具
`tools/obfuscate-package.ts` 工具针对真实的 npm 包测试混淆器:
1. 克隆该包的 git 仓库
2. 安装所有依赖项(包括用于测试的 devDependencies)
3. 如果该包有构建脚本,则构建该包
4. 使用 **webpack** 将库的主入口点打包成单个 CommonJS 文件
5. 在打包后的文件上运行混淆器
6. 用混淆后的包替换库的主入口
7. 针对混淆版本运行库自带的测试套件
### 输出位置
| 路径 | 内容 |
|------|----------|
| `dist/obfuscated//bundle.js` | 每个包经过混淆的 webpack bundle |
| `dist/obfuscated/report.json` | 包含 bundle 大小、混淆状态、测试输出的完整 JSON 报告 |
## 许可证
MIT - 版权所有 2026 Nicholas Starke
标签:AST转换, CMS安全, DNS 反向解析, JavaScript, MITM代理, SOC Prime, TypeScript, 代码混淆, 加密技术, 安全插件, 开发工具, 暗色界面, 自动化payload嵌入, 自动化攻击, 防分析