关于LLVM pass的学习(3)-- BogusControlFlow(虚假控制流) 分析

功能:

是为函数增加新的虚假控制流和添加垃圾指令

入口函数:

BogusControlFlow继承了FunctionPass,因此它的入口函数即为runOnFunction该入口函数进来先判断两个参数的值:ObfTimes和ObfProbRate ,分别代表bcf(BogusControlFlow)循环运行的次数和每个basic block被混淆的几率,它们的默认值分别为1和30%。可通过设置参数boguscf-loop、 boguscf-prob修改它们的默认值
例如:

if (!((ObfProbRate > 0) && (ObfProbRate <= 100))) 

在编译程序代码时,若要启动bcf模块,需要带上参数“-mllvm -bcf”

     if (toObfuscate(flag, &F, "bcf")) 

最后调用bogus函数。bogus函数首先将本function的所有basicblock存放到一个list容器中,然后使用一个while循环调用addBogusFlow函数对选中的basicblock进行增加虚假控制流

核心函数:

  • bogus 函数
    首先判断ObfTimes和ObfProbRate 这两个参数

    判断函数

    一个大循环包括下面的内容,这个大循环是ObfTimes 来控制循环运行的次数:
    1:将函数中的所有BB 添加到list 中
    添加到list

    2: 判断上面的list 是否为空,将这个方法的内部的所有BB 根据混淆比例看是否去虚假流程
    有概率的执行虚假流

  • addBogusFlow 函数
    举个例子:

int func1(int a,int b)
{
       return a+b;
}

编译完后的IR图


IR

addBogusFlow函数
1:调用getFirstNonPHIOrDbgOrLifetime函数获取本basicblock中第一个不是Phi、Dbg、Lifetime的指令的地址(在本例中,即为%a.addr = alloca i32, aling 4的地址)
2:调用splitBasicBlock函数。splitBasicBlock函数可根据上述指令的地址将一个basicblock一分为二(可称为first basicblock 和original basicblock),i1 是%a.addr = alloca i32, aling 4的地址,分割的时候是第二块是从i1开始到最后,i1 上面的部分为一块


分割过程

分割后此时IR 如下:
image.png

接着调用createAlteredBasicBlock函数对original basicblock进行拷贝生成一个名为“altered basicblock”的basicblock,并对该basicblock加入一些垃圾指令。加入垃圾指令的方法是遍历该basicblock中的所有OpCode【操作码】,若包含有Add、Sub、UDiv、SDiv、URem、SRem、Shl、LShr、AShr、And、Or、Xor以及FAdd、FSub、FMul、FDiv、FRem指令,则用随机生成一些指令来进行替换。由于该block在程序运行时并不会执行,因此无需担心插入的指令对原始程序运行的结果产生影响。拷贝original basicblock后,


image.png

IR图如图5所示。


image.png

这时,所有的basicblock已经准备完毕,一共存在有3个basicblock,需要调整他们之间的关系。首先清除first basicblock和altered basicblock跟父节点的关系,代码为:

basicBlock->getTerminator()->eraseFromParent();
alteredBB->getTerminator()->eraseFromParent();

清除完毕后的IR图如图6所示。


image.png

接着下一步的操作是增加basicblock之间的条件跳转指令。对于first basicblock(即为图中的entry),bcf源码的做法是先增加一条比较语句 1.0 = = 1.0 ,然后为真时跳转到original basicblock,为假则跳转到altered basicblock。可用伪代码如下表示:

if( 1.0 == 1. 0)
    original basicblock
else
    altered basicblock

对于altered basicblock模块,在它的尾部增加一条跳转指令,使得当它执行完毕之后(实际上它并不会执行),跳转到original basicblock模块。此时的IR图如图7所示。


image.png

image.png

最后,获取basicblock中最后一条指令的地址(在该例子中即ret指令的地址),调用splitBasicblock函数将original basicblock一分为二(original basicblok和originalBBpart2)


image.png

然后调用如下代码:
originalBB->getTerminator()->eraseFromParent();

消除original basicblok和originalBBpart2的关系后,再在original basicblock的末尾加入一个判断语句,为真时跳转到ret指令,为假则跳转到altered basicblock,伪代码如下所示:

if( 1.0 == 1. 0)
    ret
else
    altered basicblock

此时该func1函数的IR图如图8所示:

image.png

doF函数 【和bogus 函数在位置相同】
该函数的功能是将Function中所有为真的判断语句进行替换,比如上一节中的“1.0 == 1.0 ”。它的思想是定义两个全局变量x、y并且初始化为0,然后遍历Module内的所有指令,并将所有的FCMP_TRUE分支指令替换为“y<10 || x
x(x-1)%2 ==0”。替换完毕后func1函数的IR流程图如图9所示:
image.png

至此,对func1函数的一次bcf混淆过程就完成了

推荐阅读更多精彩内容