当前位置: 首页 > news >正文

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

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

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

http://www.jsqmd.com/news/347177/

相关文章:

  • 在service方法中已经catch异常,Transactional失效怎么办 - 教程
  • 拼多多 最新 anti-content 分析
  • 【信息融合与状态估计】时滞系统的协方差交叉融合估计研究附Matlab代码
  • 建立普通的基类Building,用来存储一座楼房的层数、房间数以及它的总平方米数。建立派生类House,继承Building,并存储下面的内容:卧室与浴室的数量。另外,建立派生类Office,继承Bu
  • 【信息矩阵融合算法】【融合】【Dempster-Shafer】自动驾驶应用中环境感知的目标级融合研究附Matlab代码
  • 【通信】基于DPCM编码及2DPSK调制数字频带通信系统Matlab仿真
  • LLVM的混淆之旅(五)-控制流平坦化混淆
  • 2026-02-05学习
  • 数字图像处理篇---颜色空间
  • 【信号调制】使用不同的分类器(逻辑回归分类器、决策树、随机森林、全连接密集层和CNN)来训练模型,以预测不同信噪比值下信号的调制类型附Python代码
  • 数字图像处理篇---颜色空间的发展历程
  • 软件时代正在终结?2026,一场静默的AI革命正重塑我们的工作与未来
  • 数字图像处理篇---图像高通滤波
  • Redmi AX6 TTL 救砖记录
  • 数字图像处理篇---图像合成
  • 深度学习篇---Diffusion模型
  • 数字图像处理篇---小波变换
  • WSL2 + Aider(Kimi) + Windows Trae 双擎开发环境实施文档
  • 数字图像处理篇---图像低通滤波
  • 数字图像处理篇---图像离散余弦变换
  • 市场风险的防范策略
  • Excel向下舍入利器ROUNDDOWN函数:精准截断与季度计算的优雅解决方案
  • 豆包能投广告吗?如何做豆包推广?一文讲透豆包营销新路径 - 品牌2025
  • SW草图绘制之圆弧绘制
  • Matplotlib 绘制多图
  • 寒假12
  • ArcGIS Python零基础脚本开发教程---11.4 Array类
  • C 环境设置
  • Vue3 混入:深入理解组件间的数据共享与复用
  • ArcGIS Python零基础脚本开发教程---11.3 Field类