LLVM IR研究分析
前置知识
LLVM是C++编写的构架编译器的框架系统,可用于优化以任意程序语言编写的程序。
LLVM IR可以理解为LLVM平台的汇编语言,所以官方也是以语言参考手册(Language Reference Manual))的形式给出LLVM IR的文档说
明。既然是汇编语言,那么就和传统的CUP类似,有特定的汇编指令集。但是它又与传统的特定平台相关的指令集(x86,ARM,RISC-V等)
不一样,它定位为平台无关的汇编语言。也就是说,LLVM IR是一种相对于CUP指令集高级,但是又是一种低级的代码中间表示(比抽象语法树等高级表示更加低级)。
LLVM IR即代码的中间表示,有三种形式:
- .ll 格式:人类可以阅读的文本(汇编码) –>这个就是我们要学习的IR
- .bc 格式:适合机器存储的二进制文件
- 内存表示
下面给出.ll格式和.bc格式生成及相互转换的常用指令清单:
1 | .c -> .ll:clang -emit-llvm -S a.c -o a.ll |
那么我们以一道CTF赛题来分析实验,学习LLVM IR
实验解析
题目附件直接给出了中间表示.II文件
打开查看一下汇编码,毕竟.II文件是人类可以阅读的文本,这边笔者使用的是Sublime Text(使用VScode查看即可)
代码量不多,大概600行
题目初步分析
我们直接寻找一下main函数
我们可以看出题目经历了两次RC4,然后Base64,我们从上面可以看到密文,RC4_key,我们直接一把锁,cyberchef启动,会发现解不出来,那么程序应该做了其他的操作,最朴素的,我们可以想到把RC4魔改了,base64魔改等等。
So!继续学习研究ing
.II详细分析
所以本着学习的态度,我们这时候应该掏出LLVM Language Reference Manual(官方文档)来简单了解学习一些常见指令、符号标识以及特性。
这边给出一些分析 .ll 中间文件的算法流程
1 | @ - 全局变量 |
首先看到一些全局变量,知道了
RC4_key = llvmbitc
cipher = “TSzkWKgbMHszXaj
@kLBmRrnTxsNtZsSOtZzqYikCw=”
我们继续分析,重点分析各个function
b64encode
b64encode 魔改
- 每三个字符,24位,切分成4断,每段6位。
- 将6位对应的值 (value+ 59)&0xff 则是编码后的值。
1 | %22 = getelementptr inbounds i8, i8* %19, i64 %21 // 取出当前处理字符 |
RC4_init
RC4_init 正常,无魔改
1 | define dso_local void @Rc4_Init(i8*, i32) #0 { //RC4_init function |
RC4_enc
RC4_enc 魔改 多了一层xor 89
1 | define dso_local void @Rc4_Encrypt(i8*, i32) #0 { //RC4_enc function |
main
main函数逻辑
cipher –>RC4_init–>RC4_enc–>RC4_enc–>b64encode
需要注意一下在RC4_enc的参数中,传入的数据块长度是固定的16,所以说程序进行两次RC4_enc的原因也就确定了,是为了分两次对程序进行加密,也算是一点点小手段,总之,即使让你好好分析.II代码,考察对软件分析的细节,耐心,嘻嘻。
OK,理清楚逻辑,就可以试着敲代码解密啦。
解密
逆向分析过程明了之后,那么写代码就简单多了
1 |
|
flag{Hacking_for_fun@reverser$!}
总结
通过这么一道CTF题目,深入学习LLVM IR的冰山一角,认真实验,细细分析,相信会对你有极大帮助。
当然,如果单从解题来说,对于解决这道题有很多的办法,比如说将.II转化为可执行文件,然后IDA分析,但我们旨在学习LLVM IR,这里不再过多赘述。