抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

神秘的ollvm,之前都是知其状,不知其所以然。

原理

OLLVM 基于 LLVM 编译器框架,其工作原理是在程序编译的过程中插入特定的混淆变换。通过编写更加复杂的Pass,将代码复杂化,这样就达到了混淆的目的。

分类:

字符串加密

字符串加密是将源代码中的字符串数据(如常量字符串)进行加密,使得它们在二进制文件中存储时不再以明文形式出现。程序运行时,字符串需要经过解密才能被使用。编写一个pass将其中的字符串信息使用一些加密算法进行加密,然后特定的时间进行还原。

指令替换(Sub)

指令混淆的目标是改变程序的机器指令,使其变得难以逆向理解。虽然指令混淆并不改变程序的语义,但它会使得生成的二进制文件变得更加复杂,从而增加逆向工程的难度。

  • 实现细节:
    • 替代常用指令:通过使用不常见的指令来代替常见的指令,从而使得反汇编后的代码难以理解。例如,使用 xor 指令代替 addsub
    • 引入无用指令:向代码中添加一些不影响程序结果的“垃圾”指令,使得程序的机器码变得更加复杂。例如,y=x+1,替换成y=x-2+3

虚假控制流(bcf)

虚假控制流混淆的基本思想是通过改变程序的分支结构、控制流指令以及函数调用顺序等,来插入无意义的控制流,打乱程序的执行路径。

虚假控制流混淆主要通过加入包含不透明谓词的条件跳转和不可达的基本块,来干扰IDA的控制流分析和F5反汇编。

不透明谓词:

在 OLLVM 中,不透明谓词是一种插入到程序中的伪条件(如 if 语句),这些条件没有任何实际的逻辑意义或者其结果对程序的行为没有任何影响。它们通常由一些随机或不相关的值计算得出,使得它们的真假分支无法被直接推测或静态分析。在跳转前就已经确定的不等式,但是IDA无法分析,

例如

1
2
3
4
5
if (3 % 2 == 0) {
// 执行一组操作
} else {
// 执行另一组操作
}

3 % 2 == 0这个式子是恒不成立的,因此这个判断只会执行else,就可以增加虚假控制流。

不透明谓词的基本类型:

永真型不透明谓词(Tautology)

  • 定义:永真型谓词是那些始终为 true 的条件判断。换句话说,它们总是引导程序走到某个分支。

永假型不透明谓词(Contradiction)

  • 定义:永假型谓词是那些始终为 false 的条件判断。它们创建了从不被执行的代码块。

可真可假型不透明谓词(Opaque Predicate)

  • 定义:可真可假型谓词的结果由程序中的某些变量决定,但这些变量的值与程序的实际逻辑或输入无关。也就是说,这些谓词在某些情况下可能为真,某些情况下可能为假,但静态分析者无法知道这些条件何时为真,何时为假,因为它们可能依赖于随机数、外部环境、加密信息等。
1
2
3
4
5
if (rand() % 2 == 0) {
return 1; // 随机条件
} else {
return 0;
}

不可达基本块

不可达基本块是控制流图中的那些基本块,它们的执行路径永远不会被激活,即使它们在代码中存在,也不被任何真实的执行路径覆盖。

不可达基本块的作用

  • 增加程序复杂度:不可达基本块的插入通过增加无意义的代码,使得程序的控制流图变得更加复杂,从而增加了逆向工程的难度。
  • 引导虚假路径:不可达基本块往往是由虚假控制流引入的,它们充当着“假”的执行路径,让攻击者无法准确判断哪些代码会被执行。

可真可假型不透明谓词+不可达基本快

控制流平坦化(Fla)

其基本思想是通过引入一个主分发器来控制程序基本块(basic block)之间的执行顺序,并通过复杂的跳转逻辑隐藏原有的控制流结构,从而使得程序的执行流程更加难以理解。

该方法将所有基本代码放到控制流最底部,然后删除原理基本块之间跳转关系,添加次分发器来控制分发逻辑,然后过新的复杂分发逻辑还原原来程序块之间的逻辑关系。

基本流程:

添加一个随机数种子 blockID

  • 在开始控制流平坦化之前,OLLVM 会为每个基本块生成一个唯一的 blockID,用于后续的跳转控制。这个 blockID 通常是一个随机数,可以增加混淆的复杂度,使得控制流更加难以预测。

保存所有基本块

  • 通过遍历程序的所有基本块,将它们保存到一个列表或数组中。基本块(Basic Block)是程序中的一个代码段,它没有跳转指令,程序的控制流从一个基本块跳转到另一个基本块。

switch 改为 if

  • OLLVM 会通过将所有的 switch 语句转换为 if 语句,进一步增加程序的控制流复杂性。这是为了去掉直接的分支跳转,使得每个分支都通过 if 语句进行控制。

删除第一个基本块并进行特殊处理

  • 第一个基本块通常会被认为是程序的入口点。为了增强混淆,OLLVM 会删除第一个基本块,替换为其他复杂的分发逻辑,使得程序的开始部分不再直接可见。

识别 main 中的 if 语句并删除跳转指令

  • OLLVM 会识别 main 函数中的所有 if 语句,并删除原始的跳转指令。然后,它会插入新的跳转指令,控制程序流程。

插入一个 switch 指令

  • 为了进一步混淆控制流,OLLVM 会插入一个新的 switch 指令。这个 switch 指令将用于根据条件决定程序执行的路径,而不是直接依赖 if 语句中的跳转。

第一个块跳转到 loopEntry

  • 将程序的第一个基本块(已经被删除和替换)设置为跳转到一个新的块 loopEntry,开始控制流的平坦化。loopEntry 块将充当程序控制流的入口点,协调后续的跳转。

将所有基本块保存到 switch 语句中

  • 所有的基本块会被保存到 switch 语句的不同分支中,使得程序的控制流不再是线性的,而是根据某个条件在不同的基本块之间跳转。

重新计算 switch 变量的值

  • 为了控制程序的跳转,OLLVM 会重新计算 switch 指令中使用的变量的值。这通常是通过计算某个随机值或基于程序状态的值来决定的。这个变量的值将决定程序的实际执行路径。

