//===- BogusControlFlow.cpp - BogusControlFlow Obfuscation pass-------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------------------===// // // This file implements BogusControlFlow's pass, inserting bogus control flow. // It adds bogus flow to a given basic block this way: // // Before : // entry // | // ______v______ // | Original | // |_____________| // | // v // return // // After : // entry // | // ____v_____ // |condition*| (false) // |__________|----+ // (true)| | // | | // ______v______ | // +-->| Original* | | // | |_____________| (true) // | (false)| !-----------> return // | ______v______ | // | | Altered |<--! // | |_____________| // |__________| // // * The results of these terminator's branch's conditions are always true, but these predicates are // opacificated. For this, we declare two global values: x and y, and replace the FCMP_TRUE // predicate with (y < 10 || x * (x + 1) % 2 == 0) (this could be improved, as the global // values give a hint on where are the opaque predicates) // // The altered bloc is a copy of the original's one with junk instructions added accordingly to the // type of instructions we found in the bloc // // Each basic block of the function is choosen if a random number in the range [0,100] is smaller // than the choosen probability rate. The default value is 30. This value can be modify using // the option -boguscf-prob=[value]. Value must be an integer in the range [0, 100], otherwise // the default value is taken. Exemple: -boguscf -boguscf-prob=60 // // The pass can also be loop many times on a function, including on the basic blocks added in // a previous loop. Be careful if you use a big probability number and choose to run the loop // many times wich may cause the pass to run for a very long time. The default value is one loop, // but you can change it with -boguscf-loop=[value]. Value must be an integer greater than 1, // otherwise the default value is taken. Exemple: -boguscf -boguscf-loop=2 // // // Defined debug types: // - "gen" : general informations // - "opt" : concerning the given options (parameter) // - "cfg" : printing the various function's cfg before transformation // and after transformation if it has been modified, and all // the functions at end of the pass, after doFinalization. // // To use them all, simply use the -debug option. // To use only one of them, follow the pass' command by -debug-only=name. // Exemple, -boguscf -debug-only=cfg // // // Stats: // The following statistics will be printed if you use // the -stats command: // // a. Number of functions in this module // b. Number of times we run on each function // c. Initial number of basic blocks in this module // d. Number of modified basic blocks // e. Number of added basic blocks in this module // f. Final number of basic blocks in this module // // file : lib/Transforms/Obfuscation/BogusControlFlow.cpp // date : june 2012 // version: 1.0 // author : julie.michielin@gmail.com // modifications: pjunod // project: Obfuscator // library: LLVMobfuscator.so // option : -boguscf // //===----------------------------------------------------------------------------------===// #include "llvm/Transforms/Obfuscation/BogusControlFlow.h" #define DEBUG_TYPE "boguscf" using namespace llvm; // Options for the pass const int defaultObfRate = 30, defaultObfTime = 1; static cl::opt ObfProbRate("boguscf-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 ObfTimes("boguscf-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); static cl::opt FunctionName("funcBCF",cl::init(""),cl::desc("Bogus Control Flow only certain functions: -mllvm -func=\"func1,func2\"")); static cl::opt Percentage("perBCF",cl::init(100),cl::desc("Bogus Control Flow only a certain percentage of functions")); namespace { struct BogusControlFlow : public FunctionPass { static char ID; // Pass identification BogusControlFlow() : FunctionPass(ID) {} /* runOnFunction * * Overwrite FunctionPass method to apply the transformation * to the function. See header for more details. */ virtual bool runOnFunction(Function &F){ // Check if declaration or variadic if(F.isDeclaration()) { return false; } // Check if the percentage is correct if (ObfTimes <= 0) { LLVMContext &ctx = llvm::getGlobalContext(); ctx.emitError(Twine ("BogusControlFlow application number -boguscf-loop=x must be x > 0")); } // Check if the number of applications is correct if ( !((Percentage > 0) && (Percentage <= 100)) ) { LLVMContext &ctx = llvm::getGlobalContext(); ctx.emitError(Twine ("BogusControlFlow application function percentage -perBCF=x must be 0 < x <= 100")); } // Check if the number of applications is correct if ( !((ObfProbRate > 0) && (ObfProbRate <= 100)) ) { LLVMContext &ctx = llvm::getGlobalContext(); ctx.emitError(Twine ("BogusControlFlow application basic blocks percentage -boguscf-prob=x must be 0 < x <= 100")); } // For statistics and debug ++NumFunction; int NumBasicBlocks = 0; bool firstTime = true; // First time we do the loop in this function bool 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; int NumObfTimes = ObfTimes; if((FunctionName.size() != 0 && FunctionName.find(F.getName()) != string::npos) || ((int)llvm::cprng->get_range(100)) < Percentage) { // Real begining of the pass // Loop for the number of time we run the pass on the function do{ DEBUG_WITH_TYPE("cfg", errs() << "bcf: Function " << F.getName() <<", before the pass:\n"); DEBUG_WITH_TYPE("cfg", F.viewCFG()); // Put all the function's block in a list std::list basicBlocks; for (Function::iterator i=F.begin();i!=F.end();++i) { basicBlocks.push_back((BasicBlock *)i); } DEBUG_WITH_TYPE("gen", errs() << "bcf: Iterating on the Function's Basic Blocks\n"); while(!basicBlocks.empty()){ NumBasicBlocks ++; // Basic Blocks' selection if((int)llvm::cprng->get_range(100) <= ObfProbRate){ DEBUG_WITH_TYPE("opt", errs() << "bcf: Block " << NumBasicBlocks <<" selected. \n"); hasBeenModified = true; ++NumModifiedBasicBlocks; NumAddedBasicBlocks += 3; FinalNumBasicBlocks += 3; // Add bogus flow to the given Basic Block (see description) BasicBlock *basicBlock = basicBlocks.front(); addBogusFlow(basicBlock, F); } else{ DEBUG_WITH_TYPE("opt", errs() << "bcf: Block " << NumBasicBlocks <<" not selected.\n"); } // remove the block from the list basicBlocks.pop_front(); if(firstTime){ // first time we iterate on this function ++InitNumBasicBlocks; ++FinalNumBasicBlocks; } } // end of while(!basicBlocks.empty()) DEBUG_WITH_TYPE("gen", errs() << "bcf: End of function " << F.getName() << "\n"); if(hasBeenModified){ // if the function has been modified DEBUG_WITH_TYPE("cfg", errs() << "bcf: Function " << F.getName() <<", after the pass: \n"); DEBUG_WITH_TYPE("cfg", F.viewCFG()); } else{ DEBUG_WITH_TYPE("cfg", errs() << "bcf: Function's not been modified \n"); } firstTime = false; }while(--NumObfTimes > 0); } return true; } // end of runOnFunction() /* 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->getFirstNonPHIOrDbgOrLifetime(); Twine *var; var = new Twine("originalBB"); BasicBlock *originalBB = basicBlock->splitBasicBlock(i1, *var); DEBUG_WITH_TYPE("gen", errs() << "bcf: First and original basic blocks: ok\n"); // Creating the altered basic block on which the first basicBlock will jump Twine * var3 = new Twine("alteredBB"); BasicBlock *alteredBB = createAlteredBasicBlock(originalBB, *var3, &F); DEBUG_WITH_TYPE("gen", errs() << "bcf: Altered basic block: ok\n"); // Now that all the blocks are created, // we modify the terminators to adjust the control flow. alteredBB->getTerminator()->eraseFromParent(); basicBlock->getTerminator()->eraseFromParent(); DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator removed from the altered" <<" and first basic blocks\n"); // 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); DEBUG_WITH_TYPE("gen", errs() << "bcf: Value LHS and RHS created\n"); // The always true condition. End of the first block Twine * var4 = new Twine("condition"); FCmpInst * condition = new FCmpInst(*basicBlock, FCmpInst::FCMP_TRUE , LHS, RHS, *var4); DEBUG_WITH_TYPE("gen", errs() << "bcf: Always true condition created\n"); // 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); DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator instruction in first basic block: ok\n"); // The altered block loop back on the original one. BranchInst::Create(originalBB, alteredBB); DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator instruction in altered block: ok\n"); // 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 originalBB BasicBlock::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); DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator part of the original basic block" << " is isolated\n"); // 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 condition Twine * var6 = new Twine("condition2"); FCmpInst * condition2 = new FCmpInst(*originalBB, CmpInst::FCMP_TRUE , LHS, RHS, *var6); BranchInst::Create(originalBBpart2, alteredBB, (Value *)condition2, originalBB); DEBUG_WITH_TYPE("gen", errs() << "bcf: Terminator original basic block: ok\n"); DEBUG_WITH_TYPE("gen", errs() << "bcf: End of addBogusFlow().\n"); } // end of addBogusFlow() /* createAlteredBasicBlock * * This function return a basic block similar to a given one. * It's inserted just after the given basic block. * The instructions are similar but junk instructions are added between * the cloned one. The cloned instructions' phi nodes, metadatas, uses and * debug locations are adjusted to fit in the cloned basic block and * behave nicely. */ 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); DEBUG_WITH_TYPE("gen", errs() << "bcf: Original basic block cloned\n"); // 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 instruction for(User::op_iterator opi = i->op_begin (), ope = i->op_end(); opi != ope; ++opi){ // get the value for the operand Value *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"); } } DEBUG_WITH_TYPE("gen", errs() << "bcf: Operands remapped\n"); // Remap phi nodes' incoming blocks. if (PHINode *pn = dyn_cast(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(v)); } } } DEBUG_WITH_TYPE("gen", errs() << "bcf: PHINodes remapped\n"); // Remap attached metadata. SmallVector, 4> MDs; i->getAllMetadata(MDs); for (SmallVectorImpl >::iterator MI = MDs.begin(), ME = MDs.end(); MI != ME; ++MI) { MDNode *Old = MI->second; MDNode *New = MapValue(Old, VMap, RF_None, 0); if (New != Old) i->setMetadata(MI->first, New); } DEBUG_WITH_TYPE("gen", errs() << "bcf: Metadatas remapped\n"); // important for compiling with DWARF, using option -g. i->setDebugLoc(ji->getDebugLoc()); ji++; DEBUG_WITH_TYPE("gen", errs() << "bcf: Debug information location setted\n"); } // The instructions' informations are now all correct DEBUG_WITH_TYPE("gen", errs() << "bcf: The cloned basic block is now correct\n"); DEBUG_WITH_TYPE("gen", errs() << "bcf: Starting to add junk code in the cloned bloc...\n"); // add random instruction in the middle of the bloc. This part can be improve for (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 instructions if(i->isBinaryOp()){ // binary instructions unsigned opcode = i->getOpcode(); BinaryOperator *op, *op1 = NULL; Twine *var = new Twine("_"); // treat differently float or int // Binary int if(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::cprng->get_range(10); random < 10; ++random){ switch(llvm::cprng->get_range(4)){ // to improve case 0: //do nothing break; 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 float if(opcode == Instruction::FAdd || opcode == Instruction::FSub || opcode == Instruction::FMul || opcode == Instruction::FDiv || opcode == Instruction::FRem){ for(int random = (int)llvm::cprng->get_range(10); random < 10; ++random){ switch(llvm::cprng->get_range(3)){ // can be improved case 0: //do nothing break; 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::cprng->get_range(3)){ // must be improved case 0: //do nothing break; case 1: currentI->swapOperands(); break; case 2: // randomly change the predicate switch(llvm::cprng->get_range(10)){ case 0: currentI->setPredicate(ICmpInst::ICMP_EQ); break; // equal case 1: currentI->setPredicate(ICmpInst::ICMP_NE); break; // not equal case 2: currentI->setPredicate(ICmpInst::ICMP_UGT); break; // unsigned greater than case 3: currentI->setPredicate(ICmpInst::ICMP_UGE); break; // unsigned greater or equal case 4: currentI->setPredicate(ICmpInst::ICMP_ULT); break; // unsigned less than case 5: currentI->setPredicate(ICmpInst::ICMP_ULE); break; // unsigned less or equal case 6: currentI->setPredicate(ICmpInst::ICMP_SGT); break; // signed greater than case 7: currentI->setPredicate(ICmpInst::ICMP_SGE); break; // signed greater or equal case 8: currentI->setPredicate(ICmpInst::ICMP_SLT); break; // signed less than case 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::cprng->get_range(3)){ // must be improved case 0: //do nothing break; case 1: currentI->swapOperands(); break; case 2: // randomly change the predicate switch(llvm::cprng->get_range(10)){ case 0: currentI->setPredicate(FCmpInst::FCMP_OEQ); break; // ordered and equal case 1: currentI->setPredicate(FCmpInst::FCMP_ONE); break; // ordered and operands are unequal case 2: currentI->setPredicate(FCmpInst::FCMP_UGT); break; // unordered or greater than case 3: currentI->setPredicate(FCmpInst::FCMP_UGE); break; // unordered, or greater than, or equal case 4: currentI->setPredicate(FCmpInst::FCMP_ULT); break; // unordered or less than case 5: currentI->setPredicate(FCmpInst::FCMP_ULE); break; // unordered, or less than, or equal case 6: currentI->setPredicate(FCmpInst::FCMP_OGT); break; // ordered and greater than case 7: currentI->setPredicate(FCmpInst::FCMP_OGE); break; // ordered and greater than or equal case 8: currentI->setPredicate(FCmpInst::FCMP_OLT); break; // ordered and less than case 9: currentI->setPredicate(FCmpInst::FCMP_OLE); break; // ordered or less than, or equal } break; } } } } return alteredBB; } // end of createAlteredBasicBlock() /* 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. */ virtual bool doFinalization(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. DEBUG_WITH_TYPE("gen", errs()<<"bcf: Starting doFinalization...\n"); // The global values 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 toEdit, toDelete; BinaryOperator *op,*op1 = NULL; LoadInst * opX , * opY; ICmpInst * condition, * condition2; // Looking for the conditions and branches to transform 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 } } } // Replacing all the branches we found for(std::vector::iterator i =toEdit.begin();i!=toEdit.end();++i){ //if y < 10 || x*(x+1) % 2 == 0 opX = 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()); DEBUG_WITH_TYPE("gen", errs() << "bcf: Erase branch instruction:" << *((BranchInst*)*i) << "\n"); (*i)->eraseFromParent(); // erase the branch } // Erase all the associated conditions we found for(std::vector::iterator i =toDelete.begin();i!=toDelete.end();++i){ DEBUG_WITH_TYPE("gen", errs() << "bcf: Erase condition instruction:" << *((Instruction*)*i)<< "\n"); (*i)->eraseFromParent(); } // Only for debug DEBUG_WITH_TYPE("cfg", errs() << "bcf: End of the pass, here are the graphs after doFinalization\n"); for(Module::iterator mi = M.begin(), me = M.end(); mi != me; ++mi){ DEBUG_WITH_TYPE("cfg", errs() << "bcf: Function " << mi->getName() <<"\n"); DEBUG_WITH_TYPE("cfg", mi->viewCFG()); } return true; } // end of doFinalization }; // end of struct BogusControlFlow : public FunctionPass } char BogusControlFlow::ID = 0; static RegisterPass X("boguscf", "inserting bogus control flow"); Pass *llvm::createBogus() { return new BogusControlFlow(); }