簡介
之前的教學中,簡單的演示了LLVM的基本用法,下面,展示一個實戰項目。
編譯目標
本次的實驗編譯樣例是下面判斷正數,負數,和零的代碼
#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的核心代碼
代碼流程:
識別與收集分支路徑(代碼塊)-> 構建控制骨架 -> 初始化狀態變量 -> 配置分發器 -> 重構跳轉邏輯
我將代碼的解釋都標註在註釋中
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();
}
}
}
}
}
};
}
編譯和使用
這部分之前文章講過,這裏就不浪費篇幅,不懂的可以翻看
使用效果演示
未使用時

使用後

至此,我們成功實現了控制流平坦化
如果❤喜歡❤本系列教程,就點個關注吧,後續不定期更新~