LLVM從小白到放棄(二)- LLVM Pass

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 描述.");

效果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章