处理非条件跳转

  • 对于那些非条件跳转(如直接跳转到其他基本块的 gotojump),OLLVM 会删除原来的跳转指令,并将其替换为指向 loopEnd 块的跳转指令。loopEnd 块是一个循环结束点,所有非条件跳转最终都会跳转到这个点进行下一轮的循环。

处理条件跳转

  • 对于原本的条件跳转,OLLVM 会重新安排其分支执行逻辑。具体而言:
    • 真分支:如果条件判断为真,则选择真分支的 blockID
    • 假分支:如果条件判断为假,则选择假分支的 blockID
  • 这一步骤通过修改条件判断的逻辑,使得程序的控制流变得更加复杂。

源码阅读

站在巨人的肩膀上,网上已经有很多大佬分析,自己再跟着分析一遍。文件在/home/cruve/tools/ollvm/obfuscator/lib/Transforms/Obfuscation

Substitution指令替换

Substitution::runOnFunction

检测是否需要进行混淆,是则进入混淆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool Substitution::runOnFunction(Function &F) {
//检查是否设置了有效的混淆次数
if (ObfTimes <= 0) {
errs()<<"Substitution application number -sub_loop=x must be x > 0";
return false;
}

Function *tmp = &F;
//判断是否需要对当前函数进行混淆操作
if (toObfuscate(flag, tmp, "sub")) {
substitute(tmp);
return true;
}

return false;
}

substitute

根据不同的运算符进行随机混淆

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
bool Substitution::substitute(Function *f) {
Function *tmp = f;

// 执行替换混淆操作的次数
int times = ObfTimes;
do {
//第一个 for 循环遍历函数中的所有基本块,第二个 for 循环遍历当前基本块中的所有指令
for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
//检查当前指令是否是二元操作符
if (inst->isBinaryOp()) {
//根据指令的操作符类型(getOpcode() 返回操作符类型)进入不同的分支
switch (inst->getOpcode()) {
case BinaryOperator::Add:
//随机选择一个加法替换函数,然后对当前指令进行替换。
(this->*funcAdd[llvm::cryptoutils->get_range(NUMBER_ADD_SUBST)])(
cast<BinaryOperator>(inst));
++Add;
break;
case BinaryOperator::Sub:
//如果是减法操作 (Sub),则从 funcSub 数组中随机选择一个减法替换函数进行替换,并增加 Sub 计数器。
(this->*funcSub[llvm::cryptoutils->get_range(NUMBER_SUB_SUBST)])(
cast<BinaryOperator>(inst));
++Sub;
break;
...
default:
break;
} // End switch
} // End isBinaryOp
} // End for basickblock
} // End for Function
} while (--times > 0); // for times
return false;
}

addNeg

实现了对加法指令的替换,具体将 加法操作(a = b + c 转换为等效的操作 **a = b - (-c)**。

1
2
3
4
5
6
7
8
9
10
11
12
13
void Substitution::addNeg(BinaryOperator *bo) {
BinaryOperator *op = NULL;

//首先检查传入的二元操作指令 bo 是否为 Add 类型
if (bo->getOpcode() == Instruction::Add) {
//使用 CreateNeg 函数生成一条新的指令,对 bo 的第二个操作数(c)进行取负操作,结果是 -c
op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo);
//使用 Create 函数生成一个新的减法指令,用第一操作数 b 减去上一步计算的 -c,结果是 b - (-c),即原来的加法 b + c。
op =BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo);
//将原指令 bo 的所有使用替换为新创建的减法指令 op。
bo->replaceAllUsesWith(op);
}
}

SplitBasicBlocks拆分

顾名思义,将一个大的基本块拆分成多个较小的基本块

SplitBasicBlock::runOnFunction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool SplitBasicBlock::runOnFunction(Function &F) {
//防止用户配置了无效的 SplitNum 参数,避免无效的拆分操作
if (!((SplitNum > 1) && (SplitNum <= 10))) {
errs()<<"Split application basic block percentage\
-split_num=x must be 1 < x <= 10";
return false;
}

Function *tmp = &F;

// 决定是否进行拆
if (toObfuscate(flag, tmp, "split")) {
split(tmp);
++Split;
}

return false;
}

split

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
void SplitBasicBlock::split(Function *f) {
std::vector<BasicBlock *> origBB;
int splitN = SplitNum;

//保存所有基本块
for (Function::iterator I = f->begin(), IE = f->end(); I != IE; ++I) {
origBB.push_back(&*I);
}
//遍历基本块
for (std::vector<BasicBlock *>::iterator I = origBB.begin(),
IE = origBB.end();
I != IE; ++I) {
BasicBlock *curr = *I;

//跳过特殊基本块(指令数<2的基本块和存在phi的基本块)
//PHI节点用来表示从不同的前驱基本块中接收的值
if (curr->size() < 2 || containsPHI(curr)) {
continue;
}

// 确保拆分次数合法
if ((size_t)splitN > curr->size()) {
splitN = curr->size() - 1;
}

// 生成拆分点,创建一个 std::vector<int> test,它保存当前基本块中所有可能的拆分点
std::vector<int> test;
for (unsigned i = 1; i < curr->size(); ++i) {
test.push_back(i);
}

// 打乱拆分点:如果拆分点不止一个,使用 shuffle(test) 随机打乱拆分点的顺序。然后对前 splitN 个拆分点进行排序,确保拆分点的位置是有序的。
if (test.size() != 1) {
shuffle(test);
std::sort(test.begin(), test.begin() + splitN);
}

//执行基本块拆分
BasicBlock::iterator it = curr->begin();//初始化一个迭代器 it,它指向当前基本块的第一个指令。
BasicBlock *toSplit = curr;//将当前基本块赋给 toSplit,以便对其进行拆分。
int last = 0;
for (int i = 0; i < splitN; ++i) {
for (int j = 0; j < test[i] - last; ++j) {
++it;
}
last = test[i];
if(toSplit->size() < 2)//拆分前检查
continue;
//调用 splitBasicBlock 函数在拆分点 it 处拆分当前基本块。
toSplit = toSplit->splitBasicBlock(it, toSplit->getName() + ".split");
}

++Split;
}
}

