OLLVM研究之FLA

概述

FLA即控制流平坦化,首先将原程序中switch结构转换成链式的平坦结构,然后创建loopEntry和loopEnd两个基本块来分发调度除第一个基本块外的所有原始基本块.

控制流平坦化前的流程图如下:

控制流平坦化前

控制流平坦化后的流程图如下:

控制流平坦化后

控制流平坦化后,在IDA中的示例如下:

IDA示例

这里我们可以很清楚的看到真实的基本块、loopEntry以及loopEnd的位置.

测试文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//luoTst.c
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv) {
  /*
  第一个基本块最后一条指令为有条件分支跳转
  if(argc > 2){
    printf("argc > 2\n");
  }
  else{
    printf("argc <= 2\n");
  }
  */
  switch(argc){
    case 0:
      printf("0 hello ollvm\n");
    case 1:
      printf("1 hello ollvm\n");
    case 2:
      printf("2 hello ollvm\n");
  }
  return 0;
}

流程分析

入口函数

  1. fla的声明在ollvm/include/llvm/Transforms/Obfuscation/Flattening.h
1
2
3
//创建对应的Pass
Pass *createFlattening();
Pass *createFlattening(bool flag);
  1. fla的入口函数是runOnFunction,在ollvm/lib/Transforms/Obfuscation/Flattening.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
bool Flattening::runOnFunction(Function &F) {
  Function *tmp = &F;
  // Do we obfuscate
  if (toObfuscate(flag, tmp, "fla")) {
    if (flatten(tmp)) {
      ++Flattened;
    }
  }

  return false;
}
  1. toObfuscate函数用于判断是否需要进行fla

toObfuscate函数

关键函数

fla的关键函数是flatten,其大概思路就是先将所有的基本块进行保存,找到第一个基本块,在其后面分配一个switchVar变量并赋值,然后添加switch指令,根据switchVar跳向其他基本块,最后更新switchVar,让基本块跳转到正确的后续基本块中.

  1. 生成随机key
1
2
3
4
// SCRAMBLER
char scrambling_key[16];
llvm::cryptoutils->get_bytes(scrambling_key, 16);
// END OF SCRAMBLER
  1. 将源码中的switch结构转成一个链式的分支结构
1
2
3
//Lower switch
FunctionPass *lower = createLowerSwitchPass();
lower->runOnFunction(*f);

转换前后的IR指令对比图如下:

createLowerSwitchPass

  1. 保存函数中需要进行平坦化的块
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Save all original BB
for (Function::iterator i = f->begin(); i != f->end(); ++i) {
  BasicBlock *tmp = &*i;
  origBB.push_back(tmp);

  BasicBlock *bb = &*i;
  //当某一个块的末尾是Invoke指令,即调用其他函数指令 则不进行保护
  if (isa<InvokeInst>(bb->getTerminator())) {
    return false;
  }
}

//原始块太少,也不进行保护
if (origBB.size() <= 1) {
  return false;
}
  1. 处理第一个基本块
 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
//在保存的第一个基本块的列表中去除第一个基本块
origBB.erase(origBB.begin());

//将insert指向第一个基本块
Function::iterator tmp = f->begin();  //++tmp;
BasicBlock *insert = &*tmp;

// If main begin with an if
BranchInst *br = NULL;
//如果第一个基本块的最后一条指令是分支指令,则br指向这条分支指令
if (isa<BranchInst>(insert->getTerminator())) {
  br = cast<BranchInst>(insert->getTerminator());
}

//如果br是一个有条件跳转,则会对这个基本块进行分割,将分支跳转指令连同前一条指令一起分割为一个新的基本块,并命名为first
if ((br != NULL && br->isConditional()) ||
    insert->getTerminator()->getNumSuccessors() > 1) {
  BasicBlock::iterator i = insert->end();
--i;

  if (insert->size() > 1) {
    --i;
  }

  BasicBlock *tmpBB = insert->splitBasicBlock(i, "first");
  origBB.insert(origBB.begin(), tmpBB);
}

转换前后的IR指令对比图如下:

原始基本块分割为2个基本块

  1. 在第一个基本块的末尾添加switchVar变量并为其赋值
1
2
3
4
5
6
// Create switch variable and set as it
switchVar =
    new AllocaInst(Type::getInt32Ty(f->getContext()), 0, "switchVar", insert);
  ConstantInt * first_num = ConstantInt::get(Type::getInt32Ty(f->getContext()),
                   llvm::cryptoutils->scramble32(0, scrambling_key));
new StoreInst(first_num, switchVar, insert);

这个地方注意下,first_num生成的随机值受两个变量影响,若这两个变量值不变,则下次生成的first_num和之前生成的相同

  1. 创建循环开始块和循环结束块
 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
// Create main loop
loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert);
loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert);

load = new LoadInst(switchVar, "switchVar", loopEntry);

