OLLVM混淆学习(2)——虚假控制流(BCF)
0x01 虚假控制流基本介绍
简单的说就是在原来的程序流程中克隆原基本块,利用不透明谓词将克隆的基本块作为永不可达的虚假分支,并在这个不可达的虚假分支中随机添加垃圾指令,以达到程序混淆的效果,得到的程序流程图和控制流平坦化不同,是程长条型的。
所谓不透明谓词就是:
“不透明谓词是指一个表达式,他的值在执行到某处时,对程序员而言必然是已知的,但是由于某种原因,编译器或者说静态分析器无法推断出这个值,只能在运行时确定。”简单来说是程序员在编写的时候知道该表达式的结果,但是IDA在分析的时候无法分析出该结果,达到混淆的目的。
0x02 实现方法
添加混淆轮次参数
// 混淆次数,混淆次数越多混淆结果越复杂
static cl::opt<int> obfuTimes("bcf_loop", cl::init(1), cl::desc("Obfuscate a function <bcf_loop> time(s)."));
1. 基本块拆分
将基本块拆分成头部、中部和尾部三个基本块。
通过 getFirstNonPHI 函数获取第一个不是 PHINode 的指令,以该指令为界限进行分割,得到 entryBB 和 bodyBB。以 bodyBB 的终结指令为界限进行分割,最终得到头部、中部和尾部三个基本块,也就是 entryBB, bodyBB 和 endBB。
// 第一步,拆分得到 entryBB, bodyBB, endBB
// 其中所有的 PHI 指令都在 entryBB(如果有的话)
// endBB 只包含一条终结指令
BasicBlock *bodyBB = entryBB->splitBasicBlock(entryBB->getFirstNonPHI(), "bodyBB");
BasicBlock *endBB = bodyBB->splitBasicBlock(bodyBB->getTerminator(), "endBB");
2. 基本块克隆
LLVM 自带 CloneBasicBlock 函数,但该函数为不完全克隆,在克隆的基本块中,仍然引用了原基本块中的 %a 变量,该引用是非法的,故需要将 %a 映射为 %a.clone,所以还需要做优化。
// 第二步,克隆 bodyBB 得到克隆块 cloneBB
BasicBlock *cloneBB = createCloneBasicBlock(bodyBB);
BasicBlock* createCloneBasicBlock(BasicBlock *BB){
// 克隆之前先修复所有逃逸变量
vector<Instruction*> origReg;
BasicBlock &entryBB = BB->getParent()->getEntryBlock();
for(Instruction &I : *BB){
if(!(isa<AllocaInst>(&I) && I.getParent() == &entryBB)
&& I.isUsedOutsideOfBlock(BB)){
origReg.push_back(&I);
}
}
for(Instruction *I : origReg){
DemoteRegToStack(*I, entryBB.getTerminator());
}
ValueToValueMapTy VMap;
BasicBlock *cloneBB = CloneBasicBlock(BB, VMap, "cloneBB", BB->getParent());
// 对克隆基本块的引用进行修复
for(Instruction &I : *cloneBB){
for(int i = 0;i < I.getNumOperands();i ++){
Value *V = MapValue(I.getOperand(i), VMap);
if(V){
I.setOperand(i, V);
}
}
}
return cloneBB;
}
3. 构造虚假跳转
将 entryBB 到 bodyBB 的绝对跳转改为条件跳转;将 bodyBB 到 endBB 的绝对跳转改为条件跳转;添加 cloneBB 到 bodyBB 的绝对跳转
// 第三步,构造虚假跳转
// 1. 将 entryBB, bodyBB, cloneBB 末尾的绝对跳转移除
entryBB->getTerminator()->eraseFromParent();
bodyBB->getTerminator()->eraseFromParent();
cloneBB->getTerminator()->eraseFromParent();
// 2. 在 entryBB 和 bodyBB 的末尾插入条件恒为真的虚假比较指令
Value *cond1 = createBogusCmp(entryBB);
Value *cond2 = createBogusCmp(bodyBB);
// 3. 将 entryBB 到 bodyBB 的绝对跳转改为条件跳转
BranchInst::Create(bodyBB, cloneBB, cond1, entryBB);
// 4. 将 bodyBB 到 endBB的绝对跳转改为条件跳转
BranchInst::Create(endBB, cloneBB, cond2, bodyBB);
// 5. 添加 bodyBB.clone 到 bodyBB 的绝对跳转
BranchInst::Create(bodyBB, cloneBB);
createBogusCmp函数的具体实现,构造恒成立式子
Value* BogusControlFlow::createBogusCmp(BasicBlock *insertAfter){
// if((y < 10 || x * (x + 1) % 2 == 0))
// 等价于 if(true)
Module *M = insertAfter->getModule();
GlobalVariable *xptr = new GlobalVariable(*M, TYPE_I32, false, GlobalValue::CommonLinkage, CONST_I32(0), "x");
GlobalVariable *yptr = new GlobalVariable(*M, TYPE_I32, false, GlobalValue::CommonLinkage, CONST_I32(0), "y");
LoadInst *x = new LoadInst(TYPE_I32, xptr, "", insertAfter);
LoadInst *y = new LoadInst(TYPE_I32, yptr, "", insertAfter);
ICmpInst *cond1 = new ICmpInst(*insertAfter, CmpInst::ICMP_SLT, y, CONST_I32(10));
BinaryOperator *op1 = BinaryOperator::CreateAdd(x, CONST_I32(1), "", insertAfter);
BinaryOperator *op2 = BinaryOperator::CreateMul(op1, x, "", insertAfter);
BinaryOperator *op3 = BinaryOperator::CreateURem(op2, CONST_I32(2), "", insertAfter);
ICmpInst *cond2 = new ICmpInst(*insertAfter, CmpInst::ICMP_EQ, op3, CONST_I32(0));
return BinaryOperator::CreateOr(cond1, cond2, "", insertAfter);
}
0x03 参考资料
https://www.anquanke.com/post/id/212768
https://bbs.pediy.com/thread-266201.htm
https://www.zhihu.com/question/46259412/answer/199689652
https://github.com/bluesadi/Pluto-Obfuscator/blob/kanxue/Transforms/src/BogusControlFlow.cpp