splitBasicBlock函数

将当前基本块拆分为两个基本块

在 LLVM 中,PHINode 用于处理基本块之间的控制流汇聚。当一个基本块有多个前驱基本块时,PHINode 用于选择从不同路径传来的值。

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
BasicBlock *BasicBlock::splitBasicBlock(iterator I, const Twine &BBName) {
//检查当前基本块是否包含终结指令
assert(getTerminator() && "Can't use splitBasicBlock on degenerate BB!");
//确保分割点 I 有效
assert(I != InstList.end() && "Trying to get me to create degenerate basic block!");
//创建新的基本块
BasicBlock *New = BasicBlock::Create(getContext(), BBName, getParent(), this->getNextNode());
//移动指令到新基本块
DebugLoc Loc = I->getDebugLoc();
New->getInstList().splice(New->end(), this->getInstList(), I, end());//将指令插入到新基本块的末尾
//将当前基本块跳转到新创建的基本块 New
BranchInst *BI = BranchInst::Create(New, this);
BI->setDebugLoc(Loc);
//处理后继基本块的 PHI 节点
for (succ_iterator I = succ_begin(New), E = succ_end(New); I != E; ++I) {//遍历 New 的后继基本块
BasicBlock *Successor = *I;
PHINode *PN;
//将当前指令转换为 PHINode,如果是 PHINode,则继续处理
for (BasicBlock::iterator II = Successor->begin(); (PN = dyn_cast<PHINode>(II)); ++II) {
int IDX = PN->getBasicBlockIndex(this);//获取当前基本块在 PHINode 中的索引位置。
while (IDX != -1) {
PN->setIncomingBlock((unsigned)IDX, New);//更新 PHI 节点的前驱块,将当前基本块的前驱更改为新的基本块。
IDX = PN->getBasicBlockIndex(this);
}
}
}
return New;
}

BogusControlFlow 虚假控制流

runOnFunction 混淆入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
virtual bool runOnFunction(Function &F){
//检查混淆次数是否有效
if (ObfTimes <= 0) {
errs()<<"BogusControlFlow application number -bcf_loop=x must be x > 0";
return false;
}

// 检查混淆概率是否有效
if ( !((ObfProbRate > 0) && (ObfProbRate <= 100)) ) {
errs()<<"BogusControlFlow application basic blocks percentage -bcf_prob=x must be 0 < x <= 100";
return false;
}
//判断是否进行混淆
if(toObfuscate(flag,&F,"bcf")) {
bogus(F);//混淆函数
doF(*F.getParent());//进一步的处理
return true;
}

return false;
} // end of runOnFunction()

bogus 混淆逻辑

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
void bogus(Function &F) {
//初始统计和调试信息
++NumFunction;
int NumBasicBlocks = 0;
bool firstTime = true; // First time we do the loop in this function
bool hasBeenModified = false;
//...
NumTimesOnFunctions = ObfTimes;
int NumObfTimes = ObfTimes;

// 主混淆逻辑
do{//调试输出 CFG(控制流图):在混淆前查看函数的基本块结构
DEBUG_WITH_TYPE("cfg", errs() << "bcf: Function " << F.getName()<<", before the pass:\n");
DEBUG_WITH_TYPE("cfg", F.viewCFG());
// 将当前函数的所有基本块保存到 basicBlocks 列表中,用于后续处理。
std::list<BasicBlock *> basicBlocks;
for (Function::iterator i=F.begin();i!=F.end();++i) {
basicBlocks.push_back(&*i);
}
DEBUG_WITH_TYPE("gen", errs() << "bcf: Iterating on the Function's Basic Blocks\n");
//遍历所有基本块
while(!basicBlocks.empty()){
NumBasicBlocks ++;
//根据 ObfProbRate 决定是否对当前基本块应用混淆
if((int)llvm::cryptoutils->get_range(100) <= ObfProbRate){
DEBUG_WITH_TYPE("opt", errs() << "bcf: Block "<< NumBasicBlocks <<" selected. \n");
hasBeenModified = true;
++NumModifiedBasicBlocks;
NumAddedBasicBlocks += 3;
FinalNumBasicBlocks += 3;
// Add bogus flow to the given Basic Block (see description)
BasicBlock *basicBlock = basicBlocks.front();
//调用 addBogusFlow,对基本块插入假的控制流。
addBogusFlow(basicBlock, F);
}
else{
DEBUG_WITH_TYPE("opt", errs() << "bcf: Block "<< NumBasicBlocks <<" not selected.\n");
}
// remove the block from the list
basicBlocks.pop_front();

if(firstTime){ // 如果是第一次处理当前函数,初始化基本块计数器。
++InitNumBasicBlocks;
++FinalNumBasicBlocks;
}
} // end of while(!basicBlocks.empty())
...
firstTime = false;
}while(--NumObfTimes > 0);
}

addBogusFlow对基本块插入假的控制流

