OLLVM 移植 LLVM18 踩坑:一步步调试修复控制流平坦化

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

前言

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

关于移植过程参考这篇文章:OLLVM 移植 LLVM 18 实战,轻松实现 C&C++ 代码混淆

测试代码 fla.c

arduino 复制代码
#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(控制流平坦化)报错如下:

less 复制代码
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 -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" -ferror-limit 19 -fmessage-length=133 -fno-use-cxa-atexit -fms-extensions -fms-compatibility -fms-compatibility-version=19.42.34433 -fskip-odr-check-in-gmf -fdelayed-template-parsing -fcolor-diagnostics -mllvm -fla -faddrsig -o C:\\Users\\17759\\AppData\\Local\\Temp\\fla-6c1e10.o -x c fla.c
1.      <eof> parser at end of file
2.      Optimizer
Exception Code: 0xC0000005
 #0 0x00007ff627d441ff llvm::Instruction::handleMarkerRemoval(void) D:\Projects\llvm-project\llvm\lib\IR\Instruction.cpp:87:0
 #1 0x00007ff627d443d9 llvm::Instruction::eraseFromParent(void) D:\Projects\llvm-project\llvm\lib\IR\Instruction.cpp:94:0
 #2 0x00007ff62bb26d39 llvm::FlatteningPass::flatten(class llvm::Function &) D:\Projects\llvm-project\llvm\lib\Passes\Obfuscation\Flattening.cpp:101:0
 #3 0x00007ff62bb26316 llvm::FlatteningPass::run(class llvm::Function &, class llvm::AnalysisManager<class llvm::Function> &) D:\Projects\llvm-project\llvm\lib\Passes\Obfuscation\Flattening.cpp:20:0
 #4 0x00007ff62ba9ab18 llvm::detail::PassModel<class llvm::Function, class llvm::FlatteningPass, class llvm::PreservedAnalyses, class llvm::AnalysisManager<class llvm::Function>>::run(class llvm::Function &, class llvm::AnalysisManager<class llvm::Function> &) D:\Projects\llvm-project\llvm\include\llvm\IR\PassManagerInternal.h:89:0
 #5 0x00007ff623887eb1 llvm::PassManager<class llvm::Function, class llvm::AnalysisManager<class llvm::Function>>::run(class llvm::Function &, class llvm::AnalysisManager<class llvm::Function> &) D:\Projects\llvm-project\llvm\include\llvm\IR\PassManager.h:543:0
 #6 0x00007ff6238884e8 llvm::detail::PassModel<class llvm::Function, class llvm::PassManager<class llvm::Function, class llvm::AnalysisManager<class llvm::Function>>, class llvm::PreservedAnalyses, class llvm::AnalysisManager<class llvm::Function>>::run(class llvm::Function &, class llvm::AnalysisManager<class llvm::Function> &) D:\Projects\llvm-project\llvm\include\llvm\IR\PassManagerInternal.h:89:0
 #7 0x00007ff627f3661f llvm::ModuleToFunctionPassAdaptor::run(class llvm::Module &, class llvm::AnalysisManager<class llvm::Module> &) D:\Projects\llvm-project\llvm\lib\IR\PassManager.cpp:123:0
 #8 0x00007ff623888be8 llvm::detail::PassModel<class llvm::Module, class llvm::ModuleToFunctionPassAdaptor, class llvm::PreservedAnalyses, class llvm::AnalysisManager<class llvm::Module>>::run(class llvm::Module &, class llvm::AnalysisManager<class llvm::Module> &) D:\Projects\llvm-project\llvm\include\llvm\IR\PassManagerInternal.h:89:0
 #9 0x00007ff627f4bf21 llvm::PassManager<class llvm::Module, class llvm::AnalysisManager<class llvm::Module>>::run(class llvm::Module &, class llvm::AnalysisManager<class llvm::Module> &) D:\Projects\llvm-project\llvm\include\llvm\IR\PassManager.h:543:0
