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

    效果:

  • 相关阅读:
    拦截器和过滤器区别
    sql语句查询出数据重复,取唯一数据
    bootstrap ace treeview树表
    bootstrap 时间选择器 datetime
    ajax请求后加额外的数据
    使用ajax请求,模态框调用并更改密码
    ajax get和post请求 后端接收并返回数据
    类的访问级别
    继承与组合
    类型转换函数
  • 原文地址:https://www.cnblogs.com/Tu9oh0st/p/16355503.html
Copyright © 2020-2023  润新知