OLLVM移植和使用

ollvm是一个基于llvm的开源项目,利用llvm会生成IR中间代码并通过pass进行优化的特点,通过增加Pass来对代码进行优化.但这种优化是为了让代码更加复杂,达到混淆的目的.主要有以下3种混淆手段:

  1. 指令替换(Substitution)
  2. 虚假控制流(Bogus Control Flow)
  3. 控制流平坦化(Control Flow Flattening)

高版本移植

ollvm目前官方版本是基于llvm4.0,现在我们将其移植到llvm9.0.1

  1. 下载官方的ollvm最新版
1
git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
  1. 复制原版ollvm,{obfuscator}/include/llvm/Transforms/Obfuscation文件夹到llvm9.0.1同等目录下

ollvm头文件

  1. 将CryptoUtils.h复制到llvm9.0.1的同等目录下

头文件移植

  1. 复制原版ollvm,{obfuscator}/lib/llvm/Transforms/Obfuscation文件夹到llvm9.0.1同等目录下.

ollvm实现文件

  1. 按照官方提交的commit记录来修改CMakeLists.txt以及LLVMBuild.txt文件.

C文件修改

C文件修改2

C文件修改3

  1. 按照ollvm最新的4.0版本,进行llvm9.0.1的{llvm-project}/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp文件修改

可以看下方官方提交的记录来进行修改,绿色的就是新增的.

https://github.com/obfuscator-llvm/obfuscator/commit/adbe45b199d4e42400ee646ad62a781f34b07860#diff-243d2d7bcf5596188ce66141702e60b552ed8af141b9c2b9d0209fb8e9c81fac

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//PassManagerBuilder.cpp
#include "llvm/Transforms/Obfuscation/BogusControlFlow.h"
#include "llvm/Transforms/Obfuscation/Flattening.h"
#include "llvm/Transforms/Obfuscation/Split.h"
#include "llvm/Transforms/Obfuscation/Substitution.h"
#include "llvm/CryptoUtils.h"


// Flags for obfuscation
static cl::opt<bool> Flattening("fla", cl::init(false),
                                cl::desc("Enable the flattening pass"));

static cl::opt<bool> BogusControlFlow("bcf", cl::init(false),
                                      cl::desc("Enable bogus control flow"));

static cl::opt<bool> Substitution("sub", cl::init(false),
                                  cl::desc("Enable instruction substitutions"));

static cl::opt<std::string> AesSeed("aesSeed", cl::init(""),
                                    cl::desc("seed for the AES-CTR PRNG"));

static cl::opt<bool> Split("spli", cl::init(false),
                           cl::desc("Enable basic block splitting"));

PassManagerBuilder::PassManagerBuilder() {
 
	//------------------------------------------
    // Initialization of the global cryptographically
    // secure pseudo-random generator
    if(!AesSeed.empty()) {
      llvm::cryptoutils->prng_seed(AesSeed.c_str());
    }
}

void PassManagerBuilder::populateModulePassManager(
    legacy::PassManagerBase &MPM) {
      MPM.add(createForceFunctionAttrsLegacyPass());

  //--------------
  MPM.add(createSplitBasicBlock(Split));
  MPM.add(createBogus(BogusControlFlow));
  //注意,这个地方跟官方版本不一样,进行了修改
  if (Flattening){
    MPM.add(createLowerSwitchPass());
  }
  MPM.add(createFlattening(Flattening));
  MPM.add(createSubstitution(Substitution));
  //--------------
}
  1. 按照下方的链接,进行bcf bug修复.

https://github.com/obfuscator-llvm/obfuscator/pull/76/files

  1. 此时进行编译,会出错,我们继续进行修改.

注释掉下方的2行.然后在PassManagerBuilder.cpp文件中进行修改.(上方已修改过)

bug1

还有一处是返回类型的修改,如下:

