LLVM Study Log
0x0. 简述
刚接触LLVM的时候的记录,算是笔记吧,想从代码混淆的思路学习,学习如何写Pass,以及把自己写的Pass应用到实际的程序中。
学习笔记更新中…
0x1. LLVM
1.1 简介
LLVM是一个编译器框架,LLVM框架提供的中间表示(IR),可以作为多种语言的后端,并且根据IR可以做语言无关的优化以及
生成对应各种构架(x86,amd64,arm等)的代码。
主要分为三个部分:前端、Pass、后端
- 前端: 获取源码,转成IR。
- Pass:做各种优化工作或者一些过程的变换工作。
- 后端: 生成对应平台的机器码。
1 | Source Code ----> Frontend ----> Optimizer ----> Backend ----> Machine Code |
更多细节直接看官网
1.2 安装
安装的话直接按照官方的文档去安装就可以了.
下载源码
1 | $ cd where-you-want-llvm-to-live |
迁移出clang
1 | $ cd llvm/tools |
运行库
1 | $ cd where-you-want-llvm-to-live |
编译
1 | $ mkdir build |
就是最后编译的时候,时间会比较久,make -j x
,x给的大一点
会编译的快一点。
1.3 IR
1 | LLVM IR有三种形式,可读的文本形式(.ll),硬盘上存储的二进制形式(.bc),内存中的编译器检测和修改的形式。 |
下面编写测试代码,来看一下IR语言。
1 |
|
编译clang -emit-llvm test.cpp -S -o test.ll
得到IR
1 | ; ModuleID = 'test.cpp' |
感觉配合着官网的文档,很容易就可以读懂,语法也很清晰明了。
比如test函数
1 | define i32 @_Z4testii(i32, i32) #0 { //@是全局标识符,%是局部标识符 |
弱弱的说一句…感觉好像JAVA字节码啊
更多的内容,还是官方文档
0x2. Pass
Pass 的主要分类有以下几种。
ImmutablePass
Immutable,字面意思一成不变,即这种pass不是普通的用来转换、分析的pass,他可以提供当前编译器配置的信息。这种pass不需要运行、也不改变状态、也不需要更新。MoudlePass
从ModulePass派生表示这个pass使用整个程序作为一个单元,不可预测的顺序引用函数体,或者添加、删除函数;这种pass对子类行为并不了解,所以无法对其做优化。CallGraphSCCPass
这种需要遍历自下而上的函数调用图。FuncationPass
在每个函数上执行。这个pass再llvm的文档上有例子,那个hello world的例子。LoopPass
这种再每个循环上执行,与函数中其他的循环无关;LoopPass使用嵌套顺序处理循环,外层最后处理。RegionPass
类似LoopPass,但是在函数执行中的每个单个条目的退出区域上执行。还是嵌套顺序处理区域,即最外部的区域最后被处理。BasicBlockPass
类似FunctionPass,但是必须一次限制它们对基本块的检查和修改范围,具体的限制见文档。MachineFunctionPass
LLVM代码生成器的一部分,在程序中的每个LLVM函数的依赖于机器的表示上执行。
根据LLVM for Grad Students文章的方式去动态
使用pass。
1 |
|
CmakeList.txt文件
1 | add_library(SkeletonPass MODULE |
编译pass
1 | mkdir build |
然后就可以了
1 | # muhe @ muhe-work in ~/Code/llvm-pass-skeleton/build on git:master x [17:10:02] |
现在编写一个程序来测试这个Pass,我们的Pass可以标识出代码块和指令。
1 |
|
现在运行这个Passclang -Xclang -load -Xclang build/skeleton/libSkeletonPass.so -c test.c
得到的结果略长,如下
1 | # muhe @ muhe-work in ~/Code/llvm-pass-skeleton on git:master x [17:20:12] |
- 一些包含关系:
[Module [Function [BasicBlock [Instruction]]]]
他这篇文章后面例子还有使用Pass替换一些指令,如把add换成了mul,感兴趣可以跟着他的文章写一下。
0x3. 使用LLVM来做混淆
add替换成sub指令,即add a,b
换成了sub a,-b
1 |
|
1 | add_library(SimplePass MODULE |
我的测试代码如下
1 | $ cat example.c |
先编译正常的程序clang example.c -o before
1 | $ file before |
看一下main的逻辑:
1 | objdump -M intel -d before |
现在编译一个混淆过的
1 | mkdir build_1 |
然后clang -Xclang -load -Xclang build_1/simple/libSimplePass.so -c example.c
得到example.o
文件,这个还没链接,我们使用clang再链接一下就好了。
1 | $ clang example.o -o example |
反汇编看一下main的逻辑:
1 | 0000000000400580 <main>: |
这个只是demo而已,几乎没什么强度。