LLVM的混淆之旅(五)-手动实现控制流平坦化混淆

简介

之前的教学中,简单的演示了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();
                        }
                    }
                }
            }
        }
    };
}

编译和使用

这部分之前文章讲过,这里就不浪费篇幅,不懂的可以翻看

使用效果演示

未使用时

控制流3
控制流4

使用后

控制流1
控制流2

至此,我们成功实现了控制流平坦化

如果❤喜欢❤本系列教程,就点个关注吧,后续不定期更新~

posted @ 2026-02-05 22:54  ClownLMe  阅读(1)  评论(0)    收藏  举报