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 逆向題
// Test/TestProgram.cpp
#include <cstdio>
#include <cstring>
char input[100] = {0};
char enc[100] = "\x86\x8a\x7d\x87\x93\x8b\x4d\x81\x80\x8a\x43\x7f\x49\x49\x86\x71\x7f\x62\x53\x69\x28\x9d";
void encrypt(unsigned char *dest, char *src) {
int len = strlen(src);
for (int i = 0; i < len; i ++) {
dest[i] = (src[i] + (32 - i)) ^ i;
}
}
int main() {
printf("Please input your flag: ");
scanf("%s", input);
unsigned char dest[100] = {0};
encrypt(dest, input);
bool result = strlen(input) == 22 && !memcmp(dest, enc, 22);
if (result) {
printf("Congratulations~\n");
} else {
printf("Sorry try again.\n");
}
}
Transforms/include 文件夾:存放整個 LLVM Pass 項目的頭文件,暫時還沒有用到
Transforms/src 文件夾:存放整個 LLVM Pass 項目的源代碼
Transforms/src/HelloWorld.cpp:HelloWorld Pass 的源代碼,一般來說一個 Pass 使用一個 cpp 文件 實現即可。
Transforms/CMakeLists.txt:整個 CMake 項目的配置文件,內容如下
# 參考官方文檔:https://llvm.org/docs/CMake.html#developing-llvm-passes-out-of-source
project(OLLVM++)
cmake_minimum_required(VERSION 3.13.4)
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories("./include") # 包含 ./include 文件夾中的頭文件
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_llvm_library(
LLVMObfuscator MODULE
src/HelloWorld.cpp
)
test.sh:編譯 LLVM Pass 並對 Test 文件夾中的代碼進行測試,內容如下:
cd ./Build
cmake ../Transforms
make
cd ../Test
clang -S -emit-llvm TestProgram.cpp -o TestProgram.ll
opt -load ../Build/LLVMObfuscator.so -hlw -S TestProgram.ll -o TestProgram_hlw.ll
clang TestProgram_hlw.ll -o TestProgram_hlw
./TestProgram_hlw
LLVM Pass源代碼模板
- 創建一個類(class),繼承FunctionPass父類
- 在創建的類中實現runOnFunction(Function &F)函數
- 向LLVM註冊我們的Pass類
HelloWorld.cpp
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace{
class HelloWorld : public FunctionPass { // 繼承Pass的FunctionPass
public:
static char ID;
HelloWorld() : FunctionPass(ID) {
// 構造函數
}
bool runOnFunction(Function &F); // runOnFunction實現
};
}
bool HelloWorld::runOnFunction(Function &F) {
// todo
outs() << "Hello, " << F.getName() << "\n";
}
char HelloWorld::ID = 0;
static RegisterPass<HelloWorld> X("hlw", "Pass 描述.");
效果: