OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,以增加对逆向工程的难度。后期转向商业项目strong.protect。目前,OLLVM已经支持LLVM-4.0版本。

本文主要记录了 OLLVM 的编译,使用流程。

介绍

obfuscator 在 2010年由 HEIG-VD 发起,为 LLVM 提供了代码混淆编译组件。Obfuscator 作用在 LLVM 的 IR 层,因此,它和其他所有可经由 LLVM 编译的语言都兼容,比如 C,C++, OC, Ada 等,并且也支持所有的平台(x86, x86-64, PowerPC, PowerPC-64, ARM, Thumb, SPARC, Alpha, CellSPU, MIPS, MSP430, SystemZ, and XCore)。

编译

obfuscator 有多个分支,我们选 llvm-4.0, 它基于 LLVM 官方版本 4.0.1。

编译 obfuscator 需要安装 CMake,克隆代码需要 git, 读者自行补齐。

只需要执行如下系列命令:

1
2
3
4
5
6
$ git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
$ cd obfuscator
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make -j7

大概需要个个把小时,就会有结果。编译结果的二进制文件在 obfuscator/build/bin 目录下,比如 clang 命令。

使用

假设我们有源文件 cff.c, 路径位于相对于源码根目录 ./obfuscator 下:

1
2
3
4
5
6
7
8
int main(int argc, char** argv) {
int a = 0;
if(a == 0)
return 1;
else
return 10;
return 0;
}

我们先用系统自带的 clang 编译,产生结果文件(在 cff.c 源码路径):
$ clang cff.c -o original

然后我们使用自己编译的 clang + ‘Control Flow Flattening’ 模式混淆。

在编译结果 bin 路径执行如下命令:
./clang-4.0 ../../obfuscator/cff.c -o ../../obfuscator/obs -mllvm -fla

然后我们用 Hopper 分别打开 original 和 obs 两个对象文件:

对比发现,经过 cfa 混淆后,原本程序的控制流被打破,增加了阅读门槛。

几种混淆模式

指令替换模式(Instructions Substitution)

本质上指令替换就是用等价的更加复杂的指令替换原本可读性更好的指令。比如,加减以及布尔指令。

编译时,使用 -mllvm -sub 参数即可。

控制流平展模式(Control Flow Flattening)

该模式改变原本程序的控制流。
例如在 _使用_ 示例,原本程序的控制流如下:

经过控制流平展后,控制流变成:

编译时使用 -mllvm -fla 参数。

控制流伪造模式 (Bogus Control Flow)

也是对程序的控制流做操作,不同的是,BCF模式会在原代码块的前后随机插入新的代码块,新插入的代码块不是确定的,然后新代码块再通过条件判断跳转到原代码块中。更要命地是,原代码块可能会被克隆并插入随机的垃圾指令。

编译时加 -mllvm -bcf 参数即可。

字符串混淆

注意该模式 obfuscator 没有实现,这里只是提及一下,也是一种常见的混淆模式。混淆后的字符串没办法直接搜索到,变成一系列操作后的合成产物,提高了反编译成本。

注解

有的时候,由于效率或其他原因的考虑,我们只想给指定的函数混淆或不混淆该函数,OLLVM也提供了对这一特性的支持,你只需要给对应的函数添加attributes即可。比如,想对函数foo()使用fla混淆,只需要给函数foo()增加fla属性即可。

例如:

1
2
3
4
int foo() __attribute((__annotate__(("fla"))));
int foo() {
return 2;
}

参考

  1. https://www.jianshu.com/p/a631b5584de6

  2. https://github.com/obfuscator-llvm/obfuscator/wiki/Functions-annotations