移植 OLLVM 到 LLVM18,修复控制流平坦化报错

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

前言

把 OLLVM 移植到 LLVM18 后,发现 -fla(控制流平坦化)并不能正常使用。

关于移植过程可以参考这篇文章 【移植 OLLVM 到 LLVM 18,C&C++代码混淆

测试代码 fla.c

#include <stdlib.h>
int main(int argc, char** argv) {
  int a = atoi(argv[1]);
  if(a == 0)
    return 1;
  else
    return 10;
  return 0;
}

使用 clang 编译并启用 Control Flow Flattening(控制流平坦化)报错如下:

D:\Projects\llvm-project\build>clang -mllvm -fla fla.c -o fla.exe
[OLLVM] run.PipelineStartEPCallback
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: D:\\Projects\\llvm-project\\build\\bin\\clang.exe -cc1 -triple x86_64-pc-windows-msvc19.42.34433 -emit-obj -mrelax-all -mincremental-linker-compatible -dumpdir fla.exe- -disable-free -clear-ast-before-backend -main-file-name fla.c -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -fdebug-compilation-dir=D:\\Projects\\llvm-project\\build -fcoverage-compilation-dir=D:\\Projects\\llvm-project\\build -resource-dir D:\\Projects\\llvm-project\\build\\lib\\clang\\18 -internal-isystem D:\\Projects\\llvm-project\\build\\lib\\clang\\18\\include -internal-isystem D:\\App\\VisualStudio\\IDE\\VC\\Tools\\MSVC\\14.42.34433\\include -internal-isystem D:\\App\\VisualStudio\\IDE\\VC\\Tools\\MSVC\\14.42.34433\\ATLMFC\\include -internal-isystem D:\\App\\VisualStudio\\IDE\\VC\\Auxiliary\\VS\\include -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt" -internal-isystem D:\\App\\VisualStudio\\IDE\\VC\\Tools\\MSVC\\14.42.34433\\include -intern
<think>我们正在讨论使用AST(抽象语法树)操作来还原控制流平坦(ControlFlowFlattening)时可能遇到的常见问题和陷阱。控制流平坦是一种代码混淆技术,它通过打破代码块之间的自然顺序关系,将它们转换为一个大的switch结构或状态机结构,从而增加逆向工程的难度。还原这种混淆的关键在于识别基本块之间的真实控制流关系,并重构出原始的控制流图。常见陷阱及问题:1.**状态变量分析不准确**:控制流平坦通常引入一个或多个状态变量(或称为分发器变量),这些变量控制着基本块的执行顺序。如果状态变量的分析不准确(例如未能正确识别状态变量的类型、取值范围或更新逻辑),可能导致还原后的控制流图错误。2.**无法识别所有基本块**:在构建AST时,可能由于混淆技术的干扰(如插入无效代码、代码分裂等),导致某些基本块没有被正确识别或提取。这会导致还原的控制流图不完整。3.**循环结构还原困难**:原始代码中的循环结构在平坦后通常被转换为线性序列,并通过状态变量跳转模拟循环。还原时,需要将分散的循环块重新组合,并正确识别循环条件和循环体。如果循环结构嵌套或存在多个出口,还原将更加复杂。4.**条件分支处理错误**:原始的条件分支(如if-else)在平坦后被分解为多个独立的基本块,并通过状态变量跳转。还原时需要正确识别条件判断的原始逻辑,并重建条件分支结构。如果条件表达式被混淆(如使用复杂的逻辑运算),则可能难以还原。5.**不可达代码的干扰**:混淆器可能插入一些永远不会执行到的代码(死代码),这些代码在AST中也会被解析。如果在还原过程中没有过滤掉这些代码,它们可能会干扰控制流的重建。6.**异常处理与函数调用**:如果平坦的代码中包含异常处理(如try-catch)或函数调用,这些结构在还原时可能被破坏。因为函数调用可能改变状态变量的值,而异常处理会引入额外的控制流路径。7.**性能问题**:对大型代码进行AST遍历和重构可能非常耗时,尤其是在处理深度混淆的代码时。如果算法设计不当,可能导致内存溢出或运行时间过长。8.**多级混淆**:代码可能经过多层混淆(例如先进行控制流平坦,再进行常量加密、字符串加密等),这会使还原过程更加困难。AST操作需要按照正确的顺序解除这些混淆,否则可能无法完全还原。9.**平台和语言特性的差异**:不同编程语言(如C++、Java、Python)的AST结构不同,而不同混淆工具的实现也有差异。还原工具需要针对特定的语言和混淆工具进行适配,否则可能无法正确处理某些语法结构。10.**还原后的代码可读性**:即使成功还原了控制流,生成的代码可能仍然难以阅读(例如变量名被混淆,代码结构不清晰)。因此,还原后通常还需要进行变量重命名、死代码消除等优。为了有效应对这些陷阱,建议:-使用成熟的AST解析库(如Python的ast、Clang的AST等)来确保语法解析的准确性。-结合数据流分析来跟踪状态变量的变,提高状态分析的准确性。-采用逐步还原的策略,先处理简单的部分,再逐步解决复杂结构。-使用图形工具可视控制流图,帮助理解还原过程。§§相关问题§§1.如何准确识别控制流平坦中的状态变量?2.在还原控制流平坦时,如何处理循环结构的还原?3.有哪些工具或库可以辅助进行AST操作以还原控制流平坦?4.控制流平坦与其他混淆技术(如虚假控制流)结合时,如何有效还原?5.还原控制流平坦后,如何进一步优代码的可读性?</think>### AST操作还原控制流平坦的常见陷阱 控制流平坦是一种代码混淆技术,通过将原始控制流拆解为状态机结构(通常用`switch-case`实现),增加逆向分析难度。使用**抽象语法树(AST)** 还原时需注意以下陷阱: --- #### 1. **状态变量识别错误** - **问题**:混淆后的代码依赖**状态变量**(如`int state`)驱动跳转,若分析工具未能准确追踪该变量的数据流,会导致还原失败。 - **案例**:状态变量可能被多重赋值或加密,例如: ```c state = (input * 0xDEADBEEF) % 10; // 非线性运算干扰分析 ``` - **解决**:结合数据流分析(如常量传播、污点分析)精确跟踪状态变量变[^1]。 --- #### 2. **基本块合并失效** - **问题**:平坦将原始代码拆分为独立**基本块**(Basic Block),还原需合并相关块。但以下情况会导致合并错误: - **虚假控制流**:混淆器插入永不执行的条件分支(如`if(false){...}`)。 - **间接跳转**:通过函数指针或`goto`动态跳转,AST静态分析难以解析。 - **解决**:动态执行(如符号执行)辅助验证控制流可行性[^2]。 --- #### 3. **循环结构还原困难** - **问题**:原始循环被拆解为多个离散状态,AST操作可能: - 将循环误识别为顺序结构; - 无法重建循环边界条件(如混淆后的`for`→`switch`嵌套)。 - **案例**: ```c while(i<10){...} // 原始代码 // 平坦后变为: switch(state){ case 0: if(i<10) state=1; else state=2; ... } ``` - **解决**:识别循环模式(如状态变量回退到前驱块)。 --- #### 4. **上下文信息丢失** - **问题**:AST操作可能忽略**环境依赖**: - 全局变量或寄存器值影响状态跳转; - 外部函数调用修改关键状态(如`state = rand()`)。 - **后果**:还原后的控制流缺少外部依赖逻辑,无法执行。 - **解决**:构建跨函数调用图(Call Graph)追踪副作用。 --- #### 5. **性能与复杂度瓶颈** - **问题**:深度混淆的代码生成**超大规模AST**,导致: - 内存溢出(百万级节点); - 分析算法指数级耗时(如路径爆炸)。 - **优**: 1. 分阶段处理:先简AST(删除死代码),再还原控制流; 2. 启发式剪枝:忽略概率极低的路径。 --- #### 6. **平台与编译器差异** - **问题**:不同编译器(GCC/Clang/MSVC)生成AST结构差异大,例如: - Clang为`switch`生成`CompoundStmt`嵌套层; - GCC可能插入隐式内存屏障。 - **陷阱**:通用还原工具需适配多种IR(中间表示)。 --- ### 关键缓解策略 1. **混合分析**:结合静态(AST)与动态(符号执行/DSE)验证; 2. **模式匹配**:预定义平坦模板(如OLLVM模式)加速识别; 3. **增量还原**:优先处理确定性路径,逐步扩展至模糊路径; 4. **可视辅助**:生成控制流图(CFG)人工验证关键节点。 > 示例工具链: > - **静态分析**:Clang AST + LLVM Pass > - **动态分析**:Angr/KLEE符号执行引擎 > - **可视**:GraphViz生成CFG
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值