ollvm_BCF源码阅读
虚假控制流 BCF (Bogus Control Flow)
原理:
- 虚假控制流混淆通过加入包含不透明谓词的条件跳转(永真or永假)和不可达的基本块,来干扰 IDA 的控制流分析和 F5 反汇编。
反混淆:
1、将全局变量赋值并将 segment 设为只读。
- 对于常规的 ollvm 的 bcf 混淆来说,bcf 的不透明谓词都是处于 .bss段 中。
- Edit->Segments->Edit segment 将 Write 复选框取消勾选, .bss段 就设为只读
2、d810
3、idapython patch 不透明谓词
源码位置:
obfuscator\lib\Transforms\Obfuscation\BogusControlFlow.cpp
源码解析
Pass结构与参数
1 | struct BogusControlFlow : public FunctionPass { |
继承自 LLVM 的 FunctionPass。
在 LLVM Pass 设计中,ID 用于标识插件自身,flag 可用来控制是否对目标函数实际作用(如按用户参数选择)。这里有两个构造函数,用于接收可选参数。
pass流程主入口
1 | virtual bool runOnFunction(Function &F){ //Pass的核心入口 |
随机选择、遍历每个基本块
bogus(F)
的主要逻辑是:循环要混淆多少轮,每轮遍历函数当前全部基本块。对每个BasicBlock按概率 ObfProbRate 随机决定是否插入虚假控制流——选中后调用addBogusFlow(basicBlock, F)。其中插入虚假流程会导致新生成3个基本块,因此统计量需相应自增。每一轮循环处理后,firstTime位设为false,避免重复初始化统计变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43void bogus(Function &F) {
++NumFunction;
int NumBasicBlocks = 0;
bool firstTime = true;
bool hasBeenModified = false;
int NumObfTimes = ObfTimes;
do {
std::list<BasicBlock *> basicBlocks;
// --- 1. 把当前F的所有基本块收集到list里 ---
for(Function::iterator i = F.begin(); i != F.end(); ++i){
basicBlocks.push_back(&*i);
}
// --- 2. 遍历所有基本块,按概率挑选 ---
while(!basicBlocks.empty()){
NumBasicBlocks ++;
BasicBlock *basicBlock = basicBlocks.front();
// -- 3. 按概率决定是否对该块混淆 --
if((int)cryptoutils->get_range(100) < ObfProbRate){
hasBeenModified = true;
++NumModifiedBasicBlocks; // 混淆的块计数
NumAddedBasicBlocks += 3; // 每次新增3个BB
FinalNumBasicBlocks += 3;
// --- 核心:对该块做bogus混淆 ----
addBogusFlow(basicBlock, F);
}
// -- 4. pop掉已经处理过的块 --
basicBlocks.pop_front();
// -- 5. 第一次遍历时,用于记录统计 --
if(firstTime){
++InitNumBasicBlocks;
++FinalNumBasicBlocks;
}
}
firstTime = false; // 只在第一轮初始化时统计
}
while(--NumObfTimes > 0); // 视混淆轮数多次叠加
}
拆分基本块,制造分支点
1 | BasicBlock::iterator i1 = basicBlock->begin(); |
将当前basicBlock按照首个非phi与非调试/声明作用域指令拆分:第一块只保留phi与必要的头部信息,剩余的内容新生成 originalBB。这样后续插入分支时可保证ir合法性,不会破坏phi变量。新originalBB成了插入跳转的目标。
创建原始块的变形副本
1 | Twine * var3 = new Twine("alteredBB"); |
生成了一份originalBB的深拷贝,并按后续代码在其中间随机插入了垃圾指令
控制流扇出与插入恒真判定
1 | // 准备条件恒为真的IR |
实际上就是1.0和1.0做浮点比较,谓词FCMP_TRUE代表“永远为真”。
再用它构造条件分支,如果 condition 为真跳 originalBB,否则跳 alteredBB。
但实际上一直走 originalBB。最后会由doF将该恒真判断替换成更复杂的表达式。
让alteredBB循环回originalBB
1 | BranchInst::Create(originalBB, alteredBB); |
alteredBB 的终止指令直接跳回 originalBB
再次拆分 originalBB 的结尾,伪造 return / alteredBF 跳转
1 | BasicBlock::iterator i = originalBB->end(); |
克隆原始基本块并插入垃圾指令
1 | BasicBlock * alteredBB = llvm::CloneBasicBlock (basicBlock, VMap, Name, F); |
用 LLVM 提供的 CloneBasicBlock API 深拷贝一个IR基本块,然后对其中每条指令做参数和 phi 节点等的 remapping,保证各指向正确,然后按照类型继续在合适位置插入垃圾指令。
全局替换恒真判定条件
1 | //(1)遍历找所有 FCMP_TRUE 的分支 |
总而言之是将所有的假跳转复杂化
pass 混淆思路核心
1、随机选取基本块
2、将其一拆三,并构造冗余always-true条件和虚假分支
3、插入带有垃圾指令的altered副本块
4、算法完成后,再把这些恒真的 predicate 全变成复杂的“不透明谓词”
5、达到控制流与符号流分析难度大幅提升的效果