LLVM Pass的基本概念
- LLVM Pass框架是整个LLVM 提供给用户用来干预代码优化过程的框架,也是我们编写代码混淆工具的基础。
- 编译后的LLVM Pass通过优化器opt进行加载,可以对LLVM IR中间代码进行分析和修改,生成新的中间代码。
llvm/inlcude/llvm 文件夹
- llvm/include/llvm 文件夹存放了LLVM提供的一些公共头文件。
- 即我们在开发过程中可以使用的头文件。
llvm/lib文件夹
- llvm/lib文件夹存放了LLVM大部分源代码(.cpp文件)和一些不公开的头文件
llvm/lib/Transforms
- llvm/lib/Transforms文件夹存放所有LLVM Pass的源代码
- llvm/lib/Transforms文件夹也存放了一些LLVM自带的Pass
LLVM Pass的编写、编译以及加载
LLVM Pass的编写:Hello World
- LLVM Pass支持三种编译方式:
- 第一种是与整个LLVM一起重新编译,Pass代码需要存放在llvm/lib/Transforms文件夹中 (编译太耗时间)
- 第二种方法是通过CMake对Pass进行单独编译 (好!)
- 第三种方法是使用命令行对Pass进行单独编译 (项目越大越不好管理)
- 在设计一个新的LLVM Pass时,你最先要决定的就是选择Pass的类型。
- LLVM有多种类型的Pass可供选择,包括:ModulePass、FuncitonPass、CallGraphPass、LoopPass等等。
- FunctionPass以函数为单位进行处理
- FunctionPass的子类必须实现runOnFunction(Function &F)函数。
- 在FunctionPass运行时,会对程序中的每个函数执行runOnFunction函数。
LLVM Pass的编写:步骤
- 创建一个类(class),继承FunctionPass父类
- 在创建的类中实现runOnFunction(Function &F)函数
- 向 LLVM 注册我们的 Pass 类。
LLVM Pass的加载
- 使用优化器 opt 将处理中间代码,生成新的中间代码:
opt -load ./LLVMObfuscator.so -hlw -S hello.ll -o hello_opt.ll
- -load 加载编译好的 LLVM Pass(.so文件)进行优化
编写第一个LLVM Pass-实践部分
CMake创建
目录结构:
➜ OLLVM++-DEmo tree
.
├── Build
├── Test
│ └── TestProgram.cpp
├── test.sh
└── Transforms
├── CMakeLists.txt
├── include
└── src
└── HelloWorld.cpp
5 directories, 4 files
LLVM Pass的编写、编译以及加载
各自目录功能介绍
Build 文件夹:存放编译后 LLVM Pass
Test 文件夹:存放测试程序 TestProgram.cpp
Test/TestProgram.cpp:一个简单的 CTF 逆向题
1 | // Test/TestProgram.cpp |
Transforms/include 文件夹:存放整个 LLVM Pass 项目的头文件,暂时还没有用到
Transforms/src 文件夹:存放整个 LLVM Pass 项目的源代码
Transforms/src/HelloWorld.cpp:HelloWorld Pass 的源代码,一般来说一个 Pass 使用一个 cpp 文件 实现即可。
Transforms/CMakeLists.txt:整个 CMake 项目的配置文件,内容如下
1 | # 参考官方文档:https://llvm.org/docs/CMake.html#developing-llvm-passes-out-of-source |
test.sh:编译 LLVM Pass 并对 Test 文件夹中的代码进行测试,内容如下:
1 | cd ./Build |
LLVM Pass源代码模板
- 创建一个类(class),继承FunctionPass父类
- 在创建的类中实现runOnFunction(Function &F)函数
- 向LLVM注册我们的Pass类
HelloWorld.cpp
1 | #include "llvm/Pass.h" |
效果: