1 添加pass
在目录 lib\Transforms\Obfuscation 新建MyBcf.cpp
剩下的 流程更上一篇一样
ollvm 添加新的命令参数并编写第一个pass_flygle~的博客-CSDN博客_ollvm 参数
什么是虚假控制流
虚假控制流指的是,通过向正常控制流中插入若干不可达基本块和由不透明谓词造成的虚假跳转以产生大量垃圾代码干扰攻击者分析的混淆
2 编写测试函数 来一个最简单的函数
int myadd(int a ,int b)__attribute((__annotate__(("mybcp"))));
int myadd(int a ,int b)
{
int i=10;
return a+b+i;
}
查看 cfg 可以看到只有一个基本块 接下来 的任务就是在此基本块的基础上增加一个虚假控制流
myadd 函数编译后的 IR 代码
接下来的pass会基于该基本块的IR 代码进行处理
BasicBlock
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i32, align 4
store i32 %0, i32* %3, align 4
call void @llvm.dbg.declare(metadata i32* %3, metadata !237, metadata !238), !dbg !239
store i32 %1, i32* %4, align 4
call void @llvm.dbg.declare(metadata i32* %4, metadata !240, metadata !238), !dbg !241
call void @llvm.dbg.declare(metadata i32* %5, metadata !242, metadata !238), !dbg !243
store i32 10, i32* %5, align 4, !dbg !243
%6 = load i32, i32* %3, align 4, !dbg !244
%7 = load i32, i32* %4, align 4, !dbg !245
%8 = add nsw i32 %6, %7, !dbg !246
%9 = load i32, i32* %5, align 4, !dbg !247
%10 = add nsw i32 %8, %9, !dbg !248
ret i32 %10, !dbg !249
以下 是IR代码常用的指令解释
* 语法简介
@ - 全局变量
% - 局部变量
alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
i32 - 32位4字节的整数
align - 对齐
store - 将数据写入到指定的内存中
load - 读出,store写入
icmp - 两个整数值比较,返回布尔值
br - 选择分支,根据条件来转向label,不根据条件跳转的话类型goto
label - 代码标签
call - 调用函数
虚假控制流原理:
首先会根据基本块进行分割 ,分割完毕创建一个永真的判定条件进行控制流分发 ,达到虚假的控制流目的
3 编写bcf pass代码
void MyBcf::mybogus(Function &F)
{
std::list<BasicBlock *> basicBlocks;
//遍历添加函数的所有基本块
int index = 0;
for (Function::iterator i=F.begin();i!=F.end();++i)
{
basicBlocks.push_back(&*i);
index++;
}
errs() <<"basicBlocks count "<< index << "\n";
while(!basicBlocks.empty())
{
//BasicBlock *originalBB = basicBlock->splitBasicBlock(i1, *var);
BasicBlock *basicBlock = basicBlocks.front();
errs() <<"basicB "<< *basicBlock << "\n";
//1 找到第一条指令
BasicBlock::iterator i1 = basicBlock->begin();
//获取本basicblock中第一个不是Phi、debug、terminator的指令的地址
//LLVM的指令类型中包含一种特殊节点叫 PhiNode,PhiNode 的存在是为了解决 LLVM IR 中因 SSA (静态单次赋值)引起的条件初始化问题
if(basicBlock->getFirstNonPHIOrDbgOrLifetime())
i1 = (BasicBlock::iterator)basicBlock->getFirstNonPHIOrDbgOrLifetime();
//2 然后调用splitBasicBlock函数。该函数根据上述指令的地址将一个basicblock进行拆分
Twine *var;
var = new Twine("originalBB");
BasicBlock *originalBB = basicBlock->splitBasicBlock(i1, *var);
//3 先脱离父节点的关系
errs() <<"eraseFromParent before "<< *basicBlock << "\n";
basicBlock->getTerminator()->eraseFromParent();
errs() <<"eraseFromParent after "<< *basicBlock << "\n";
//4 将originalBB 克隆一份
Twine * var3 = new Twine("copyBB");
BasicBlock *copyBB = createCopyBasicBlock(originalBB, *var3, &F);
//5 添加判定语句 永真 if(1.0 == 1.0)
Value * LHS = ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0);
Value * RHS = ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0);
Twine * var4 = new Twine("condition");
FCmpInst * condition = new FCmpInst(*basicBlock, FCmpInst::FCMP_TRUE , LHS, RHS, *var4);
//6 为真时跳转到originalBB,为假则跳转到alteredBB
BranchInst::Create(originalBB, copyBB, (Value *)condition, basicBlock);
errs() <<"basicBlock "<< *basicBlock << "\n";
errs() <<"copyBB "<< *copyBB << "\n";
errs() <<"originalBB "<< *originalBB << "\n";
basicBlocks.pop_front();
}
}
//复制一个基本块 随机加一些指令 因为用不到所以可以随便加
BasicBlock* MyBcf::createCopyBasicBlock(BasicBlock * basicBlock, const Twine &Name, Function *F )
{
ValueToValueMapTy VMap;
//先克隆基本块
//但是CloneBasicBlock函数进行的克隆并不是完全的克隆,第一他不会对指令的操作数进行替换
BasicBlock * alteredBB = llvm::CloneBasicBlock (basicBlock, VMap, Name, F);
//所以要通过VMap对所有操作数进行映射,使其恢复正常:
BasicBlock::iterator ji = basicBlock->begin();
for (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end() ; i != e; ++i)
{
for(User::op_iterator opi = i->op_begin (), ope = i->op_end(); opi != ope; ++opi){
// 获取操作数的值
Value *v = MapValue(*opi, VMap, RF_None, 0);
if (v != 0){
*opi = v;
}
}
//对phinode 进行处理
if (PHINode *pn = dyn_cast<PHINode>(i)) {
for (unsigned j = 0, e = pn->getNumIncomingValues(); j != e; ++j) {
Value *v = MapValue(pn->getIncomingBlock(j), VMap, RF_None, 0);
if (v != 0){
pn->setIncomingBlock(j, cast<BasicBlock>(v));
}
}
}
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
i->getAllMetadata(MDs);
i->setDebugLoc(ji->getDebugLoc());
ji++;
}
for (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end() ; i != e; ++i)
{
if(i->isBinaryOp()){
//获取操作码
unsigned opcode = i->getOpcode();
BinaryOperator *op, *op1 = NULL;
Twine *var = new Twine("_");
//opcode 操作码
if(opcode == Instruction::Add || opcode == Instruction::Sub )
{
op1 = BinaryOperator::Create(Instruction::Sub,
i->getOperand(0),
i->getOperand(1),*var,&*i);
op = BinaryOperator::Create(Instruction::Mul,op1,
i->getOperand(1),"gen",&*i);
}
}
}
return alteredBB;
}
4 代码解释
以上代码都加了注释 详细就不多说了
解释一下流程
1 先遍历添加函数的所有基本块 然后循环取出基本块 找到第一条指令
2 调用splitBasicBlock函数 进行分割 分割过后基本块会多一个标签
3 调用createCopyBasicBlock 会拷贝一个原始基本块 由于我们原始的基本块很简单就直接是加法 这里我只处理了opcde 为add的逻辑
处理逻辑如下 如果是指令add 就直接添加 一个sub 指令 和 mul 指令 当然也可以添加更多的指令
//opcode 操作码
if(opcode == Instruction::Add || opcode == Instruction::Sub )
{
op1 = BinaryOperator::Create(Instruction::Sub,
i->getOperand(0),
i->getOperand(1),*var,&*i);
op = BinaryOperator::Create(Instruction::Mul,op1,
i->getOperand(1),"gen",&*i);
}
以下是copy 出来的基本块 可以看到里面多了sub mul 指令
copyBB
; <label>:13: ; preds = %2
%14 = alloca i32, align 4
%15 = alloca i32, align 4
%16 = alloca i32, align 4
store i32 %0, i32* %14, align 4
call void @llvm.dbg.declare(metadata i32* %14, metadata !250, metadata !238), !dbg !239
store i32 %1, i32* %15, align 4
call void @llvm.dbg.declare(metadata i32* %15, metadata !284, metadata !238), !dbg !241
call void @llvm.dbg.declare(metadata i32* %16, metadata !285, metadata !238), !dbg !243
store i32 10, i32* %16, align 4, !dbg !243
%17 = load i32, i32* %14, align 4, !dbg !244
%18 = load i32, i32* %15, align 4, !dbg !245
%19 = sub i32 %17, %18
%20 = mul i32 %19, %18
%21 = add nsw i32 %17, %18, !dbg !246
%22 = load i32, i32* %16, align 4, !dbg !247
%23 = sub i32 %21, %22
%24 = mul i32 %23, %22
%25 = add nsw i32 %21, %22, !dbg !248
ret i32 %25, !dbg !249
4 添加判定语句 永真
这里逻辑就是 为true 就跳转到原始块, false 跳转到 copy 的块 ,由于判定永远为true 所以只能跳转到原始块 这样之前的代码逻辑改变的前提下 还多了一个判定逻辑,所以控制流也就慢慢出现雏形了 编译后 再看cfg 是不是就多了一些块了
5 替换永真语句为代数恒等式
添加两个全局变量 即不透明谓词 让ida无法识别 x y 的变量的值 自然也不会进行任何优化
bool MyBcf::doF(Module &M)
{
//添加全局变量
Twine * varX = new Twine("x");
Twine * varY = new Twine("y");
Value * x1 =ConstantInt::get(Type::getInt32Ty(M.getContext()), 0, false);
Value * y1 =ConstantInt::get(Type::getInt32Ty(M.getContext()), 0, false);
GlobalVariable * x = new GlobalVariable(M, Type::getInt32Ty(M.getContext()), false,
GlobalValue::CommonLinkage, (Constant * )x1,
*varX);
GlobalVariable * y = new GlobalVariable(M, Type::getInt32Ty(M.getContext()), false,
GlobalValue::CommonLinkage, (Constant * )y1,
*varY);
std::vector<Instruction*> toEdit, toDelete;
BinaryOperator *op,*op1 = NULL;
LoadInst * opX , * opY;
ICmpInst * condition, * condition2;
//在整个类 遍历 cmp 为true 的条件语句
// basicBlock
// %3 = fcmp true float 1.000000e+00, 1.000000e+00
// br i1 %3, label %4, label %13
for(Module::iterator mi = M.begin(), me = M.end(); mi != me; ++mi){
for(Function::iterator fi = mi->begin(), fe = mi->end(); fi != fe; ++fi){
//fi->setName("");
TerminatorInst * tbb= fi->getTerminator();
if(tbb->getOpcode() == Instruction::Br){
BranchInst * br = (BranchInst *)(tbb);
if(br->isConditional()){
FCmpInst * cond = (FCmpInst *)br->getCondition();
unsigned opcode = cond->getOpcode();
if(opcode == Instruction::FCmp){
if (cond->getPredicate() == FCmpInst::FCMP_TRUE){
DEBUG_WITH_TYPE("gen",
errs()<<"bcf: an always true predicate !\n");
toDelete.push_back(cond); // The condition
toEdit.push_back(tbb); // The branch using the condition
}
}
}
}
/*
for (BasicBlock::iterator bi = fi->begin(), be = fi->end() ; bi != be; ++bi){
bi->setName(""); // setting the basic blocks' names
}
*/
}
}
//替换 为 以下代数恒等式
for(std::vector<Instruction*>::iterator i =toEdit.begin();i!=toEdit.end();++i)
{
//if x*(x+1) % 2 || y < 10 == 0
opX = new LoadInst ((Value *)x, "", (*i));
opY = new LoadInst ((Value *)y, "", (*i));
//Sub
op = BinaryOperator::Create(Instruction::Add, (Value *)opX, //加
ConstantInt::get(Type::getInt32Ty(M.getContext()), 1,
false), "", (*i));
op1 = BinaryOperator::Create(Instruction::Mul, (Value *)opX, op, "", (*i)); //乘
op = BinaryOperator::Create(Instruction::URem, op1,
ConstantInt::get(Type::getInt32Ty(M.getContext()), 2,
false), "", (*i));
condition = new ICmpInst((*i), ICmpInst::ICMP_EQ, op, //相等
ConstantInt::get(Type::getInt32Ty(M.getContext()), 0,
false));
condition2 = new ICmpInst((*i), ICmpInst::ICMP_SLT, opY, //小于
ConstantInt::get(Type::getInt32Ty(M.getContext()), 10,
false));
op1 = BinaryOperator::Create(Instruction::Or, (Value *)condition,
(Value *)condition2, "", (*i));
//获取后继者 br i1 %3, label %12, label %21
//如果op1 结果为true 执行 getSuccessor(0) 否则 执行getSuccessor(1)
BranchInst::Create(((BranchInst*)*i)->getSuccessor(0),
((BranchInst*)*i)->getSuccessor(1),(Value *) op1,
((BranchInst*)*i)->getParent());
(*i)->eraseFromParent(); // erase the branch
}
// Erase all the associated conditions we found
for(std::vector<Instruction*>::iterator i =toDelete.begin();i!=toDelete.end();++i)
{
(*i)->eraseFromParent();
}
return true;
} // end of doFinalization