1
virtual void addBogusFlow(BasicBlock * basicBlock, Function &F){

在basicBlock基本快和其原后续块之间插入新块originalBB

1
2
3
4
5
6
7
8
9
10
11
12
  BasicBlock::iterator i1 = basicBlock->begin();
//找到 basicBlock 的首个非 PHI、调试信息或生命周期信息的指令(getFirstNonPHIOrDbgOrLifetime)。
if(basicBlock->getFirstNonPHIOrDbgOrLifetime())
i1 = (BasicBlock::iterator)basicBlock->getFirstNonPHIOrDbgOrLifetime();
Twine *var;
var = new Twine("originalBB");
//调用 splitBasicBlock 将 basicBlock 拆分成两部分:
//第一部分只保留前置的 PHI 节点和调试信息。
//第二部分包含剩余的指令。
//originalBB 是拆分出的新基本块,保留原始的执行逻辑。
BasicBlock *originalBB = basicBlock->splitBasicBlock(i1, *var);
DEBUG_WITH_TYPE("gen", errs() << "bcf: First and original basic blocks: ok\n");

调用 createAlteredBasicBlock 创建一个伪造的基本块 alteredBB。删除basicblock和alteredBB指向后续块的指令

1
2
3
4
5
6
7
8
Twine * var3 = new Twine("alteredBB");
BasicBlock *alteredBB = createAlteredBasicBlock(originalBB, *var3, &F);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Altered basic block: ok\n");

//删除伪造块和原始块的旧终止指令,为插入新的分支逻辑做准备
alteredBB->getTerminator()->eraseFromParent();
basicBlock->getTerminator()->eraseFromParent();
DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator removed from the altered"<<" and first basic blocks\n");

创建伪造的条件,插入虚假分支逻辑

basicblock基本快后面插入一个永远跳入oringalBB的Fcmp条件跳转,

另外一跳指向alteredBB,并且alterBB结束指向originalBB,而originalBB指向我们basicblock原先的后继基本块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建两个值用于后续的比较指令
Value * LHS = ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0);
Value * RHS = ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Value LHS and RHS created\n");

// 创建一个伪造的浮点比较指令 condition,始终返回 true
Twine * var4 = new Twine("condition");
FCmpInst * condition = new FCmpInst(*basicBlock, FCmpInst::FCMP_TRUE , LHS, RHS, *var4);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Always true condition created\n");

// 在 basicBlock 的末尾插入条件分支:若 condition 为真,则跳转到 originalBB。否则跳转到 alteredBB(实际上总是跳到 originalBB)。
BranchInst::Create(originalBB, alteredBB, (Value *)condition, basicBlock);
DEBUG_WITH_TYPE("gen",errs() << "bcf: Terminator instruction in first basic block: ok\n");

// 在 alteredBB 的末尾插入回到 originalBB 的无条件分支。
BranchInst::Create(originalBB, alteredBB);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator instruction in altered block: ok\n");

拆分 originalBB 基本块,将终结指令移到新创建的 originalBBpart2 基本块中。

originalBB 的末尾添加一个总是为 true 的条件比较指令(FCmpInst)。

基于该条件,创建一个分支指令跳转到 originalBBpart2(ture) 和 alteredBB(false),从而增加控制流的复杂度,形成伪控制流。

originalBB 中删除原有的终结指令,替换为新的分支指令,进一步增加代码的混淆。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//始化了一个iterator,该迭代器指向 originalBB 的末尾
BasicBlock::iterator i = originalBB->end();

//将 originalBB 基本块从倒数第二个指令(--i)处拆分为两个基本块。 新的基本块名为originalBBpart2
Twine * var5 = new Twine("originalBBpart2");
BasicBlock * originalBBpart2 = originalBB->splitBasicBlock(--i , *var5);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator part of the original basic block"<< " is isolated\n");
//删除了 originalBB 中的终结指令
originalBB->getTerminator()->eraseFromParent();
//再次创建一个 Twine 对象,用于为下一个条件判断创建一个标签("condition2")
Twine * var6 = new Twine("condition2");
//这行代码创建了一个 FCmpInst(浮动点比较指令),它比较两个常量浮点数 LHS 和 RHS,并且使用 FCMP_TRUE(即总是为 true)作为比较操作。
FCmpInst * condition2 = new FCmpInst(*originalBB, CmpInst::FCMP_TRUE , LHS, RHS, *var6);//这表示该条件始终为真
//创建了一个分支指令(BranchInst),它基于条件 condition2 来决定跳转目标,分支指令会被添加到 originalBB 的末尾,作为新的终结指令
BranchInst::Create(originalBBpart2, alteredBB, (Value *)condition2, originalBB);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator original basic block: ok\n");
DEBUG_WITH_TYPE("gen", errs() << "bcf: End of addBogusFlow().\n");
}

createAlteredBasicBlock函数

克隆基本块

