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的三种表示形式
- 可读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安装命令:
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()); });
关键点解析:
- 继承
FunctionPass类,实现对单个函数的操作 - 必须声明
runOnFunction方法,覆盖父类虚方法 - 需要定义pass标识符
ID - 通过
RegisterPass注册pass,指定命令行参数和名称 - 可选:通过
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逆向分析
关键逆向点:
- 虚表位置是分析重点,
runOnFunction通常位于虚表末尾 - 比赛题目通常会重写
runOnFunction函数 - 通过
RegisterPass调用链可以找到对象创建过程
逆向技巧:
- 查找
callDefaultCtor调用,这是对象创建的起点 - 关注虚表结构,特别是最后一个函数指针
- 分析
runOnFunction的具体实现,通常是漏洞所在
7. 总结
学习路径:
- 理解编译原理基础
- 掌握LLVM架构和IR格式
- 学习编写基本Pass
- 分析Pass的二进制实现
- 研究漏洞利用技术
后续方向:
- 更复杂的Pass编写
- IR优化技术
- Pass漏洞挖掘和利用
- 混淆与反混淆技术