OLLVM混淆学习(0)——环境搭建及混淆初体验
0x01 LLVM环境搭建
该小节环境搭建和原版OLLVM混淆使用无关,仅为后续LLVM PASS编写做准备,如仅需要进行OLLVM混淆该小节可跳过
我选择的环境为Ubuntu 18.04、LLVM 12.0.1、CMake 3.16.6
先下载LLVM和Clang的源码并利用CMake进行源码编译
https://github.com/llvm/llvm-project/releases/tag/llvmorg-12.0.1
因为Ubuntu 18.04利用包管理器默认安装的CMake最高版本达不到LLVM编译需求,所以我们需要自行安装,具体命令如下:
wget http://www.cmake.org/files/v3.16/cmake-3.16.6.tar.gz
tar xf cmake-3.16.6.tar.gz
cd cmake-3.16.6
sudo apt-get install build-essential
sudo apt-get install libssl-dev
sudo chmod -R 777 cmake-3.16.6
./bootstrap
make
sudo make install
cmake --version
然后如下图创建build、llvm、clang目录,将llvm和clang对应源码放入目录中,后面来编写build.sh文件
cd build
cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang" \
-DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" \
-DBUILD_SHARED_LIBS=On ../llvm
make
make install
最后给予build.sh执行权限并执行等待一段时间后就编译好了,可以通过clang --version进行验证是否编译成功,在编译的时候最好给虚拟机大一点的运行内存避免发生未知意外
LLVM编译基本命令
LLVM IR 有两种表现形式,一种是人类可阅读的文本形式,对应文件后缀为 .ll ;另一种是方便机器处理的二进制格式,对应文件后缀为 .bc 。
clang -S -emit-llvm hello.cpp -o hello.ll
clang -c -emit-llvm hello.cpp -o hello.bc
使用 opt 指令对 LLVM IR 进行优化
opt -load LLVMObfuscator.so -hlw -S hello.ll -o hello_opt.ll
-load 加载特定的 LLVM Pass (集合)进行优化(通常为.so文件)
-hlw 是 LLVM Pass 中自定义的参数,用来指定使用哪个 Pass 进行优化
从 LLVM IR 到可执行文件中间还有一系列复杂的流程,Clang 帮助我们整合了这个过程:
clang hello_opt.ll -o hello
0x02 Obfuscator-LLVM环境搭建
为避免出现冲突,这一节我是专门拿了个新的Ubuntu虚拟机进行操作的
使用 nickdiego/ollvm-build这个docker环境进行OLLVM源码编译,docker的安装参考网络上教程即可
docker pull nickdiego/ollvm-build
然后将源码下载下来
git clone https://github.com/nickdiego/docker-ollvm.git
git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
在ollvm-build.sh的第150行添加DOCKER_CMD+=" -DLLVM_INCLUDE_TESTS=OFF"
最后执行ollvm-build.sh
chmod 777 ollvm-build.sh
sudo ./ollvm-build.sh ../obfuscator
编译完成后在 obfuscator/build_release 目录执行指令创建硬链接
sudo ln ./bin/* /usr/bin/
clang --version
OLLVM基本用法
使用的demo(hello.cpp)
#include <stdio.h>
#include <stdlib.h>
int encryptFunc(int inputNum_1,int inputNum_2){
int tmpNum_1 = 666, tmpNum_2 = 888, tmpNum_3 = 777;
return tmpNum_1 ^ tmpNum_2 + tmpNum_3 * inputNum_1 - inputNum_2;
}
int main(int argc,char *argv[]){
int printNum = 55;
if (argc > 1)
{
printNum = encryptFunc(printNum, atoi(argv[1]));
}else{
printNum = encryptFunc(printNum, argc);
}
printf("Hello OLLVM %d\r\n", printNum);
return 0;
}
控制流平坦化(Control Flow Flattening)
可用选项:
- -mllvm -fla : 激活控制流平坦化
- -mllvm -split : 激活基本块分割
- -mllvm -split_num=3 : 指定基本块分割的数目
clang -mllvm -fla -mllvm -split -mllvm -split_num=3 hello.cpp -o hello_fla
如果提示stdio.h头文件找不到可以尝试下载g++和gcc,如果提示stddef.h或者stdarg.h头文件找不到可以sudo find -L /usr -name "*stddef*" -type f将其复制到 /usr/include目录下
虚假控制流(Bogus Control Flow)
可用选项:
- -mllvm -bcf : 激活虚假控制流
- -mllvm -bcf_loop=3 : 混淆次数,这里一个函数会被混淆3次,默认为 1
- -mllvm -bcf_prob=40 : 每个基本块被混淆的概率,这里每个基本块被混淆的概率为40%,默认为 30 %
clang -mllvm -bcf -mllvm -bcf_loop=3 -mllvm -bcf_prob=40 hello.cpp -o hello_bcf
指令替换(Instruction Substitution)
可用选项:
- -mllvm -sub : 激活指令替代
- -mllvm -sub_loop=3 : 混淆次数,这里一个函数会被混淆3次,默认为 1次
clang -mllvm -sub -mllvm -sub_loop=3 hello.cpp -o hello_sub
通过LLVM IR生成多平台可执行文件,以控制流平坦化为例
clang -mllvm -fla -mllvm -split -mllvm -split_num=3 -S -emit-llvm hello.cpp -o hello_fla.ll
# 切换至Windows(提前安装好clang)
clang hello_fla.ll -o hello_fla.exe
0x03 去混淆
可以参考TSRC的那篇文章使用符号执行脚本deflat的方式进行去混淆,对于指令替换可以使用D810这个IDA插件进行操作,详细的去混淆方法后续文章在深入讨论。
参考链接
https://www.cnblogs.com/jsdy/p/12689470.html
https://www.kanxue.com/book-section_list-88.htm
https://blog.wuxu92.com/stdargs.h-no-such-file-or-directory/