Straw's B1og.

OLLVM-控制流平坦化

字数统计: 1.3k阅读时长: 6 min
2024/03/15

OLLVM-控制流平坦化

建议结合代码后的注释食用!

炸毛芙

第一步:保存基本块

控制流1

第二步:创建分发块和返回块

控制流2

第三步:实现分发块调度

控制流3

第四步:实现调度变量的自动调整

控制流4

第五步:修复PHI指令和逃逸变量

控制流5

代码:

FLattening.cpp

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
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Transforms/Utils.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Utils/Local.h"
#include "SplitBasicBlock.h"
#include "Utils.h"
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace llvm;
using std::vector;
namespace{
class Flattening :public FunctionPass{
public :
static char ID;

Flattening():FunctionPass(ID){
srand(time(0));
}
void flatten(Function &F);
bool runOnFunction(Function &F);

};

}
bool Flattening::runOnFunction(Function &F){
INIT_CONTEXT(F);
FunctionPass * pass =createSplitBasicBlockPass();
pass->runOnFunction(F);
flatten(F);
return true;
}
void Flattening::flatten(Function &F){
//第一步,保存除了入口块的基本块
vector<BasicBlock*> origBB;//创建一个容器
for(BasicBlock& BB:F){
origBB.push_back(&BB);
}//保存所有基本快
origBB.erase(origBB.begin());//移除第一个基本块
BasicBlock &entryBlock=F.getEntryBlock();//获取入口块
if(BranchInst *br=dyn_cast<BranchInst>(entryBlock.getTerminator())){
if(br->isConditional()){//判断是不是条件跳转
origBB.insert(origBB.begin(),entryBlock.splitBasicBlock(br));//是条件跳转,单独拎出来作为新的基本块
}
}
//第二步,创建分发块和返回块
BasicBlock *dispatchBB=BasicBlock::Create(*CONTEXT,"dispatchBB",&F,&entryBlock);//创建分发块
BasicBlock *retBB=BasicBlock::Create(*CONTEXT,"retBB",&F,&entryBlock);//创建返回块
entryBlock.moveBefore(dispatchBB);//移动入口块到最前面
entryBlock.getTerminator()->eraseFromParent();//踢掉入口块原来的跳转
BranchInst::Create(dispatchBB,&entryBlock);//入口块绝对跳转到分发快
BranchInst::Create(dispatchBB,retBB);//返回块到分发块

//第三部,实现分发块的调度功能
int randNumcase=rand();//rand参数
AllocaInst * swVarPtr =new AllocaInst(TYPE_I32,0,"swVar.ptr",entryBlock.getTerminator());//入口块创建alloca指令,插入到入口块终结指令前
new StoreInst(CONST_I32(randNumcase),swVarPtr,entryBlock.getTerminator());//对switch变量进行初始化
LoadInst *swVar=new LoadInst(TYPE_I32,swVarPtr,"swVAR",dispatchBB);//添加switch指令
BasicBlock *defaultBB = BasicBlock::Create(*CONTEXT,"defaultBB",&F,retBB);//创建一个返回块前的基本块
BranchInst::Create(retBB,defaultBB); //基本块肯定要有一条终结指令,所以这里随便添加一条跳转指令
SwitchInst *swInst=SwitchInst::Create(swVar,defaultBB,0,dispatchBB);//插入到分发块的后面

//为每个基本块分配随机的case值
for(BasicBlock *BB:origBB){
BB->moveBefore(retBB);//移动到返回块前面
swInst->addCase(CONST_I32(randNumcase),BB);//保存随机数
randNumcase=rand();//重新生成随机数

}
//第四步 实现调度变量自动调整
for(BasicBlock *BB:origBB){
if(BB->getTerminator()->getNumSuccessors()==0){//如果这个基本块的最后一条指令是返回指令(没有后继快)
continue;
} else if(BB->getTerminator()->getNumSuccessors()==1){//如果这个基本块还有一个后继块(即为绝对跳转)
ConstantInt *numcase = swInst->findCaseDest(BB->getTerminator()->getSuccessor(0));//找到对应的case值
new StoreInst(numcase,swVarPtr,BB->getTerminator());//修改swicth中的参数,修改为对应case
BB->getTerminator()->eraseFromParent();//原先的跳转删除
BranchInst::Create(retBB,BB);//添加跳转
}else if(BB->getTerminator()->getNumSuccessors()==2){ //有两个后继块
ConstantInt *numcase1 = swInst->findCaseDest(BB->getTerminator()->getSuccessor(0));
ConstantInt *numcase2 = swInst->findCaseDest(BB->getTerminator()->getSuccessor(1));//全找了
BranchInst *br1=cast<BranchInst>(BB->getTerminator()); //两个分支也有可能是switch指令,但是在混淆前,使用LLVM IR指令 (使用自带的 LowerSwitch的LLVM Pass),将switch替换成了branch,所以这里最后一条指令只能是branch
SelectInst *sel=SelectInst::Create(br1->getCondition(),numcase1,numcase2,"",BB->getTerminator());//三元运算符,找到对应的case
new StoreInst(sel,swVarPtr,BB->getTerminator());//保存在switch变量
BB->getTerminator()->eraseFromParent(); //原先的跳转删除
BranchInst::Create(retBB,BB);//添加
}
}
//第五步 修复 phi指令和逃逸变量
fixStack(F);
}

