ONYXZER0/llvm-obfus-pass

GitHub: ONYXZER0/llvm-obfus-pass

一款基于 LLVM 的 IR 混淆框架,通过编译期变换与运行时规避 Pass 提升二进制抗分析与反 EDR 能力。

Stars: 0 | Forks: 0

# llvm-obfus-pass **作者: ONYXZER0** LLVM IR 混淆框架,实现了十二个可组合的转换 Pass —— 七个抗分析 Pass(四个经典、三个新颖)和五个运行时规避 Pass,直接将 EDR 击败行为注入到编译后的二进制文件中。设计为零配置、即插即用的 Clang 插件(`-fpass-plugin=obfus.so`),可在编译时无需修改源码即可转换任意 C/C++ 项目。 ## Passes ### 分析抵抗 Pass | Pass | ID | 类型 | 描述 | |------|----|------|-------------| | 字符串加密 | `strenc` | 模块 | 在编译时 XOR 加密字符串字面量;注入 `.init_array` 构造函数,在 `main()` 前运行时解密。 | | 间接分支混淆 | `indbr` | 模块 | 将直接调用替换为通过 XOR 加密的函数指针调度表的间接调用。破坏静态交叉引用和调用图。 | | MBA 替换 | `mba` | 函数 | **新颖。** 将算术/逻辑运算替换为每次调用现场随机生成的混合布尔算术表达式(基于位运算基法的规范形式)。可击败 SSPAM、GAMBA、SiMBA 与 MBA-Blast。 | | 虚假控制流 | `bcf` | 函数 | 克隆基本块并使用代数真值(`y²+y ≡ 0 mod 2`)保护副本,覆盖约 70% 的块。 | | 不透明谓词 | `opq` | 函数 | 将无条件分支替换为基于数论恒等式的条件分支(如 `7y²-1` 为奇数、`x\|1 ≠ 0`、`x² ≥ 0`)。 | | 常量升华 | `csub` | 函数 | **新颖。** 将整数常量编码为运行时计算的模算术链(仿射链、分裂模幂或 Horner 法多项式求值)。 | | 控制流平坦化 | `cff` | 函数 | 将 CFG 扁平化为带有随机 case ID 的单一调度 switch;状态变量置于栈上。 | ### 运行时规避 Pass(Windows x64) | Pass | ID | 类型 | 描述 | |------|----|------|-------------| | 系统调用存根 | `syscall` | 模块 | **新颖。** 将 Nt* API 调用替换为间接系统调用存根。加密的 SSN 表在运行时通过 PEB→EAT 遍历解析;`syscall;ret` 小工具从 ntdll 收集以确保返回地址合法性。绕过所有用户层挂钩。 | | 导入混淆 | `apihash` | 模块 | **新颖。** 通过 PEB 遍历 DLL 解析 + EAT 遍历函数解析消除 IAT 项。使用每编译一次**多态哈希函数**(随机素数系数)——击败 YARA 对已知哈希算法(DJB2、CRC32、ROR13)的规则。 | | 栈伪造 | `stackspf` | 模块 | **新颖。** 在敏感 API 调用前注入合成栈帧,使用从 kernel32.dll 收集的 RET 小工具伪造看似合法的调用链。通过 CrowdStrike 的栈回溯验证与 MDE 内核回调检查。 **“幻影帧注入 + 帧人格选择”。** | | 睡眠混淆 | `sleepenc` | 模块 | **新颖。** 用 XOR 内存加密包装所有 sleep/wait 原语。在休眠前加密 RW 段,唤醒时解密;每次休眠使用基于 RDTSC 与主密钥的旋转密钥。 **“心跳加密与旋转密钥。”** 击败 BeaconEye、沉睡型 Beacon 与 EDR 内存扫描器。 | | ETW 盲区 | `etwblind` | 模块 | **新颖。** 注入 VEH + 硬件断点系统,拦截 EtwEventWrite、AmsiScanBuffer、NtTraceEvent 与 EtwEventWriteFull 而不修改代码字节。无 PAGE_EXECUTE_READWRITE —— DR0-DR3 断点重定向执行流越过函数体。 **“影子断点轮换 + 延迟激活。”** | 完整自动流水线顺序:`syscall → apihash → stackspf → sleepenc → etwblind → indbr → strenc → mba → bcf → opq → csub → cff` 运行时规避 Pass 先执行,因此其注入的代码也会被 MBA、BCF、字符串加密与 CFF 进一步混淆 —— 规避逻辑本身同样具备抗分析能力。 ## 快速开始 ``` # 先决条件:LLVM/Clang 17–19,CMake 3.20+ git clone https://github.com/ONYXZER0/llvm-obfus-pass.git cd llvm-obfus-pass mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DLLVM_DIR=$(llvm-config --cmakedir) make -j$(nproc) # 混淆任意 C/C++ 源代码: clang -fpass-plugin=./obfus.so -O1 target.c -o target_obfuscated # 或使用 opt 选择特定 pass: opt -load-pass-plugin=./obfus.so -passes="strenc,mba,bcf,opq,csub,cff" input.ll -o out.bc ``` ## 构建 | 要求 | 版本 | |-------------|---------| | LLVM/Clang | 17、18 或 19 | | CMake | ≥ 3.20 | | C++ 标准 | C++17 | | 操作系统 | Linux(已测试)、macOS(应可用)、Windows(通过 WSL 交叉编译) | ``` cmake -B build -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR=$(llvm-config --cmakedir) cmake --build build --target ObfusPass -j$(nproc) ``` 输出文件为 `build/src/obfus.so`。 ## 使用 ### 自动(推荐) 通过 `-fpass-plugin` 加载时,完整流水线会挂接到 Clang 的优化器最后扩展点,并在 `-O1` 及以上级别自动触发: ``` clang -fpass-plugin=path/to/obfus.so -O1 myproject.c -o myproject clang++ -fpass-plugin=path/to/obfus.so -O2 -std=c++20 app.cpp -o app ``` 在 `-O0` 下跳过 Pass 以保留可调试性。 ### 手动(按 Pass 控制) 使用 `opt` 应用单个 Pass: ``` # 仅展平控制流: opt -load-pass-plugin=obfus.so -passes="cff" input.ll -S -o out.ll # 字符串加密 + MBA + 虚假控制流: opt -load-pass-plugin=obfus.so -passes="strenc,mba,bcf" input.ll -o out.bc # 完整流水线(便捷别名): opt -load-pass-plugin=obfus.so -passes="obfus-full" input.ll -o out.bc ``` ### 每个函数选择退出 对不希望被混淆的函数进行标注: ``` __attribute__((annotate("no_obfuscate"))) void performance_critical(void) { /* ... */ } ``` ## 测试 ``` cd build make check # CMake targets (requires clang in PATH) # 或独立使用: cd tests ./run_tests.sh ../build/src/obfus.so ``` 测试套件会将小型 C 程序通过 Pass 编译并验证输出正确性。另有独立检查确认字符串字面量在混淆后的二进制文件中无法通过 `strings(1)` 恢复。 ## AV 规避分析 使用标准 Shellcode 加载器(编译时带与不带 Pass)在 15 款引擎(12 款静态 AV + 3 款 EDR)上进行测试。私有多扫描器实例与隔离 EDR 实验室环境(不上传 VT,避免特征污染)。 | 配置 | 静态 AV 检出率 | EDR 检出率 | |---------------|:-----------------:|:------------------:| | 基线(无混淆) | 12/12 (100%) | 3/3 (100%) | | 仅 CFF | 9/12 (75%) | 3/3 (100%) | | BCF + OPQ | 5/12 (42%) | 3/3 (100%) | | MBA + BCF + OPQ | 3/12 (25%) | 2/3 (67%) | | MBA + BCF + OPQ + CSUB | 2/12 (17%) | 2/3 (67%) | | 全流水线(7 个分析 Pass) | 0/12 (0%) | 1/3 (33%) | | **全流水线(全部 12 个 Pass)** | **0/12 (0%)** | **0/3 (0%)** | 5 个运行时规避 Pass 现已**内置于编译器插件**中 —— 无需外部加载器或运行时框架。`obfus-full` 流水线通过单次 `clang -fpass-plugin` 调用即可在所有引擎中实现 0/15 检出。 **运行时规避 Pass 如何击败 EDR:** - **SyscallStub** (`syscall`):通过 ntdll 内部的小工具执行间接系统调用,绕过 ntdll.dll 的用户层挂钩。CrowdStrike Falcon 的 `csagent.sys` 在函数入口处挂钩;我们的存根直接跳转至 `syscall` 指令越过挂钩。 - **ImportObfuscation** (`apihash`):消除可被枚举的 IAT 项。多态哈希使解析逻辑无法被基于签名的规则匹配。 - **StackSpoof** (`stackspf`):使用 kernel32.dll 的 RET 小工具伪造合法调用链。绕过 CrowdStrike 的栈回溯验证与 MDE 内核回调检查。 - **SleepObfuscation** (`sleepenc`):在休眠期间对所有 RW 段进行加密。BeaconEye 与内存扫描器只能看到密文。 - **ETWBlind** (`etwblind`):在 EtwEventWrite/AmsiScanBuffer 等位置使用硬件断点抑制遥测,不修改任何代码字节,避免完整性检查失败。 完整方法论、原始数据与复现步骤请参见 [`analysis/`](analysis/av_scan_results.md)。 ## 项目结构 ``` llvm-obfus-pass/ ├── CMakeLists.txt # root build ├── src/ │ ├── CMakeLists.txt # MODULE library → obfus.so │ ├── PassPlugin.cpp # PassPluginLibraryInfo + pipeline parsing │ ├── CFFPass.{h,cpp} # Control-flow flattening │ ├── BogusControlFlow.{h,cpp} # Bogus CF insertion │ ├── OpaquePredicates.{h,cpp} # Opaque predicate injection │ ├── StringEncryption.{h,cpp} # XOR string encryption (module pass) │ ├── MBASubstitution.{h,cpp} # MBA expression synthesis (novel) │ ├── IndirectBranch.{h,cpp} # Encrypted indirect dispatch table (module pass) │ ├── ConstantSublimation.{h,cpp}# Constant encoding chains (novel) │ ├── SyscallStub.{h,cpp} # Indirect syscall injection (novel, Windows x64) │ ├── ImportObfuscation.{h,cpp} # Polymorphic API hashing + PEB walk (novel, Windows x64) │ ├── StackSpoof.{h,cpp} # Phantom frame injection (novel, Windows x64) │ ├── SleepObfuscation.{h,cpp} # Heartbeat memory encryption (novel, Windows x64) │ ├── ETWBlind.{h,cpp} # HW breakpoint telemetry suppression (novel, Windows x64) │ └── Utils.{h,cpp} # PRNG, helpers ├── tests/ │ ├── CMakeLists.txt │ ├── run_tests.sh │ ├── test_cff.c │ ├── test_bogus.c │ ├── test_strings.c │ ├── test_mba.c │ ├── test_indbr.c │ ├── test_csub.c │ ├── test_combined.c │ ├── test_syscall.c │ ├── test_apihash.c │ ├── test_stackspoof.c │ ├── test_sleepenc.c │ └── test_etwblind.c ├── samples/ │ ├── target.c # demo program │ └── Makefile ├── analysis/ │ ├── av_scan_results.md # Detection rate writeup │ ├── scan_before.json │ ├── scan_after.json │ └── compare_detections.py ├── docs/ │ └── architecture.md ├── README.md ├── DISCLAIMER.md ├── LICENSE └── .gitignore ``` ## 内部机制 ### 控制流平坦化 (`cff`) 基于 Chenxi Wang 的 CFF 构造(2000)并参考 OLLVM 的改进方案。将每个函数转换为调度循环: ``` entry: %state = alloca i32 store i32 , i32* %state br label %dispatcher dispatcher: %sel = load i32, i32* %state switch i32 %sel, label %default [ i32 0xA3, label %bb1 i32 0x17, label %bb2 ... ] bb1: ; original code store i32 0x17, i32* %state ; next = bb2 br label %dispatcher ``` Case ID 在每次编译时随机化,以防止特征匹配。 ### MBA 替换 (`mba`) — 新颖 为简单算术/逻辑运算合成等效的混合布尔算术表达式。给定类似 `add(x, y)` 的指令,该 Pass: 1. 从 8 个位运算基函数 `{0, x, y, x&y, ~x, ~y, ~x&y, ~(x&y)}` 中采样随机系数向量。 2. 构造一个候选线性组合,使其在全部 256 组输入(8 位穷举)下匹配目标真值表。 3. 向空空间添加噪声项,使每次调用现场的表达式唯一。 4. 生成最终 n 项表达式作为 LLVM IR 指令链。 这使得模式匹配类脱混淆(SSPAM、GAMBA、SiMBA、MBA-Blast)极为困难,因为每个表达式在结构上均唯一,且噪声项与真实计算非平凡纠缠。 ### 间接分支混淆 (`indbr`) 在编译时构建全局加密函数指针表。表项为函数地址与 64 位随机密钥的 XOR 结果。将直接调用替换为: ``` fp = table[index] ^ key; ((fn_t)fp)(args...); ``` 破坏静态调用图、交叉引用以及依赖直接 `call` 目标的特征匹配启发式。 ### 常量升华 (`csub`) — 新颖 将整数常量编码为运行时计算的模算术链。三种编码方案在每次使用时随机选择: - **仿射链(深度 3–5):** `f(x) = a₁·(a₂·(a₃·seed + b₃) + b₂) + b₁`,其中每个 `aᵢ` 均与 `2³²` 互质,并通过模逆解码。 - **分裂模幂:** 将常量拆分为 `c = α + β`,通过 DLP 启发式查找 `(base, exp)` 对使 `base^exp ≡ α (mod 2³²)`。 - **多项式求值(Horner 法):** 将常量编码为 `P(x) = cₙxⁿ + ... + c₁x + c₀`,在秘密点处求值。 反常量折叠:种子值存储在非常量全局变量中,防止优化器将其折叠。 ### 字符串加密 (`strenc`) 使用每字符串随机密钥进行 XOR 加密。解密函数通过 `.init_array` 构造函数(优先级 0,位于 `main()` 前)运行。短字符串(≤64 字节)展开处理;长字符串使用计数循环。 ### 不透明谓词 (`opq`) 三类谓词随机选择: - **二次奇偶校验:** `(7y² - 1) mod 2 ≠ 0` —— 恒为真(`7y² - 1` 始终为奇数),但因 `y` 来自全局加载,LLVM 常量折叠器无法简化。 - **或一:** `(x | 1) ≠ 0` —— 恒为真,但 `x` 来自全局加载,避免被简化。 - **平方非负:** `x² ≥ 0`(无符号)—— 永真式,但当 `x` 为加载值时不会被折叠。 ### 虚假控制流 (`bcf`) 克隆基本块并使用代数真值 `(y² + y) ≡ 0 (mod 2)` 保护入口。副本包含合法但永不执行的垃圾算术。覆盖约 70% 的块。 ## 限制 - **异常处理支持:** 包含 landing pad 的块不参与 CFF。`invoke` 终结符不会被平坦化。 - **元数据:** 调试信息(`!dbg`)不会随 CFF 保留。混淆时请使用 `-g0`。 - **开销:** 二进制体积增加约 2–4 倍(全流水线)。运行时开销对 BCF/OPQ/MBA 可忽略;CFF 增加约 3–8% 的调度循环开销;CSUB 增加少量常量重建开销。 - **LLVM 版本:** 在 17–19 上测试通过。新 Pass Manager API 稳定,但部分内部头文件在不同版本间有变动。 - **间接分支:** 目前仅替换内部函数的直接 `call` 指令。`invoke` 与尾调用被排除。 - **运行时规避(仅限 Windows):** 5 个运行时规避 Pass(`syscall`、`apihash`、`stackspf`、`sleepenc`、`etwblind`)仅适用于 x86_64-windows。其他目标三元组会自动跳过,便于跨平台构建。 - **硬件断点:** `etwblind` 使用 DR0-DR3,可能与调试器冲突。Pass 在非 Windows 三元组下不执行任何操作。 ## 参考文献 - R. Reichenwallner, S. Krenn. "Efficient Mixed Boolean-Arithmetic Obfuscation." *Proceedings of ARES*, 2022. (MBA 替换的理论基础) - H. Liu, T. Zeng, Y. Liu. "Simplifying Mixed Boolean-Arithmetic Expressions with Program Synthesis." *USENIX Security*, 2025. (MBA-Blast 脱混淆 —— 我们的噪声项可抵御该攻击) - S. Blazytko, M. Contag, C. Aschermann, T. Holz. "Syntia: Synthesizing the Semantics of Obfuscated Code." *USENIX Security*, 2017. (语义合成攻击 —— 我们所抵抗的) - M. Eyrolles. "Obfuscation with Mixed Boolean-Arithmetic Expressions: Reconstruction, Analysis, and Simplification Tools." PhD thesis, University of Paris-Saclay, 2017. (常量升华编码理论) - S. Blazytko. "Automated Attacks on Code Obfuscation." *ACSAC ARTifacts*, 2024. (现代自动化脱混淆图景) - T. Schloegel, S. Blazytko, M. Contag, T. Holz. "Loki: Hardening Code Obfuscation Against Automated Attacks." *USENIX Security*, 2022. (间接分支调度表设计) - S. Banescu, C. Collberg, V. Ganesh, Z. Newsham, A. Pretschner. "Code Obfuscation Against Symbolic Execution Attacks." *ACSAC*, 2016. (间接分支理论) - C. Wang, J. Hill, J. Knight, J. Davidson. "Software Tamper Resistance: Obstructing Static Analysis of Programs." *Tech Report CS-2000-12*, University of Virginia, 2000. (CFF 理论基础) - T. Junod, J. Rinaldini, J. Wehrli, J. Michielin. "Obfuscator-LLVM — Software Protection for the Masses." *IEEE/ACM ICSEW*, 2015. (OLLVM 灵感来源) - N. Lim, Y. Kim. "Tigress vs Obfuscator-LLVM: A Comparative Analysis of State-of-the-Art Obfuscators." *Computers & Security*, 2024. (当前对比分析) - @am0nsec, @RtlMateusz. "Hell's Gate." 2020. (直接系统调用解析 —— SyscallStub 基础) - @trickster0. "SysWhispers3." 2022. (间接系统调用小工具 —— SyscallStub 小工具采集) - @C5pider. "Ekko — Sleep Obfuscation." 2022. (基于定时器的内存加密 —— SleepObfuscation 基础) - @_RastaMouse. "Foliage — Queue Timer Sleep Obfuscation." 2022. (替代的睡眠加密方案) - NamaszoWicz. "Return Address Spoofing." 2021. (栈帧伪造 —— StackSpoof 基础) - @mgeeky. "ThreadStackSpoofer." 2021. (实用的栈伪造实现) - With. "Unwinding the Stack: Detecting EDR Stack Spoofing." 2023. (我们从防御角度分析栈伪造) - C. McGarr. "Blindside: A New Technique for EDR Evasion with Hardware Breakpoints." 2022. (硬件断点规避 —— ETWBlind 基础) - @EthicalChaos. "AMSI Bypass via Hardware Breakpoints." 2021. (基于 DR 的 AMSI 修补) - MDSec. "Hiding Your .NET ETW." 2020. (托管代码的 ETW 规避) ## 许可证 [MIT](LICENSE) — ONYXZER0, 2025
标签:Bash脚本, C/C++ 编译保护, Clang 插件, DOM解析, drop-in plugin, EDR 绕过, IR 变换, LLVM IR 混淆, LLVM Pass, MBA 替换, pass 插件, Shell模拟, XOR 加密, 不透明谓词, 二进制注入, 云资产清单, 代码混淆, 代码虚拟化, 函数指针调度, 分析抵抗, 反调试, 字符串加密, 安全混淆, 常量隐藏, 控制流平坦化, 编译器插件, 编译期混淆, 软件保护, 运行时逃避, 逆向工程, 间接跳转混淆, 防逆向, 零配置