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

image-20220706151310564

image-20220706151325543

因为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文件

image-20220706152104662

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"

image-20220707142910600

最后执行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

image-20220707155342174

如果提示stdio.h头文件找不到可以尝试下载g++和gcc,如果提示stddef.h或者stdarg.h头文件找不到可以sudo find -L /usr -name "*stddef*" -type f将其复制到 /usr/include目录下

image-20220707155253075

虚假控制流(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

image-20220707160139661

指令替换(Instruction Substitution)

可用选项:

  • -mllvm -sub : 激活指令替代
  • -mllvm -sub_loop=3 : 混淆次数,这里一个函数会被混淆3次,默认为 1次
clang -mllvm -sub -mllvm -sub_loop=3 hello.cpp -o hello_sub

image-20220707160516874

通过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/

https://security.tencent.com/index.php/blog/msg/112

https://github.com/joydo/d810