char Flattening::ID=0;
static RegisterPass<Flattening>X("fla","My control flow flattening obfuscation");

Utils.cpp

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
#include"Utils.h"
#include<vector>
#include "llvm/IR/Instructions.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include "llvm/Transforms/Utils/Cloning.h"
using std::vector;
using namespace llvm;
LLVMContext *CONTEXT=nullptr;
//第五步 修复PHI指令和逃逸变量
void llvm::fixStack(Function &F){
vector<PHINode*> origPHI;
vector<Instruction*> origReg;
BasicBlock &entryBB=F.getEntryBlock();
for(BasicBlock &BB:F){
for (Instruction &I:BB)
{
if(PHINode *PN=dyn_cast<PHINode>(&I)){
origPHI.push_back(PN);
}else if(!(isa<AllocaInst>(&I)&& I.getParent()==&entryBB)&& I.isUsedOutsideOfBlock(&BB)){//判断是不是逃逸变量(除了通过Alloc,并且定义在入口块的,都要处理)
origReg.push_back(&I);
}
}

}
for(PHINode *PN:origPHI){
DemotePHIToStack(PN,entryBB.getTerminator());//修复PHI指令
}
for(Instruction *I:origReg){
DemoteRegToStack(*I,entryBB.getTerminator());//修复逃逸变量
}
}

Utils.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef _UTILS_H_
#define _UTILS_H_

#include "llvm/IR/Function.h"
#define INIT_CONTEXT(X) CONTEXT = &X.getContext()
#define TYPE_I64 Type::getInt64Ty(*CONTEXT)
#define TYPE_I32 Type::getInt32Ty(*CONTEXT)
#define TYPE_I8 Type::getInt8Ty(*CONTEXT)
#define GET_TYPE(X) TYPE::getInt(X) Ty(*CONTEXT)
#define CONST_I64(V) ConstantInt::get(TYPE_I64, V, false)
#define CONST_I32(V) ConstantInt::get(TYPE_I32, V, false)
#define CONST_I8(V) ConstantInt::get(TYPE_I8, V, false)
#define CONST(T, V) ConstantInt::get(T, V)
#define RANDOM(X) (cryptoutils->get_uint8_t() % 100 < X)
extern llvm::LLVMContext *CONTEXT;

namespace llvm{
void fixStack(Function &F);
BasicBlock* createCloneBasicBlock(BasicBlock *BB);
}

#endif
CATALOG
  1. 1. OLLVM-控制流平坦化
    1. 1.0.0.1. 第一步:保存基本块
    2. 1.0.0.2. 第二步:创建分发块和返回块
    3. 1.0.0.3. 第三步:实现分发块调度
    4. 1.0.0.4. 第四步:实现调度变量的自动调整
    5. 1.0.0.5. 第五步:修复PHI指令和逃逸变量
    6. 1.0.0.6. 代码: