从0到1的ollvm-3-基于llvm-pass自制fla混淆
MyOllvm
大致思路
对于每一个Function,我们要对其BasicBlock进行处理
这里我把其Block类型大致分为入口点的EntryBlock,其他的普通分支BasicBlock,和返回块EndBlock

我们要做的,就是在EntryBlock中新建一个SwitchValue变量,并建立一个SwitchValueBB块,根据当前SwitchValue的值,去控制下一个执行的Block。对于每个BasicBlock,我们先给其一个唯一的SwitchVal Key,用哈希表储存起来。然后再重新遍历所有的BasicBlock,将其后继对应的SwitchVal Key给SwitchValue赋值,再跳回SwitchValueBB块,删除原来的terminator,也就是原来的跳转指令。
代码编写
我们先用哈希表对每一个基本块都赋予一个唯一的switchValue
1 | std::default_random_engine e; |
然后要插入建立SwitchValue变量的指令
如果想要建立一条新建switchVar的指令,要插入在EntryBlock中,不然可能影响优化、也可能在某些 CFG/异常路径下不符合预期。
但如果直接在插入末尾,则会出现Basic Block in function ‘cmp’ does not have terminator!的错误
1 | LLVMContext &Ctx = F.getContext(); |
BasicBlock 必须以 terminator 结束(br/ret/switch 等)。新建块后如果没插终结指令,IR 是非法的。
查看一波IR,SwitchValue已经在PHI之后最早插入点创建
1 | define dso_local i32 @cmp(i32 noundef %0, i32 noundef %1) #0 { |
然后是创建一个SwitchValue分配handler块
这里需要注意switch的后继,最好设置为自己,不能设置为EntryBlock
1 | BasicBlock *SwitchValueBB = BasicBlock::Create(Ctx, "SwitchValueBB", &F); |
查看IR的效果
1 | SwitchValueBB: ; preds = %16, %15, %14, %10, %9, %2, %SwitchValueBB |
然后就是对每个BasicBlock的关键处理
对于ret块,switch/indirectbr,我们直接略过不处理。
对于单一的无条件跳转,我们获取其后继哈希表对应的SwitchValue,在结尾进行赋值
对于条件跳转,我们需要分别获取条件True和False时对应产生的后继,并从哈希表获取对应的SwitchValue,通过创建select指令来给SwitchValue赋值
最后对于处理的情况,还需要将原terminator删除,使其都直接跳转到SwitchValueBB
1 | for (BasicBlock &BB : F) { |
再看一波生成的IR,已经没问题了
1 | define dso_local i32 @cmp(i32 noundef %0, i32 noundef %1) #0 { |
最终效果
用一个较复杂的xxtea算法测试一下,发现能够生成fla的实际效果,虽然cfg看起来一点也不平坦就是了

最后的完整代码
1 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { |
- 标题: 从0到1的ollvm-3-基于llvm-pass自制fla混淆
- 作者: 两只羊
- 创建于 : 2026-02-25 12:10:27
- 更新于 : 2026-02-25 22:14:16
- 链接: https://twogoat.github.io/2026/02/25/从0到1的ollvm-3-基于llvm-pass自制fla混淆/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。