OLLVM虚假控制流源码学习笔记
本文为看雪论坛精华文章
看雪论坛作者ID:34r7hm4n
0x00. runOnFunction函数
virtual bool runOnFunction(Function &F){// Check if the percentage is correctif (ObfTimes <= 0) {errs()<<"BogusControlFlow application number -bcf_loop=x must be x > 0";return false;}// Check if the number of applications is correctif ( !((ObfProbRate > 0) && (ObfProbRate <= 100)) ) {errs()<<"BogusControlFlow application basic blocks percentage -bcf_prob=x must be 0 < x <= 100";return false;}
static cl::opt<int>ObfProbRate("bcf_prob", cl::desc("Choose the probability [%] each basic blocks will be obfuscated by the -bcf pass"), cl::value_desc("probability rate"), cl::init(defaultObfRate), cl::Optional);static cl::opt<int>ObfTimes("bcf_loop", cl::desc("Choose how many time the -bcf pass loop on a function"), cl::value_desc("number of times"), cl::init(defaultObfTime), cl::Optional);
// If fla annotationsif(toObfuscate(flag,&F,"bcf")) {bogus(F);doF(*F.getParent());return true;}return false;} // end of runOnFunction()
0x01. bogus函数
void bogus(Function &F) {// For statistics and debug++NumFunction;int NumBasicBlocks = 0;bool firstTime = true; // First time we do the loop in this functionbool hasBeenModified = false;DEBUG_WITH_TYPE("opt", errs() << "bcf: Started on function " << F.getName() << "\n");DEBUG_WITH_TYPE("opt", errs() << "bcf: Probability rate: "<< ObfProbRate<< "\n");if(ObfProbRate < 0 || ObfProbRate > 100){DEBUG_WITH_TYPE("opt", errs() << "bcf: Incorrect value,"<< " probability rate set to default value: "<< defaultObfRate <<" \n");ObfProbRate = defaultObfRate;}DEBUG_WITH_TYPE("opt", errs() << "bcf: How many times: "<< ObfTimes<< "\n");if(ObfTimes <= 0){DEBUG_WITH_TYPE("opt", errs() << "bcf: Incorrect value,"<< " must be greater than 1. Set to default: "<< defaultObfTime <<" \n");ObfTimes = defaultObfTime;}NumTimesOnFunctions = ObfTimes;
NumTimesOnFunctions = ObfTimes;STATISTIC(NumTimesOnFunctions, "b. Number of times we run on each function");do{// Put all the function's block in a liststd::list<BasicBlock *> basicBlocks;for (Function::iterator i=F.begin();i!=F.end();++i) {basicBlocks.push_back(&*i);}
while(!basicBlocks.empty()){NumBasicBlocks ++;// Basic Blocks' selectionif((int)llvm::cryptoutils->get_range(100) <= ObfProbRate){hasBeenModified = true;BasicBlock *basicBlock = basicBlocks.front();addBogusFlow(basicBlock, F);}// remove the block from the listbasicBlocks.pop_front();} // end of while(!basicBlocks.empty())
0x02. addBogusFlow函数第一部分
/* addBogusFlow** Add bogus flow to a given basic block, according to the header's description*/virtual void addBogusFlow(BasicBlock * basicBlock, Function &F){// Split the block: first part with only the phi nodes and debug info and terminator// created by splitBasicBlock. (-> No instruction)// Second part with every instructions from the original block// We do this way, so we don't have to adjust all the phi nodes, metadatas and so on// for the first block. We have to let the phi nodes in the first part, because they// actually are updated in the second part according to them.BasicBlock::iterator i1 = basicBlock->begin();if(basicBlock->getFirstNonPHIOrDbgOrLifetime())i1 = (BasicBlock::iterator)basicBlock->getFirstNonPHIOrDbgOrLifetime();Twine *var;var = new Twine("originalBB");BasicBlock *originalBB = basicBlock->splitBasicBlock(i1, *var);
// Creating the altered basic block on which the first basicBlock will jumpTwine * var3 = new Twine("alteredBB");BasicBlock *alteredBB = createAlteredBasicBlock(originalBB, *var3, &F);
0x03. createAlteredBasicBlock函数
virtual BasicBlock* createAlteredBasicBlock(BasicBlock * basicBlock,const Twine & Name = "gen", Function * F = 0){// Useful to remap the informations concerning instructions.ValueToValueMapTy VMap;BasicBlock * alteredBB = llvm::CloneBasicBlock (basicBlock, VMap, Name, F);
orig:%a = ...%b = fadd %a, ...clone:%a.clone = ...%b.clone = fadd %a, ... ; Note that this references the old %a andnot %a.clone!
所以之后要通过VMap对所有操作数进行映射,使其恢复正常:
// Remap operands.BasicBlock::iterator ji = basicBlock->begin();for (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end() ; i != e; ++i){// Loop over the operands of the instructionfor(User::op_iterator opi = i->op_begin (), ope = i->op_end(); opi != ope; ++opi){// get the value for the operandValue *v = MapValue(*opi, VMap, RF_None, 0);if (v != 0){*opi = v;DEBUG_WITH_TYPE("gen", errs() << "bcf: Value's operand has been setted\n");}}
// Remap phi nodes' incoming blocks.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));}}}
// Remap attached metadata.SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;i->getAllMetadata(MDs);// important for compiling with DWARF, using option -g.i->setDebugLoc(ji->getDebugLoc());ji++;} // The instructions' informations are now all correct
至此我们就完成了对一个基本块的克隆,之后是往基本块里添加垃圾指令,这里大概的思路就是往基本块里插入一些没用的赋值指令,或者修改cmp指令的条件,BinaryOp大概指的是add、mul、cmp这类运算指令:
// add random instruction in the middle of the bloc. This part can be improvefor (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end() ; i != e; ++i){// in the case we find binary operator, we modify slightly this part by randomly// insert some instructionsif(i->isBinaryOp()){ // binary instructionsunsigned opcode = i->getOpcode();BinaryOperator *op, *op1 = NULL;Twine *var = new Twine("_");// treat differently float or int// Binary intif(opcode == Instruction::Add || opcode == Instruction::Sub ||opcode == Instruction::Mul || opcode == Instruction::UDiv ||opcode == Instruction::SDiv || opcode == Instruction::URem ||opcode == Instruction::SRem || opcode == Instruction::Shl ||opcode == Instruction::LShr || opcode == Instruction::AShr ||opcode == Instruction::And || opcode == Instruction::Or ||opcode == Instruction::Xor){for(int random = (int)llvm::cryptoutils->get_range(10); random < 10; ++random){switch(llvm::cryptoutils->get_range(4)){ // to improvecase 0: //do nothingbreak;case 1: op = BinaryOperator::CreateNeg(i->getOperand(0),*var,&*i);op1 = BinaryOperator::Create(Instruction::Add,op,i->getOperand(1),"gen",&*i);break;case 2: op1 = BinaryOperator::Create(Instruction::Sub,i->getOperand(0),i->getOperand(1),*var,&*i);op = BinaryOperator::Create(Instruction::Mul,op1,i->getOperand(1),"gen",&*i);break;case 3: op = BinaryOperator::Create(Instruction::Shl,i->getOperand(0),i->getOperand(1),*var,&*i);break;}}}// Binary floatif(opcode == Instruction::FAdd || opcode == Instruction::FSub ||opcode == Instruction::FMul || opcode == Instruction::FDiv ||opcode == Instruction::FRem){for(int random = (int)llvm::cryptoutils->get_range(10); random < 10; ++random){switch(llvm::cryptoutils->get_range(3)){ // can be improvedcase 0: //do nothingbreak;case 1: op = BinaryOperator::CreateFNeg(i->getOperand(0),*var,&*i);op1 = BinaryOperator::Create(Instruction::FAdd,op,i->getOperand(1),"gen",&*i);break;case 2: op = BinaryOperator::Create(Instruction::FSub,i->getOperand(0),i->getOperand(1),*var,&*i);op1 = BinaryOperator::Create(Instruction::FMul,op,i->getOperand(1),"gen",&*i);break;}}}if(opcode == Instruction::ICmp){ // Condition (with int)ICmpInst *currentI = (ICmpInst*)(&i);switch(llvm::cryptoutils->get_range(3)){ // must be improvedcase 0: //do nothingbreak;case 1: currentI->swapOperands();break;case 2: // randomly change the predicateswitch(llvm::cryptoutils->get_range(10)){case 0: currentI->setPredicate(ICmpInst::ICMP_EQ);break; // equalcase 1: currentI->setPredicate(ICmpInst::ICMP_NE);break; // not equalcase 2: currentI->setPredicate(ICmpInst::ICMP_UGT);break; // unsigned greater thancase 3: currentI->setPredicate(ICmpInst::ICMP_UGE);break; // unsigned greater or equalcase 4: currentI->setPredicate(ICmpInst::ICMP_ULT);break; // unsigned less thancase 5: currentI->setPredicate(ICmpInst::ICMP_ULE);break; // unsigned less or equalcase 6: currentI->setPredicate(ICmpInst::ICMP_SGT);break; // signed greater thancase 7: currentI->setPredicate(ICmpInst::ICMP_SGE);break; // signed greater or equalcase 8: currentI->setPredicate(ICmpInst::ICMP_SLT);break; // signed less thancase 9: currentI->setPredicate(ICmpInst::ICMP_SLE);break; // signed less or equal}break;}}if(opcode == Instruction::FCmp){ // Conditions (with float)FCmpInst *currentI = (FCmpInst*)(&i);switch(llvm::cryptoutils->get_range(3)){ // must be improvedcase 0: //do nothingbreak;case 1: currentI->swapOperands();break;case 2: // randomly change the predicateswitch(llvm::cryptoutils->get_range(10)){case 0: currentI->setPredicate(FCmpInst::FCMP_OEQ);break; // ordered and equalcase 1: currentI->setPredicate(FCmpInst::FCMP_ONE);break; // ordered and operands are unequalcase 2: currentI->setPredicate(FCmpInst::FCMP_UGT);break; // unordered or greater thancase 3: currentI->setPredicate(FCmpInst::FCMP_UGE);break; // unordered, or greater than, or equalcase 4: currentI->setPredicate(FCmpInst::FCMP_ULT);break; // unordered or less thancase 5: currentI->setPredicate(FCmpInst::FCMP_ULE);break; // unordered, or less than, or equalcase 6: currentI->setPredicate(FCmpInst::FCMP_OGT);break; // ordered and greater thancase 7: currentI->setPredicate(FCmpInst::FCMP_OGE);break; // ordered and greater than or equalcase 8: currentI->setPredicate(FCmpInst::FCMP_OLT);break; // ordered and less thancase 9: currentI->setPredicate(FCmpInst::FCMP_OLE);break; // ordered or less than, or equal}break;}}}}return alteredBB;} // end of createAlteredBasicBlock()
0x04. addBogusFlow函数第二部分
现在我们有了一个原始基本块originalBB,和一个克隆出的基本块alteredBB,以及一个只包含PHI Node和Debug信息的基本块basicBlock(就是一开始分离出来的那个),现在的状况是这样:
// Now that all the blocks are created,// we modify the terminators to adjust the control flow.alteredBB->getTerminator()->eraseFromParent();basicBlock->getTerminator()->eraseFromParent();// Preparing a condition..// For now, the condition is an always true comparaison between 2 float// This will be complicated after the pass (in doFinalization())Value * LHS = ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0);Value * RHS = ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0);// The always true condition. End of the first blockTwine * var4 = new Twine("condition");FCmpInst * condition = new FCmpInst(*basicBlock, FCmpInst::FCMP_TRUE , LHS, RHS, *var4);// Jump to the original basic block if the condition is true or// to the altered block if false.BranchInst::Create(originalBB, alteredBB, (Value *)condition, basicBlock);// The altered block loop back on the original one.BranchInst::Create(originalBB, alteredBB);
// The end of the originalBB is modified to give the impression that sometimes// it continues in the loop, and sometimes it return the desired value// (of course it's always true, so it always use the original terminator..// but this will be obfuscated too;) )// iterate on instruction just before the terminator of the originalBBBasicBlock::iterator i = originalBB->end();// Split at this point (we only want the terminator in the second part)Twine * var5 = new Twine("originalBBpart2");BasicBlock * originalBBpart2 = originalBB->splitBasicBlock(--i , *var5);// the first part go either on the return statement or on the begining// of the altered block.. So we erase the terminator created when splitting.originalBB->getTerminator()->eraseFromParent();// We add at the end a new always true conditionTwine * var6 = new Twine("condition2");FCmpInst * condition2 = new FCmpInst(*originalBB, CmpInst::FCMP_TRUE , LHS, RHS, *var6);BranchInst::Create(originalBBpart2, alteredBB, (Value *)condition2, originalBB);} // end of addBogusFlow()
0x05. doF函数
首先回顾一下runOnFunction函数最后的代码,bogus函数的代码我已经了解得差不多了,接下来是doF函数:
// If fla annotationsif(toObfuscate(flag,&F,"bcf")) {bogus(F);doF(*F.getParent());return true;}return false;} // end of runOnFunction()
/* doFinalization** Overwrite FunctionPass method to apply the transformations to the whole module.* This part obfuscate all the always true predicates of the module.* More precisely, the condition which predicate is FCMP_TRUE.* It also remove all the functions' basic blocks' and instructions' names.*/bool doF(Module &M){// In this part we extract all always-true predicate and replace them with opaque predicate:// For this, we declare two global values: x and y, and replace the FCMP_TRUE predicate with// (y < 10 || x * (x + 1) % 2 == 0)// A better way to obfuscate the predicates would be welcome.// In the meantime we will erase the name of the basic blocks, the instructions// and the functions.
// The global valuesTwine * 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;// Looking for the conditions and branches to transformfor(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){toDelete.push_back(cond); // The conditiontoEdit.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}*/}}
添加新的条件跳转,删除旧的条件跳转,注释里的条件给错了,从代码来看应该是if y < 10 || x*(x-1) % 2 == 0,不过也差不多:
// Replacing all the branches we foundfor(std::vector<Instruction*>::iterator i =toEdit.begin();i!=toEdit.end();++i){//if y < 10 || x*(x+1) % 2 == 0opX = new LoadInst ((Value *)x, "", (*i));opY = new LoadInst ((Value *)y, "", (*i));op = BinaryOperator::Create(Instruction::Sub, (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));BranchInst::Create(((BranchInst*)*i)->getSuccessor(0),((BranchInst*)*i)->getSuccessor(1),(Value *) op1,((BranchInst*)*i)->getParent());(*i)->eraseFromParent(); // erase the branch}
另外在IDA里这个条件有时候会变成if y >= 10 && x*(x-1) % 2 != 0:
// Erase all the associated conditions we foundfor(std::vector<Instruction*>::iterator i =toDelete.begin();i!=toDelete.end();++i){DEBUG_WITH_TYPE("gen", errs() << "bcf: Erase condition instruction:"<< *((Instruction*)*i)<< "\n");(*i)->eraseFromParent();}
0x06. 总结
看雪ID:34r7hm4n
https://bbs.pediy.com/user-home-910514.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!