// Move first BB on top
insert->moveBefore(loopEntry);
BranchInst::Create(loopEntry, insert);

// loopEnd jump to loopEntry
BranchInst::Create(loopEntry, loopEnd);

BasicBlock *swDefault =
    BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd);
BranchInst::Create(loopEnd, swDefault);

// Create switch instruction itself and set condition
switchI = SwitchInst::Create(&*f->begin(), swDefault, 0, loopEntry);
switchI->setCondition(load);

// Remove branch jump from 1st BB and make a jump to the while
f->begin()->getTerminator()->eraseFromParent();

BranchInst::Create(loopEntry, &*f->begin());

此时的IR表示应如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
entry:
  //...
  store i32 %0, i32* %.reg2mem
  %switchVar = alloca i32
  store i32 -1217360296, i32* %switchVar
  br label %loopEntry

loopEntry:                                        
  %switchVar9 = load i32, i32* %switchVar
  switch i32 %switchVar9, label %switchDefault
  
switchDefault:                                    
  br label %loopEnd
  
loopEnd:                                         
  br label %loopEntry
  1. 完善loopEntry中的switch结构,为保存的所有原始基本块添加case
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Put all BB in the switch
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
     ++b) {
  BasicBlock *i = *b;
  ConstantInt *numCase = NULL;

  // Move the BB inside the switch (only visual, no code logic)
  i->moveBefore(loopEnd);

  // Add case to switch
  numCase = cast<ConstantInt>(ConstantInt::get(
      switchI->getCondition()->getType(),
      llvm::cryptoutils->scramble32(switchI->getNumCases(), scrambling_key)));
  switchI->addCase(numCase, i);
}
  1. 重新计算switchVar值,保持原有基本块执行逻辑
 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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Recalculate switchVar
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
     ++b) {
  BasicBlock *i = *b;
  ConstantInt *numCase = NULL;

  //①如果该基本块无后继基本块,则不进行处理
  // Ret BB
  if (i->getTerminator()->getNumSuccessors() == 0) {
    continue;
  }
  
  //②如果该基本块最后一条指令为无条件跳转,即有1个后继基本块,找到后继基本块对应的case,更新switchVar值,然后无条件跳转到loopEnd
  // If it's a non-conditional jump
  if (i->getTerminator()->getNumSuccessors() == 1) {
    // Get successor and delete terminator
    BasicBlock *succ = i->getTerminator()->getSuccessor(0);
    i->getTerminator()->eraseFromParent();

    // Get next case
    numCase = switchI->findCaseDest(succ);

    // If next case == default case (switchDefault)
    if (numCase == NULL) {
      numCase = cast<ConstantInt>(
          ConstantInt::get(switchI->getCondition()->getType(),
                           llvm::cryptoutils->scramble32(
                               switchI->getNumCases() - 1, scrambling_key)));
    }

    // Update switchVar and jump to the end of loop
    new StoreInst(numCase, load->getPointerOperand(), i);
    BranchInst::Create(loopEnd, i);
    continue;
  }

  //③如果该基本块最后一条指令为有条件跳转,即有2个后继基本块,找到2个后继基本块对应的case,将其标记为numCaseTrue和numCaseFalse,然后创建1个select指令,计算出一个case值用来更新switchVar值,最后无条件跳转到loopEnd
  // If it's a conditional jump
  if (i->getTerminator()->getNumSuccessors() == 2) {
    // Get next cases
    ConstantInt *numCaseTrue =
        switchI->findCaseDest(i->getTerminator()->getSuccessor(0));
    ConstantInt *numCaseFalse =
        switchI->findCaseDest(i->getTerminator()->getSuccessor(1));

    // Check if next case == default case (switchDefault)
    if (numCaseTrue == NULL) {
      numCaseTrue = cast<ConstantInt>(
          ConstantInt::get(switchI->getCondition()->getType(),
                           llvm::cryptoutils->scramble32(
                               switchI->getNumCases() - 1, scrambling_key)));
    }

    if (numCaseFalse == NULL) {
      numCaseFalse = cast<ConstantInt>(
          ConstantInt::get(switchI->getCondition()->getType(),
                           llvm::cryptoutils->scramble32(
                               switchI->getNumCases() - 1, scrambling_key)));
    }

    // Create a SelectInst
    BranchInst *br = cast<BranchInst>(i->getTerminator());
    SelectInst *sel =
        SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "",
                           i->getTerminator());

    // Erase terminator
    i->getTerminator()->eraseFromParent();

    // Update switchVar and jump to the end of loop
    new StoreInst(sel, load->getPointerOperand(), i);
    BranchInst::Create(loopEnd, i);
    continue;
  }
}

参考链接

Flattening.cpp

OLLVM混淆研究之FLA篇

OLLVM源码分析 - Pass之Flattening

基于LLVM Pass实现控制流平坦化


相关内容

0%