0%

OLLVM进阶

控制流扁平化

目的是展平程序的控制流程图。

测试源码:

1
2
3
4
5
6
7
8
9
#include <stdlib.h>
int main(int argc, char** argv) {
int a = atoi(argv[1]);
if(a == 0)
return 1;
else
return 10;
return 0;
}

gcc编译:

clang-4.0 -mllvm -fla -mllvm -split -mllvm -split_num=3 test01.c -o test01混淆:

大量的while构成的无限循环,可读性十分差

虚假控制流

此方法通过在当前基本块之前添加一个基本块来修改函数调用图。这个新的基本块包含一个不透明的谓词,然后有条件地跳转到原始的基本块。

原始的基本块也被克隆并填充了随机选择的垃圾指令。

不透明谓词:指的是一个表达式,它的值是确定的,但是对于分析者,程序不执行到特定位置是个很难知道真假的。静态分析器也无法推断出这个值。

(9 封私信 / 80 条消息) 利用不透明谓词混淆代码的原理是什么? - 知乎 (zhihu.com)

测试源代码:

1
2
3
4
5
6
7
8
9
#include <stdlib.h>
int main(int argc, char** argv) {
int a = atoi(argv[1]);
if(a == 0)
return 1;
else
return 10;
return 0;
}

gcc直接编译得到:

流程图:

clang -mllvm -bcf选项处理编译:

流程图:

这里的不透明谓词是

1
( y >= 10 && (((x - 1) * x) & 1) != 0 )

((x - 1) * x)奇数乘偶数,得到的必然是偶数,偶数二进制形式最低位必然为0,所以不满足不等于0,&&的右侧表达式结果必然为假,无论y取何值,do while都不会执行第二次。

指令替换

可以实现加法、减法、和、或、异或的替换。也就是说可以把简单的运算用一种复杂的方法实现。

源码:

先用gcc编译得到一个参考对照程序,就是c=a+b

经过**-sub**选项处理

ecx存放5,edx存放3,esi存放0,esi=esi-ecx (-5),ecx存放0,ecx=ecx-edx(-3),esi=esi+ecx,(-8)eax=eax-esi(8),实现的效果就是5+3–>-((-5)+(-3))

-mllvm -sub_loop=x

使用该选项可以实现指定处理的次数x。

下一篇文章介绍如何对抗ollvm混淆。

参考:Control Flow Flattening · obfuscator-llvm/obfuscator Wiki · GitHub