#10 0x00007ff6294ff0ff `anonymous namespace'::EmitAssemblyHelper::RunOptimizationPipeline D:\Projects\llvm-project\clang\lib\CodeGen\BackendUtil.cpp:1150:0
#11 0x00007ff6294ffafc `anonymous namespace'::EmitAssemblyHelper::EmitAssembly D:\Projects\llvm-project\clang\lib\CodeGen\BackendUtil.cpp:1216:0
#12 0x00007ff6294fa859 clang::EmitBackendOutput(class clang::DiagnosticsEngine &, class clang::HeaderSearchOptions const &, class clang::CodeGenOptions const &, class clang::TargetOptions const &, class clang::LangOptions const &, class llvm::StringRef, class llvm::Module *, enum clang::BackendAction, class llvm::IntrusiveRefCntPtr<class llvm::vfs::FileSystem>, class std::unique_ptr<class llvm::raw_pwrite_stream, struct std::default_delete<class llvm::raw_pwrite_stream>>, class clang::BackendConsumer *) D:\Projects\llvm-project\clang\lib\CodeGen\BackendUtil.cpp:1377:0
#13 0x00007ff62a0c0090 clang::BackendConsumer::HandleTranslationUnit(class clang::ASTContext &) D:\Projects\llvm-project\clang\lib\CodeGen\CodeGenAction.cpp:379:0
#14 0x00007ff62e5cf6be clang::ParseAST(class clang::Sema &, bool, bool) D:\Projects\llvm-project\clang\lib\Parse\ParseAST.cpp:183:0
#15 0x00007ff62a57dfd2 clang::ASTFrontendAction::ExecuteAction(void) D:\Projects\llvm-project\clang\lib\Frontend\FrontendAction.cpp:1183:0
#16 0x00007ff62a0bd227 clang::CodeGenAction::ExecuteAction(void) D:\Projects\llvm-project\clang\lib\CodeGen\CodeGenAction.cpp:1153:0
#17 0x00007ff62a57d80c clang::FrontendAction::Execute(void) D:\Projects\llvm-project\clang\lib\Frontend\FrontendAction.cpp:1069:0
#18 0x00007ff62a53a7a4 clang::CompilerInstance::ExecuteAction(class clang::FrontendAction &) D:\Projects\llvm-project\clang\lib\Frontend\CompilerInstance.cpp:1057:0
#19 0x00007ff62a811c06 clang::ExecuteCompilerInvocation(class clang::CompilerInstance *) D:\Projects\llvm-project\clang\lib\FrontendTool\ExecuteCompilerInvocation.cpp:272:0
#20 0x00007ff622ef44ca cc1_main(class llvm::ArrayRef<char const *>, char const *, void *) D:\Projects\llvm-project\clang\tools\driver\cc1_main.cpp:294:0
#21 0x00007ff622eda94f ExecuteCC1Tool D:\Projects\llvm-project\clang\tools\driver\driver.cpp:365:0
#22 0x00007ff622edb195 clang_main(int, char **, struct llvm::ToolContext const &) D:\Projects\llvm-project\clang\tools\driver\driver.cpp:405:0
#23 0x00007ff622f27b46 main D:\Projects\llvm-project\build\tools\clang\tools\driver\clang-driver.cpp:17:0
#24 0x00007ff6329637d9 invoke_main D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:79:0
#25 0x00007ff6329636c2 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288:0
#26 0x00007ff63296357e __scrt_common_main D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:331:0
#27 0x00007ff63296386e mainCRTStartup D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp:17:0
#28 0x00007ffb44e4259d (C:\Windows\System32\KERNEL32.DLL+0x1259d)
#29 0x00007ffb456eaf38 (C:\Windows\SYSTEM32\ntdll.dll+0x5af38)
clang: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 18.1.8 (https://github.com/CYRUS-STUDIO/LLVM.git 98e0b5c7f1277be725f8150102b88a992f65d196)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: D:\Projects\llvm-project\build\bin
clang: note: diagnostic msg:
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: C:\Users\cyrus\AppData\Local\Temp\fla-7f9157.c
clang: note: diagnostic msg: C:\Users\cyrus\AppData\Local\Temp\fla-7f9157.sh
clang: note: diagnostic msg:

********************

从报错信息来看,Flattening.cpp 的第 101 行代码在运行时发生了异常,具体错误类型是 访问权限冲突 (0xC0000005),通常意味着代码尝试访问了无效或空的内存指针。

使用 CLion 调试 Flattening

使用 Clion 断点调试一下 Flattening.cpp。

1、在 CLion 中打开 llvm 工程

2、设置 CMake

  • 设置 LLVM_DIR 、 禁用 llvm-gtest
ini 复制代码
-DCMAKE_PREFIX_PATH="D:/Projects/llvm-project/build" -DLLVM_INCLUDE_TESTS=OFF
  • 工具链选择 Visual Studio

关于如何编译调试 LLVM 参考:LLVM 全面解析:NDK 为什么离不开它?如何亲手编译调试 clang

3、新增运行配置 ollvm-fla ,添加程序实参:

  • 编译可执行程序
arduino 复制代码
-mllvm -fla "D:\Projects\llvm-project\build\fla.c" -o "D:\Projects\llvm-project\build\fla.exe"
  • 或者编译 IR 文件
arduino 复制代码
-mllvm -fla -S -emit-llvm "D:\Projects\llvm-project\build\fla.c" -o "D:\Projects\llvm-project\build\fla.ll"

效果如下:

4、下断点并运行调试

通过断点调试发现崩溃的位置发生在:entryBB.getTerminator()->eraseFromParent();

问题分析与解决

在 Flattening.cpp 中 65 行和 97 行都调用了 entryBB.getTerminator()->eraseFromParent();

scss 复制代码
if (bEntryBB_isConditional) {
    entryBB.getTerminator()->eraseFromParent();
}
  • entryBB 是一个 BasicBlock。

  • getTerminator() 返回这个基本块的 终结指令 (terminator instruction),也就是最后一条控制流指令,比如 br、ret、switch。

  • eraseFromParent() 的作用是:把这条指令 从所属基本块里移除并销毁

问题原因:重复调用 eraseFromParent 从 entryBB 中移除终止指令导致的空指针异常。

把 Flattening.cpp 中 97行的代码

scss 复制代码
if (bEntryBB_isConditional) {
    entryBB.getTerminator()->eraseFromParent();
}

改为

scss 复制代码
BB->getTerminator()->eraseFromParent();

修改完成执行 ninja 命令重新编译 llvm 即可。

参考:github.com/DreamSoule/...

测试

使用 clang 并启用 -fla(控制流平坦化)编译 fla.c

复制代码
clang -mllvm -fla fla.c -o fla.exe

测试 fla.exe 正常运行

使用 IDA 打开编译后的可执行程序,下面是未使用混淆时的反汇编视图

经过控制流平坦化后

完整源码

开源地址:github.com/CYRUS-STUDI...

相关推荐
将编程培养成爱好7 小时前
C++ 设计模式《外卖菜单展示》
c++·设计模式
10001hours8 小时前
C语言第12讲
c语言·开发语言
努力的小帅9 小时前
C++_哈希
开发语言·c++·学习·算法·哈希算法·散列表
福赖9 小时前
《MySQL基础——C 语言链接》
c语言·数据库·mysql
阿让啊15 小时前
C语言strtol 函数使用方法
c语言·数据结构·c++·单片机·嵌入式硬件
liulilittle15 小时前
OPENPPP2 —— IP标准校验和算法深度剖析:从原理到SSE2优化实现
网络·c++·网络协议·tcp/ip·算法·ip·通信
田里的水稻18 小时前
C++_队列编码实例,从末端添加对象,同时把头部的对象剔除掉,中的队列长度为设置长度NUM_OBJ
java·c++·算法
Florence2318 小时前
计算机组成原理:GPU架构、并行计算、内存层次结构等
c语言
Jayden_Ruan19 小时前
C++逆向输出一个字符串(三)
开发语言·c++·算法