简介
之前的教学中,简单的演示了LLVM的基本用法,下面,展示一个实战项目。
编译目标
本次的实验编译样例是下面判断正数,负数,和零的代码
c
#include <stdio.h>
int main() {
int a = 9;
scanf_s("%d", &a);
if (a < 0) {
printf("Negative number!\n");
} else if(a > 0) {
printf("Positive number!\n");
} else {
printf("Zero!\n");
}
printf("Done.\n");
return 0;
}
一,控制流混淆平坦化
简介
什么是控制流平坦化?简单来说,就是让原本垂直的流程分支平摊到水平方向上,使用这种方法可以提高逆向难度,软件更耐造。
+-----------------------+
| [开始] |
| 设定初始状态 = 1 |
+-----------|-----------+
|
+---------------->V<----------------+
| +-------------------+ |
| | 循环控制中心 | |
| | (分发器) | |
| +---------|---------+ |
| | |
| _________V_________ |
| | | |
| | switch(状态变量) | |
| |___________________| |
| / | \ |
| / | \ |
| [状态 1] [状态 2] [退出] |
| +-----+ +-----+ +-----+ |
| | 块 1| | 块 2 | |结束 | |
| | | | | +-----+ |
| |更新 | | 更新 | |
| |状态 | | 状态 | |
| +--|--+ +--|--+ |
| | | |
+------+----------+-----------------+
实现控制流平坦化Pass的核心代码
代码流程:
识别与收集分支路径(代码块)-> 构建控制骨架 -> 初始化状态变量 -> 配置分发器 -> 重构跳转逻辑
我将代码的解释都标注在注释中
cpp
namespace{
struct mypass : public PassInfoMixin<mypass>{
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM){
//这里为了演示只混淆main函数
if(F.getName() != "main"){
return PreservedAnalyses::all();
}
errs() << "My Flattening Function:" << F.getName() << "\n";
//通过遍历,获取main函数中的所有代码块
std::vector<BasicBlock*> OrigBBs;
DenseMap<BasicBlock*, int> BBtoID;
BasicBlock &EntryBB = F.getEntryBlock();
int id_counter = 0;
for(BasicBlock &BB : F){
//这里去除首代码块
if(&BB == &EntryBB) continue;
//记录代码块并给代码块标序号
//这里主要方便case中使用,可以是很复杂的独一无二的数字
OrigBBs.push_back(&BB);
BBtoID[&BB] = id_counter++;
}
//判断收集到的分支是否存在,如果没有分支,就
if(OrigBBs.empty()){
return PreservedAnalyses::all();
}
//创建循环控制代码块
BasicBlock *LoopEntry = BasicBlock::Create(F.getContext(), "loop_entry", &F);
//控制流分发器代码块
BasicBlock *SwitchBB = BasicBlock::Create(F.getContext(), "switch_block", &F);
//循环分发器尾部(用于兜底,可以不使用)
BasicBlock *LoopEnd = BasicBlock::Create(F.getContext(), "loop_end", &F);
//在首代码块中,创建初始状态变量,用于获取刚开始跳转的状态量
IRBuilder<> builderEntry(&EntryBB);
Instruction *EntryTerm = EntryBB.getTerminator();
if (EntryTerm) builderEntry.SetInsertPoint(EntryTerm);
AllocaInst *SwitchVar = builderEntry.CreateAlloca(builderEntry.getInt32Ty(), nullptr, "switchVar");
//获取跳转指令
if(BranchInst *BI = dyn_cast_or_null<BranchInst>(EntryTerm)){
//判断是否是条件跳转
//如果是,则直接获取跳转后的代码块序号
if(BI->isUnconditional()){
BasicBlock *Target = BI->getSuccessor(0);
if(BBtoID.count(Target)){
builderEntry.CreateStore(builderEntry.getInt32(BBtoID[Target]), SwitchVar);
}
//如果是条件跳转
//创建一条条件判断指令
}else if(BI->isConditional()){
Value *Cond = BI->getCondition();
BasicBlock *TrueBB = BI->getSuccessor(0);
BasicBlock *FalseBB = BI->getSuccessor(1);
if(BBtoID.count(TrueBB) && BBtoID.count(FalseBB)){
Value*Select = builderEntry.CreateSelect(
Cond,
builderEntry.getInt32(BBtoID[TrueBB]),
builderEntry.getInt32(BBtoID[FalseBB]),
"init_state"
);
builderEntry.CreateStore(Select, SwitchVar);
}
}
}
//首部创建跳转->跳转到循环控制入口
builderEntry.CreateBr(LoopEntry);
//删除旧的跳转指令
if(EntryTerm) EntryTerm->eraseFromParent();
//创建状态变量,用于控制分发器的走向
IRBuilder<> builderLoop(LoopEntry);
LoadInst *CurrState = builderLoop.CreateLoad(builderLoop.getInt32Ty(), SwitchVar, "curr_state");
builderLoop.CreateBr(SwitchBB);
//创建switch指令
IRBuilder<> builderSwitch(SwitchBB);
SwitchInst *SwitchI = builderSwitch.CreateSwitch(CurrState, LoopEnd, OrigBBs.size());
//创建case
//其中的标识符是上面收集到的代码块的序号
for(BasicBlock *BB :OrigBBs){
//所有都跳转到尾部
BB->moveBefore(LoopEnd);
SwitchI->addCase(builderSwitch.getInt32(BBtoID[BB]), BB);
}
//用于兜底,跳转回循环控制头部
IRBuilder<> builderEnd(LoopEnd);
builderEnd.CreateBr(LoopEntry);
//遍历每个代码块,插入修改状态变量的指令
//这里根据下一个跳转到的代码块来标识状态变量
//这里的代码跟获取初次跳转数值一模一样
for(BasicBlock *BB : OrigBBs){
Instruction *Term = BB->getTerminator();
IRBuilder<> builder(BB);
//由于跳转分为有条件跳转和无条件跳转
//这里照样要做个判断
if(BranchInst*BI = dyn_cast_or_null<BranchInst>(Term)){
if(BI->isUnconditional()){
BasicBlock *Target = BI->getSuccessor(0);
if(BBtoID.count(Target)){
builder.SetInsertPoint(Term);
builder.CreateStore(builderSwitch.getInt32(BBtoID[Target]), SwitchVar);
builder.CreateBr(LoopEntry);
Term->eraseFromParent();
}
}
else if(BI->isConditional()){
BasicBlock *TrueBB = BI->getSuccessor(0);
BasicBlock *FalseBB = BI->getSuccessor(1);
if(BBtoID.count(TrueBB) && BBtoID.count(FalseBB)){
builder.SetInsertPoint(Term);
Value *Select = builder.CreateSelect(
BI->getCondition(),
builder.getInt32(BBtoID[TrueBB]),
builder.getInt32(BBtoID[FalseBB]),
"next_state"
);
builder.CreateStore(Select, SwitchVar);
builder.CreateBr(LoopEntry);
Term->eraseFromParent();
}
}
}
}
}
};
}
编译和使用
这部分之前文章讲过,这里就不浪费篇幅,不懂的可以翻看
使用效果演示
未使用时
使用后
至此,我们成功实现了控制流平坦化
如果❤喜欢❤本系列教程,就点个关注吧,后续不定期更新~