1
2
3
4
5
virtual BasicBlock* createAlteredBasicBlock(BasicBlock * basicBlock,const Twine &  Name = "gen", Function * F = 0){
// llvm::CloneBasicBlock 将原始的 basicBlock 克隆成一个新的基本块 alteredBB
ValueToValueMapTy VMap;
BasicBlock * alteredBB = llvm::CloneBasicBlock (basicBlock, VMap, Name, F);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Original basic block cloned\n");

对克隆基本块中的指令、Phi前驱块、元数据和调试信息进行正确的重新映射,确保克隆后的基本块在语义上与原始基本块一致

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
//ji 是指向原始基本块开始位置的迭代器。
BasicBlock::iterator ji = basicBlock->begin();
//遍历克隆基本块中的所有指令。e 是克隆基本块的末尾迭代器,表示循环结束的条件。
for (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end() ; i != e; ++i){
// 每条指令都有一个或多个操作数,遍历当前指令 i 的所有操作数
for(User::op_iterator opi = i->op_begin (), ope = i->op_end(); opi != ope; ++opi){
// 根据映射表 VMap 查找和返回当前操作数的映射值
Value *v = MapValue(*opi, VMap, RF_None, 0);
//如果 MapValue 返回非空值,则表示操作数已经成功映射
if (v != 0){
*opi = v;
DEBUG_WITH_TYPE("gen", errs() << "bcf: Value's operand has been setted\n");
}
}
DEBUG_WITH_TYPE("gen", errs() << "bcf: Operands remapped\n");
// 重新映射 Phi 节点的前驱块
if (PHINode *pn = dyn_cast<PHINode>(i)) {//判断当前指令是否为 PHINode
//则遍历该 Phi 节点的所有前驱块
for (unsigned j = 0, e = pn->getNumIncomingValues(); j != e; ++j) {
//重新映射 Phi 节点的前驱块
Value *v = MapValue(pn->getIncomingBlock(j), VMap, RF_None, 0);
if (v != 0){
pn->setIncomingBlock(j, cast<BasicBlock>(v));
}
}
}
DEBUG_WITH_TYPE("gen", errs() << "bcf: PHINodes remapped\n");
//重新映射指令的元数据
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
i->getAllMetadata(MDs);
DEBUG_WITH_TYPE("gen", errs() << "bcf: Metadatas remapped\n");
//重新设置调试信息
i->setDebugLoc(ji->getDebugLoc());
ji++;
DEBUG_WITH_TYPE("gen", errs() << "bcf: Debug information location setted\n");

}

添加垃圾代码

在克隆后的基本块中插入“垃圾代码”。这部分代码会随机地在每个二进制操作(如加法、减法、乘法等)中插入一些额外的运算,或者在条件比较(ICmpFCmp)中进行修改。这些插入的操作会增加控制流的复杂性,制造出伪装的效果。

对于整数和浮点操作,代码根据随机数的值决定是否插入额外的指令,例如在加法、减法等基础操作中插入额外的运算(例如取负、加法、乘法等)。

对于条件比较(ICmpFCmp),代码也会随机改变比较操作符或交换操作数,进一步增加了控制流的复杂性。

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    DEBUG_WITH_TYPE("gen", errs() << "bcf: The cloned basic block is now correct\n");
DEBUG_WITH_TYPE("gen",
errs() << "bcf: Starting to add junk code in the cloned bloc...\n");

// 遍历基本块中的指令
for (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end() ; i != e; ++i){
// 如果指令是运算指令,进行垃圾指令添加
if(i->isBinaryOp()){ // binary instructions
unsigned opcode = i->getOpcode();
BinaryOperator *op, *op1 = NULL;
Twine *var = new Twine("_");
// treat differently float or int
// Binary int
if(opcode == Instruction::Add || opcode == Instruction::Sub ||
opcode == Instruction::Mul || opcode == Instruction::UDiv ||
opcode == Instruction::SDiv || opcode == Instruction::URem ||
opcode == Instruction::SRem || opcode == Instruction::Shl ||
opcode == Instruction::LShr || opcode == Instruction::AShr ||
opcode == Instruction::And || opcode == Instruction::Or ||
opcode == Instruction::Xor){
for(int random = (int)llvm::cryptoutils->get_range(10); random < 10; ++random){
switch(llvm::cryptoutils->get_range(4)){ // to improve
case 0: //do nothing
break;
case 1: op = BinaryOperator::CreateNeg(i->getOperand(0),*var,&*i);
op1 = BinaryOperator::Create(Instruction::Add,op,
i->getOperand(1),"gen",&*i);
break;
case 2: op1 = BinaryOperator::Create(Instruction::Sub,
i->getOperand(0),
i->getOperand(1),*var,&*i);
op = BinaryOperator::Create(Instruction::Mul,op1,
i->getOperand(1),"gen",&*i);
break;
case 3: op = BinaryOperator::Create(Instruction::Shl,
i->getOperand(0),
i->getOperand(1),*var,&*i);
break;
}
}
}
// Binary float
if(opcode == Instruction::FAdd || opcode == Instruction::FSub ||
opcode == Instruction::FMul || opcode == Instruction::FDiv ||
opcode == Instruction::FRem){
for(int random = (int)llvm::cryptoutils->get_range(10); random < 10; ++random){
switch(llvm::cryptoutils->get_range(3)){ // can be improved
case 0: //do nothing
break;
case 1: op = BinaryOperator::CreateFNeg(i->getOperand(0),*var,&*i);
op1 = BinaryOperator::Create(Instruction::FAdd,op,
i->getOperand(1),"gen",&*i);
break;
case 2: op = BinaryOperator::Create(Instruction::FSub,
i->getOperand(0),
i->getOperand(1),*var,&*i);
op1 = BinaryOperator::Create(Instruction::FMul,op,
i->getOperand(1),"gen",&*i);
break;
}
}
}
if(opcode == Instruction::ICmp){ // Condition (with int)
ICmpInst *currentI = (ICmpInst*)(&i);
switch(llvm::cryptoutils->get_range(3)){ // must be improved
case 0: //do nothing
break;
case 1: currentI->swapOperands();
break;
case 2: // randomly change the predicate
switch(llvm::cryptoutils->get_range(10)){
case 0: currentI->setPredicate(ICmpInst::ICMP_EQ);
break; // equal
case 1: currentI->setPredicate(ICmpInst::ICMP_NE);
break; // not equal
case 2: currentI->setPredicate(ICmpInst::ICMP_UGT);
break; // unsigned greater than
case 3: currentI->setPredicate(ICmpInst::ICMP_UGE);
break; // unsigned greater or equal
case 4: currentI->setPredicate(ICmpInst::ICMP_ULT);
break; // unsigned less than
case 5: currentI->setPredicate(ICmpInst::ICMP_ULE);
break; // unsigned less or equal
case 6: currentI->setPredicate(ICmpInst::ICMP_SGT);
break; // signed greater than
case 7: currentI->setPredicate(ICmpInst::ICMP_SGE);
break; // signed greater or equal
case 8: currentI->setPredicate(ICmpInst::ICMP_SLT);
break; // signed less than
case 9: currentI->setPredicate(ICmpInst::ICMP_SLE);
break; // signed less or equal
}
break;
}

}
if(opcode == Instruction::FCmp){ // Conditions (with float)
FCmpInst *currentI = (FCmpInst*)(&i);
switch(llvm::cryptoutils->get_range(3)){ // must be improved
case 0: //do nothing
break;
case 1: currentI->swapOperands();
break;
case 2: // randomly change the predicate
switch(llvm::cryptoutils->get_range(10)){
case 0: currentI->setPredicate(FCmpInst::FCMP_OEQ);
break; // ordered and equal
case 1: currentI->setPredicate(FCmpInst::FCMP_ONE);
break; // ordered and operands are unequal
case 2: currentI->setPredicate(FCmpInst::FCMP_UGT);
break; // unordered or greater than
case 3: currentI->setPredicate(FCmpInst::FCMP_UGE);
break; // unordered, or greater than, or equal
case 4: currentI->setPredicate(FCmpInst::FCMP_ULT);
break; // unordered or less than
case 5: currentI->setPredicate(FCmpInst::FCMP_ULE);
break; // unordered, or less than, or equal
case 6: currentI->setPredicate(FCmpInst::FCMP_OGT);
break; // ordered and greater than
case 7: currentI->setPredicate(FCmpInst::FCMP_OGE);
break; // ordered and greater than or equal
case 8: currentI->setPredicate(FCmpInst::FCMP_OLT);
break; // ordered and less than
case 9: currentI->setPredicate(FCmpInst::FCMP_OLE);
break; // ordered or less than, or equal
}
break;
}
}
}
}
return alteredBB;
}

doF

doF主要目的是将某些FCMP_TRUE(总为真)条件替换为复杂的伪谓词,从而提高代码的混淆性

使用了两个全局变量 xy 来替代总是为 true 的条件,初始值为0,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool doF(Module &M){

DEBUG_WITH_TYPE("gen", errs()<<"bcf: Starting doFinalization...\n");

Twine * varX = new Twine("x");
Twine * varY = new Twine("y");
Value * x1 =ConstantInt::get(Type::getInt32Ty(M.getContext()), 0, false);
Value * y1 =ConstantInt::get(Type::getInt32Ty(M.getContext()), 0, false);

GlobalVariable * x = new GlobalVariable(M, Type::getInt32Ty(M.getContext()), false,
GlobalValue::CommonLinkage, (Constant * )x1,
*varX);
GlobalVariable * y = new GlobalVariable(M, Type::getInt32Ty(M.getContext()), false,
GlobalValue::CommonLinkage, (Constant * )y1,
*varY);

查找和修改某些 FCMP_TRUE(总是为真的)条件,并将这些条件加入到toEdit中,以及将对应的永真比较指令加入到toDelete中。

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

std::vector<Instruction*> toEdit, toDelete;//用于存储需要修改(toEdit)和删除(toDelete)的指令。
BinaryOperator *op,*op1 = NULL;//用于存储二进制操作符指令的指针
LoadInst * opX , * opY;//存储加载指令(加载变量的值)的指针
ICmpInst * condition, * condition2;//存储条件比较指令
// 遍历模块中的所有函数
for(Module::iterator mi = M.begin(), me = M.end(); mi != me; ++mi){
for(Function::iterator fi = mi->begin(), fe = mi->end(); fi != fe; ++fi){
//查找并处理条件分支指令
TerminatorInst * tbb= fi->getTerminator();//获取终止指令。
if(tbb->getOpcode() == Instruction::Br){//检查该终止指令是否为 BranchInst(分支指令)。
BranchInst * br = (BranchInst *)(tbb);
if(br->isConditional()){//检查分支是否是条件跳转
FCmpInst * cond = (FCmpInst *)br->getCondition();//获取条件表达式
unsigned opcode = cond->getOpcode();//获取指令的操作符
if(opcode == Instruction::FCmp){////是FCmp指令
if (cond->getPredicate() == FCmpInst::FCMP_TRUE){//检查 FCmpInst::FCMP_TRUE 条件
DEBUG_WITH_TYPE("gen",errs()<<"bcf: an always true predicate !\n");
toDelete.push_back(cond); // The condition
toEdit.push_back(tbb); // The branch using the condition
}
}
}
}
}
}

对在 toEdit 列表中的每个分支指令进行处理,创建条件码x*(x-1)%2 == 0 || y < 10,以该条件码创建条件跳转分支,然后删除之前的永真条件跳转指令。最后删除toDelete中存放的永真比较指令。

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

//遍历toEdit中存放的永真条件跳转指令
for(std::vector<Instruction*>::iterator i =toEdit.begin();i!=toEdit.end();++i){
//if y < 10 || x*(x+1) % 2 == 0,加载全局变量 x 和 y:
opX = new LoadInst ((Value *)x, "", (*i));
opY = new LoadInst ((Value *)y, "", (*i));
//x - 1
op = BinaryOperator::Create(Instruction::Sub, (Value *)opX,
ConstantInt::get(Type::getInt32Ty(M.getContext()), 1,
false), "", (*i));
// x*(x-1)
op1 = BinaryOperator::Create(Instruction::Mul, (Value *)opX, op, "", (*i));
// x*(x-1)%2
op = BinaryOperator::Create(Instruction::URem, op1,
ConstantInt::get(Type::getInt32Ty(M.getContext()), 2,
false), "", (*i));
// //创建条件码为 x*(x-1)%2 == 0
condition = new ICmpInst((*i), ICmpInst::ICMP_EQ, op,
ConstantInt::get(Type::getInt32Ty(M.getContext()), 0,
false));
// //创建条件码为 y < 10
condition2 = new ICmpInst((*i), ICmpInst::ICMP_SLT, opY,
ConstantInt::get(Type::getInt32Ty(M.getContext()), 10,
false));
// x*(x-1)%2 == 0 || y < 10
op1 = BinaryOperator::Create(Instruction::Or, (Value *)condition,
(Value *)condition2, "", (*i));
//// 创建条件跳转指令,ture进入第0个后继基本块,false进入第1个后继基本块,条件码为op1,即x*(x-1)%2 == 0 || y < 10
BranchInst::Create(((BranchInst*)*i)->getSuccessor(0),
((BranchInst*)*i)->getSuccessor(1),(Value *) op1,
((BranchInst*)*i)->getParent());
DEBUG_WITH_TYPE("gen", errs() << "bcf: Erase branch instruction:"
<< *((BranchInst*)*i) << "\n");
(*i)->eraseFromParent(); // 删除之前的永真条件分支
}

// 删除之前存储的Fcmp指令
for(std::vector<Instruction*>::iterator i =toDelete.begin();i!=toDelete.end();++i){
DEBUG_WITH_TYPE("gen", errs() << "bcf: Erase condition instruction:"
<< *((Instruction*)*i)<< "\n");
(*i)->eraseFromParent();
}
....
return true;
} // end of doFinalization
}; // end of struct BogusControlFlow : public FunctionPass
}

Flattening控制流平坦化

https://zhuanlan.zhihu.com/p/39479793

FunctionPass

混淆在flatten函数中进行

1
2
3
4
5
6
7
8
9
10
11
struct Flattening : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
bool flag;

Flattening() : FunctionPass(ID) {}
Flattening(bool flag) : FunctionPass(ID) { this->flag = flag; }

bool runOnFunction(Function &F);
bool flatten(Function *f);
};
}

flatten

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool Flattening::flatten(Function *f) {
vector<BasicBlock *> origBB;
BasicBlock *loopEntry;
BasicBlock *loopEnd;
LoadInst *load;
SwitchInst *switchI;
AllocaInst *switchVar;

// 或获取一个 16 字节的随机密钥
char scrambling_key[16];
llvm::cryptoutils->get_bytes(scrambling_key, 16);

//转换函数中的 switch 语句
FunctionPass *lower = createLowerSwitchPass();
lower->runOnFunction(*f);

reateLowerSwitchPass() 是一个函数,可能是用来创建一个 FunctionPass 的实例,这个 pass 的功能是将 switch 语句转换成等价的控制流结构

遍历函数基本块:遍历 f 中的每个基本块,并将它们存储在 origBB 向量中。

**检查 InvokeInst**:如果任何一个基本块的终止指令是 InvokeInst,则立即返回 false,不进行后续处理。这表明函数中如果有异常处理结构(invoke 调用),则不执行平坦化操作。

检查基本块数量:如果函数只有一个或没有基本块,返回 false,表示没有需要平坦化的内容。

移除第一个基本块:如果基本块数量大于 1,移除第一个基本块,可能是为了跳过不需要处理的起始块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//这行代码遍历所有基本块,将基本块添加到 origBB 向量中
for (Function::iterator i = f->begin(); i != f->end(); ++i) {
BasicBlock *tmp = &*i;
origBB.push_back(tmp);

BasicBlock *bb = &*i;
//如果函数中存在异常处理的调用,flatten 操作将被中止,因为这种结构无法被简单地“平坦化”。
//InvokeInst 是 LLVM 中的一种特殊的函数调用指令,用于调用可能抛出异常的函数
if (isa<InvokeInst>(bb->getTerminator())) {//bb->getTerminator() 返回当前基本块的终止指令
return false;
}
}

// 如果基本块数量小于等于 1,函数返回 false,表示没有操作的必要。
if (origBB.size() <= 1) {
return false;
}

// 删除origBB.begin() 位置的元素。为了去除函数中的某个起始基本块
origBB.erase(origBB.begin());

为什么基本块中含有 InvokeInst 就不进行平坦化处理?

在 LLVM 的中间表示(IR)中,InvokeInst 的特殊性决定了当函数中包含此类指令时,不能进行平坦化处理(flattening)。这是因为 InvokeInst 涉及到异常处理机制,且其控制流路径较为复杂。具体原因如下:

  1. 异常处理路径的特殊性InvokeInst 指令的控制流具有两个后继基本块:
    • 正常路径:如果没有异常发生,控制流按照正常路径继续。
    • 异常路径:如果发生异常,控制流转移到异常处理的基本块,通常是以 LandingPadInst 为开头的基本块。
  2. LandingPadInst 的约束LandingPadInst 是用于异常处理的指令,它必须位于异常处理路径的开头,并且只能通过 InvokeInst 来访问。因此,LandingPadInst 不应该出现在普通的控制流中,而应该通过 InvokeInst 来“保护”。
  3. 控制流的平坦化:控制流平坦化(flattening)通常是将多个基本块合并成一个较为简单的结构。在存在 InvokeInst 的情况下,平坦化操作可能会导致异常处理路径无法正常工作,因为平坦化可能会打破异常路径和 LandingPadInst 之间的正确关系。如果将 LandingPadInst 放入了一个非异常路径的基本块中,或者将异常处理路径错误地合并到正常路径中,就会导致异常机制的破坏,进而引发控制流错误或运行时错误。
  4. 避免破坏异常控制流:为了保持异常处理机制的完整性,LLVM 在检测到 InvokeInst 时会避免对包含该指令的函数进行平坦化处理,以防止控制流出现混乱。具体来说,如果函数中包含 InvokeInst,它的异常处理路径必须独立于其他控制流路径,以确保异常能够被正确捕获和处理。

检查基本块的终止指令是否为条件跳转或具有多个后继基本块,如果是,则需要拆分基本块。

拆分基本块:将终止指令前的指令分割到新的基本块中。删除原基本块中的终止指令,以便后续调整控制流。

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
 //获取函数f中的第一个基本块
Function::iterator tmp = f->begin(); //++tmp;
BasicBlock *insert = &*tmp;

// 检查 insert 基本块的终止指令是否是一个 BranchInst(跳转指令)
BranchInst *br = NULL;
if (isa<BranchInst>(insert->getTerminator())) {
br = cast<BranchInst>(insert->getTerminator());
}
//判断检查是否需要对当前的基本块进行拆分
//如果 br(跳转指令)不为空,并且 br 是一个条件跳转(即根据条件选择两个不同的跳转目标),则需要拆分基本块。
//如果基本块的终止指令有多个后继(即该基本块跳转到多个不同的基本块),也需要拆分。
if ((br != NULL && br->isConditional()) ||insert->getTerminator()->getNumSuccessors() > 1) {
//初始化一个迭代器 i,指向基本块 insert 的最后一条指令,然后将 i 向前移动一个位置,指向最后一条指令之前的位置。
BasicBlock::iterator i = insert->end();
--i;
//确定拆分点的位置,通常拆分点位于控制流指令(如 br)之前。
if (insert->size() > 1) {
--i;
}
//将基本块 insert 从指令 i 处分割成两个基本块
BasicBlock *tmpBB = insert->splitBasicBlock(i, "first");
//将新创建的基本块 tmpBB 插入到 origBB 向量的开头
origBB.insert(origBB.begin(), tmpBB);
}
// 删除函数f中的终止指令(即跳转指令)
insert->getTerminator()->eraseFromParent();

构建一个基于 switch 的循环结构,用于实现控制流平坦化。具体逻辑如下:

  1. 创建一个循环入口和结束基本块
    • loopEntry 作为 switch 的入口。
    • loopEnd 作为循环的终点,并跳转回 loopEntry
  2. 创建并初始化 switch 控制变量
    • 使用 switchVar 作为控制流选择的依据,并通过 LoadInst 从中加载值。
  3. 设置 switch 指令的默认行为
    • 创建 swDefault 作为默认分支,处理意外情况。
  4. 调整原控制流
    • 将第一个基本块插入新的循环逻辑中,并删除其原有的跳转逻辑。
  5. 引入 switch 指令
    • 使用 switch 指令将控制流转化为基于数值的分支跳转。
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
 // 创建 switch 变量
switchVar =new AllocaInst(Type::getInt32Ty(f->getContext()), 0, "switchVar", insert);
new StoreInst(
ConstantInt::get(Type::getInt32Ty(f->getContext()),
llvm::cryptoutils->scramble32(0, scrambling_key)),
switchVar, insert);

// 创建两个基本块
loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert);//循环入口基本块,作为 switch 的入口
loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert);//循环结束基本块,控制流回到 loopEntry
//在 loopEntry 中插入一条 LoadInst 指令,用于从 switchVar 加载当前值。这一值决定了 switch 的分支跳转。
load = new LoadInst(switchVar, "switchVar", loopEntry);

