LLVM PASS PWN(一)
字数 1306 2025-08-26 22:11:35

LLVM PASS PWN 入门教程

1. 前置知识

1.1 为什么要编译程序

  • 机器语言是计算机唯一能识别和执行的语言(1和0表示高低电平)
  • 汇编语言用助记符表示机器指令(如mov a, 1
  • 高级语言更接近人类自然语言(如a = 1
  • 编译的根本目的:将高级语言翻译成机器能识别的目标代码

1.2 编译过程

前端:
  词法分析 → 语法分析 → 语义分析 → 生成中间代码(IR)

后端:
  优化中间代码 → 生成目标平台机器码
  • 词法分析:识别源代码中的Token(如int, return, {, }等)
  • 语法分析:将Token串转换为抽象语法树(AST)
  • 语义分析:理解语句的语义(如for循环、if判断等)

2. LLVM基础

2.1 LLVM设计理念

  • 解决传统编译器(如GCC)前后端耦合问题
  • 统一中间代码(IR)设计:
    • 新编程语言只需实现新前端
    • 新平台只需实现新后端

2.2 LLVM IR的三种表示形式

  1. 可读IR(.ll文件):类似汇编代码,人类可读
  2. 二进制IR(.bc文件):不可读二进制格式
  3. 内存格式:保存在内存中的表示

2.3 LLVM Pass

  • LLVM框架的核心组件,负责编译器的转换和优化工作
  • 主要用途:
    • 插桩
    • 机器无关的代码优化
    • 静态分析
    • 代码混淆

2.4 LLVM工具链

工具 功能
llvm-as 将.ll文本IR汇编为.bc二进制IR
llvm-dis 将.bc反汇编为.ll
opt 优化LLVM IR
llc 将LLVM IR编译为汇编码
lli 解释执行LLVM IR

2.5 Clang

  • LLVM的前端,支持C/C++/Objective-C等语言
  • 功能包括:词法分析、语法分析、语义分析、生成LLVM IR

3. 环境搭建

Ubuntu 20.04安装命令:

sudo apt install clang-12 clang-8 llvm-12 llvm-8

4. LLVM IR生成示例

测试C代码(test.c):

#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv){
    char name[0x20];
    puts("hello world");
    puts("plz input your name");
    read(0, name, 0x1F);
    printf("biubiubiu");
    return 0;
}

转换命令:

# .c → .ll
clang-12 -emit-llvm -S test.c -o test.ll

# .c → .bc
clang-12 -emit-llvm -c test.c -o test.bc

# .ll → .bc
llvm-as test.ll -o test.bc

# .bc → .ll
llvm-dis test.bc -o test.ll

# .bc → .s
llc test.bc -o test.s

5. 编写第一个LLVM Pass

示例代码(Hello.cpp):

#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;

namespace {
struct Hello : public FunctionPass {
    static char ID;
    Hello() : FunctionPass(ID) {}
    
    bool runOnFunction(Function &F) override {
        errs() << "Hello: ";
        errs().write_escaped(F.getName()) << '\n';
        return false;
    }
};
} // namespace

char Hello::ID = 0;

static RegisterPass<Hello> X("hello", "Hello World Pass", 
                           false /* Only looks at CFG */, 
                           false /* Analysis Pass */);

static RegisterStandardPasses Y(
    PassManagerBuilder::EP_EarlyAsPossible,
    [](const PassManagerBuilder &Builder,
       legacy::PassManagerBase &PM) { PM.add(new Hello()); });

关键点解析:

  1. 继承FunctionPass类,实现对单个函数的操作
  2. 必须声明runOnFunction方法,覆盖父类虚方法
  3. 需要定义pass标识符ID
  4. 通过RegisterPass注册pass,指定命令行参数和名称
  5. 可选:通过RegisterStandardPasses将pass插入现有优化管道

编译命令:

clang-12 `llvm-config --cxxflags` -Wl,-znodelete -fno-rtti -fPIC -shared Hello.cpp -o LLVMHello.so `llvm-config --ldflags`

运行Pass:

opt -load ./LLVMHello.so -hello test.ll

6. LLVM Pass逆向分析

关键逆向点:

  1. 虚表位置是分析重点,runOnFunction通常位于虚表末尾
  2. 比赛题目通常会重写runOnFunction函数
  3. 通过RegisterPass调用链可以找到对象创建过程

逆向技巧:

  • 查找callDefaultCtor调用,这是对象创建的起点
  • 关注虚表结构,特别是最后一个函数指针
  • 分析runOnFunction的具体实现,通常是漏洞所在

7. 总结

学习路径:

  1. 理解编译原理基础
  2. 掌握LLVM架构和IR格式
  3. 学习编写基本Pass
  4. 分析Pass的二进制实现
  5. 研究漏洞利用技术

后续方向:

  • 更复杂的Pass编写
  • IR优化技术
  • Pass漏洞挖掘和利用
  • 混淆与反混淆技术
LLVM PASS PWN 入门教程 1. 前置知识 1.1 为什么要编译程序 机器语言是计算机唯一能识别和执行的语言(1和0表示高低电平) 汇编语言用助记符表示机器指令(如 mov a, 1 ) 高级语言更接近人类自然语言(如 a = 1 ) 编译的根本目的:将高级语言翻译成机器能识别的目标代码 1.2 编译过程 词法分析:识别源代码中的Token(如int, return, {, }等) 语法分析:将Token串转换为抽象语法树(AST) 语义分析:理解语句的语义(如for循环、if判断等) 2. LLVM基础 2.1 LLVM设计理念 解决传统编译器(如GCC)前后端耦合问题 统一中间代码(IR)设计: 新编程语言只需实现新前端 新平台只需实现新后端 2.2 LLVM IR的三种表示形式 可读IR(.ll文件):类似汇编代码,人类可读 二进制IR(.bc文件):不可读二进制格式 内存格式:保存在内存中的表示 2.3 LLVM Pass LLVM框架的核心组件,负责编译器的转换和优化工作 主要用途: 插桩 机器无关的代码优化 静态分析 代码混淆 2.4 LLVM工具链 | 工具 | 功能 | |------|------| | llvm-as | 将.ll文本IR汇编为.bc二进制IR | | llvm-dis | 将.bc反汇编为.ll | | opt | 优化LLVM IR | | llc | 将LLVM IR编译为汇编码 | | lli | 解释执行LLVM IR | 2.5 Clang LLVM的前端,支持C/C++/Objective-C等语言 功能包括:词法分析、语法分析、语义分析、生成LLVM IR 3. 环境搭建 Ubuntu 20.04安装命令: 4. LLVM IR生成示例 测试C代码(test.c): 转换命令: 5. 编写第一个LLVM Pass 示例代码(Hello.cpp): 关键点解析: 继承 FunctionPass 类,实现对单个函数的操作 必须声明 runOnFunction 方法,覆盖父类虚方法 需要定义pass标识符 ID 通过 RegisterPass 注册pass,指定命令行参数和名称 可选:通过 RegisterStandardPasses 将pass插入现有优化管道 编译命令: 运行Pass: 6. LLVM Pass逆向分析 关键逆向点: 虚表位置是分析重点, runOnFunction 通常位于虚表末尾 比赛题目通常会重写 runOnFunction 函数 通过 RegisterPass 调用链可以找到对象创建过程 逆向技巧: 查找 callDefaultCtor 调用,这是对象创建的起点 关注虚表结构,特别是最后一个函数指针 分析 runOnFunction 的具体实现,通常是漏洞所在 7. 总结 学习路径: 理解编译原理基础 掌握LLVM架构和IR格式 学习编写基本Pass 分析Pass的二进制实现 研究漏洞利用技术 后续方向: 更复杂的Pass编写 IR优化技术 Pass漏洞挖掘和利用 混淆与反混淆技术