LLVM-Obfuscator简析

LLVM的全称是Low Level Virtual Machine,翻译过来就是底层虚拟机,但事实上LLVM在现阶段已经与虚拟机关系不大,而是作为一套完整的编译链工具或者称为编译框架应用在各大软件领域。而在二进制安全领域,我们常常会用到LLVM Obfuscator进行软件保护。

LLVVM

LLVM的前端可以使用不同的编译工具得到代码的AST(抽象语法树),然后转化成LLVM的IR((Intermediate Representation,即中间表示层,也称中间语言)),不同的前端语言得到的IR都是相同的,LLVM的中间优化器(Pass)会对IR依次进行优化操作,通常包括优化运行时间等,然后LLVM后端将优化好的IR处理成目标平台的二进制代码,完成整个LLVM流程。

PASS

LLVM Obfuscator是瑞士西北应用科技大学于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,是基于LLVM实现的开源代码混淆器,可以进行多元化自定义的软件代码混淆保护。

LLVM Obfuscator的工作在IR层完成,通过编写Pass来对IR进行混淆。得益于LLVM的多支持特性,所以LLVM Obfuscator支持C、C++、Objective-C等多种语言,而且支持x86、x86-64、ARM、MIPS等多种目标平台。

0x02 一些基本概念

基本代码块

基本代码块(Basic Block)指一串顺序执行的指令,其中只有一个入口和一个出口,通常就是指的没有跳转指令的一串指令,如果遇到条件分支就进入一个新的基本代码块。

有限状态机

状态存储关于过去的信息,就是说:它反映从系统开始到现在时刻的输入变化。转移指示状态变更,并且用必须满足确使转移发生的条件来描述它。动作是在给定时刻要进行的活动的描述。有多种类型的动作:

  • 进入动作(entry action):在进入状态时进行

  • 退出动作:在退出状态时进行

  • 输入动作:依赖于当前状态和输入条件进行

  • 转移动作:在进行特定转移时进行

有限状态机下一个状态和输出是由输入和当前状态决定的。

FSM

0x03 LLVM Obfuscator功能

目前LLVM Obfuscator包含了三个独立的LLVM Pass,每个Pass实现了一种混淆方式。

1.指令变换
2.虚假控制流
3.控制流扁平化

指令变换

指令变换的实现方式是用等效但指令序列更为复杂的等式替代标准的二元运算符,通常是对加法、减法、逻辑运算(或、异或、和)进行等值替换。在编译器选项中用-mllvm -sub开启指令变换。

具体实现示例:

a = b + c => a = b - (-c)

a = -(-b + (-c))

r = rand (); a = b + r; a = a + c; a = a - r

a ^ b => a = (~a & b) | (a & ~b)

b & c => a = (b ^ ~c) & b

虚假控制流

虚假控制流通过在即将执行的基本代码块之前添加一个新的基本块来改变控制流,这个新添加的基本代码块通常包括一些随机的运算和存储,然后进行条件跳转到正确的基本代码块执行正确的指令。在编译器选项中使用-mllvm -bcf命令开启。

由于虚假控制流的随机性,一份代码进行多次虚假控制流操作得到的结果可能是完全不同的,而且对效率的影响也较大,但效果也极为明显。

控制流扁平化

控制流扁平化的目的是让原程序的基本代码块平行化,使其不具备清晰的逻辑框架,从类似if…else的结构变成类似switch…case的结构 ,增加逆向分析的难度。该功能的基本流程如下:

1.搜寻所有的基本代码块。

2.将所有的基本代码块平行放置于控制流最底部,并删除原来的跳转关系。

3.添加混淆器的流程控制分发逻辑,添加新的复杂的粘合代码还原原来代码块的逻辑关系。

添加混淆器的控制流程基本思路:对原来的每个基本代码块添加一个state_var作为状态值,混淆框架根据这个状态值,来决定控制流执行的下一个基本代码块,类似状态机。该功能通过-mllvm -fla开启。

0x04 参考资料

LLVM代码混淆分析及逻辑还原

Obfuscator-LLVM在iOS中的实践

LLVM Obfuscator 官方Wiki