nstarke/egodeath

GitHub: nstarke/egodeath

一个基于密码学不可区分混淆研究理论的 JavaScript 混淆器,旨在使代码对人类和 LLM 均极难分析。

Stars: 27 | Forks: 1

# egodeath ![Tests](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/8ea8b08584164907.svg) ![images/screenshot.png](https://raw.githubusercontent.com/nstarke/egodeath/main/images/screenshot.png) 一个旨在使代码对人类和 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嵌入, 自动化攻击, 防分析