1
2
3
4
5
6
7
8
9
//{llvm-project}/llvm/lib/Transforms/Obfuscation/BogusControlFlow.cpp
bool doF(Module &M){
    //------
    //修改前
	TerminatorInst * tbb= fi->getTerminator();
	//修改后
	Instruction * tbb= fi->getTerminator();
    //------
}
  1. 此时进行编译即可.
1
2
ninja LLVMObfuscation
ninja clang

ollvm使用

下面按照官方给的例子,来进行ollvm特性的学习.

https://github.com/obfuscator-llvm/obfuscator/wiki/Features

指令替换(sub)

原理:将一条运算指令,替换为多条等价运算指令.

测试代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//luoTst.c
#include <stdio.h>
int main(int argc, char** argv) {
  int n = argc + 2;
  if(n >= 0){
    printf("hello ollvm\r\n");
  }
  else{
    printf("hello World\r\n");
  }
  return 0;
}

使用下述命令进行编译,会等价替换1次

1
clang -mllvm -sub luoTst.c -o luoTst_sub

等价替换1

使用下述命令进行编译,会等价替换3次

1
clang -mllvm -sub -mllvm -sub_loop=3 luoTst.c -o luoTst_sub3

等价替换2

虚假控制流(bcf)

原理:克隆一个真实块,并随机替换其中的一些指令,然后用一个永远为真的条件建立一个分支,克隆后的块是不会被执行的.

测试代码如下:

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

上述代码转成中间语言表示如下:

虚假控制流2

经过虚假控制流Pass处理后,流程可能如下:

虚假控制流3

这里我们使用如下命令使用进行编译,在IDA中看下代码.

1
clang -mllvm -bcf luoTst.c -o luoTst_bcf

虚假控制流1

控制流平坦化(fla)

原理:先实现一个永真循环,然后在这个循环中放入switch语句,将代码中除了开始块的所有BasicBlock放入这个switch语句的不同case中,通过修改switch的条件,来实现BasicBlock之间的跳转.

测试代码如下:

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

上述代码经过控制流平坦化Pass处理后,代码会转成如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdlib.h>
int main(int argc, char** argv) {
  int a = atoi(argv[1]);
  int b = 0;
  while(1) {
    switch(b) {
      case 0:
        if(a == 0)
          b = 1;
        else
          b = 2;
        break;
      case 1:
        return 1;
      case 2:
        return 10;
      default:
        break;
    }
  }
  return 0;
}

未经过处理前的流程如下:

控制流平坦化1

经过处理后的流程如下:

控制流平坦化2

接下来我们使用如下命令进行编译,在IDA中体会下:

1
clang -mllvm -fla luoTst.c -o luoTst_fla

控制流平坦化3

控制流平坦化4

上面的编译选项比较简单,生成的代码,从IDA反汇编结果来看好像还可以,下面使用如下命令进行编译,来体会下控制流平坦化的强大.

1
clang -mllvm -fla -mllvm -split -mllvm -split_num=10 luoTst.c -o luoTst_fla_split10

控制流平坦化5

函数注解

可以使用注解的方式对指定函数进行指定的混淆方式.

测试代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//luoTst.c 
#include <stdlib.h>
#include <stdio.h>
int fun1(int a, int b) __attribute((__annotate__(("fla"))));
int fun2(int a, int b) __attribute((__annotate__(("nofla"))));
int main(int argc, char** argv) __attribute((__annotate__(("bcf"))));

int fun1(int a, int b){
  if(a + b > 10){
    return 10;
  }else{
    return 100;
  }
}

int fun2(int a, int b){
   if(a - b > 10){
    return 5;
  }else{
    return 50;
  }
}

int main(int argc, char** argv) {
  if(argc > 2){
    printf("hello world\n");
  }else{
    printf("hello ollvm\n");
  }
  printf("fun1:%d\n", fun1(argc, 6));
  printf("fun2:%d\n", fun2(argc, 8));
  return 0;
}

使用如下命令进行编译即可:

1
clang luoTst.c -o luoTst

参考链接

obfuscator


相关内容

0%