// 将原来的第一个基本块(insert)移动到 loopEntry 的前面。
insert->moveBefore(loopEntry);
//在 insert 的末尾插入一条无条件跳转指令,跳转到 loopEntry,将控制流接入新的循环逻辑。
BranchInst::Create(loopEntry, insert);

//设置 loopEnd 跳转回 loopEntry
BranchInst::Create(loopEntry, loopEnd);
//创建 switch 默认分支
BasicBlock *swDefault =BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd);
BranchInst::Create(loopEnd, swDefault);//在 swDefault 的末尾插入一条无条件跳转指令,使其跳转到 loopEnd

// 创建 switch 指令并设置条件
switchI = SwitchInst::Create(&*f->begin(), swDefault, 0, loopEntry);
switchI->setCondition(load);

//删除第一个基本块的原始终止指令
f->begin()->getTerminator()->eraseFromParent();
//在第一个基本块的末尾插入一条无条件跳转指令,使其直接跳转到 loopEntry,将控制流引入新的循环逻辑。
BranchInst::Create(loopEntry, &*f->begin());

**遍历所有原始基本块 (origBB)**:每个基本块代表程序逻辑中的一个独立部分。

调整基本块位置:将这些基本块移动到 loopEnd 之前,仅调整它们在 IR 中的视觉顺序,不影响实际逻辑。

switch 添加分支

  • 通过 scramble32 生成混淆后的 case 值,增加逆向工程的难度。
  • 使用 addCase 方法将每个基本块与对应的 case 值关联起来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 遍历origBB的所有基本块
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();++b) {
BasicBlock *i = *b;
ConstantInt *numCase = NULL;

//将当前基本块 i 移动到 loopEnd 之前
i->moveBefore(loopEnd);

// 创建并添加 switch case
numCase = cast<ConstantInt>(ConstantInt::get(
switchI->getCondition()->getType(),
llvm::cryptoutils->scramble32(switchI->getNumCases(), scrambling_key)));
switchI->addCase(numCase, i);
}

将基本块的分支逻辑转化为对 switch 指令变量的更新操作,从而实现通过 switch 指令完成所有分支跳转

处理无后继基本块:跳过不影响控制流的基本块(如返回块)。

处理单分支基本块:直接将后继基本块的 case 值存储到 switch 变量。

处理双分支基本块:根据分支条件选择对应的 case 值,并存储到 switch 变量。

跳转到循环结束:所有处理后的基本块统一跳转到 loopEnd,形成平坦化的控制流。

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
76
77
78
//遍历origBB中的每个基本块
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();++b) {
BasicBlock *i = *b;
ConstantInt *numCase = NULL;

// 如果基本块 i 没有后继(如 return 或 unreachable 的基本块),直接跳过
if (i->getTerminator()->getNumSuccessors() == 0) {
continue;
}

// 处理单分支(非条件跳转)的基本块
if (i->getTerminator()->getNumSuccessors() == 1) {//如果基本块的终止指令只有一个后继基本块,说明是无条件跳转
//使用 getSuccessor(0) 获取后继基本块指针
BasicBlock *succ = i->getTerminator()->getSuccessor(0);
// 删除该基本块原本的跳转指令
i->getTerminator()->eraseFromParent();

// 查找后继基本块对应的 switch case 值
numCase = switchI->findCaseDest(succ);

//如果后继基本块没有明确的 case 值(对应 switchDefault),生成一个加扰后的默认值
if (numCase == NULL) {
numCase = cast<ConstantInt>(
ConstantInt::get(switchI->getCondition()->getType(),
llvm::cryptoutils->scramble32(
switchI->getNumCases() - 1, scrambling_key)));
}

// 使用 StoreInst 将计算出的 numCase 存入 switchVar
new StoreInst(numCase, load->getPointerOperand(), i);
//将基本块的控制流引导到 loopEnd
BranchInst::Create(loopEnd, i);
continue;
}

// 处理双分支(条件跳转)的基本块
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)));
}

// 根据分支条件 br->getCondition() 创建一个选择器 sel
BranchInst *br = cast<BranchInst>(i->getTerminator());
SelectInst *sel =
SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "",
i->getTerminator());//sel 的值为 numCaseTrue 或 numCaseFalse,取决于分支条件

// 删除条件跳转指令(br
i->getTerminator()->eraseFromParent();

// 使用 StoreInst 将选择器结果存储到 switchVar,以更新 switch 的控制变量
new StoreInst(sel, load->getPointerOperand(), i);
//将当前基本块跳转到 loopEnd
BranchInst::Create(loopEnd, i);
continue;
}
}
//用于修复函数堆栈布局,以确保转换后的 IR 在运行时堆栈一致性
fixStack(f);

return true;
}

评论