本文为看雪论坛精华文章
看雪论坛作者ID:34r7hm4n
0x00. runOnFunction函数
virtual bool runOnFunction(Function &F){
// Check if the percentage is correct
if (ObfTimes <= 0) {
errs()<<"BogusControlFlow application number -bcf_loop=x must be x > 0";
return false;
}
// Check if the number of applications is correct
if ( !((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 annotations
if(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 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;
NumTimesOnFunctions = ObfTimes;
STATISTIC(NumTimesOnFunctions, "b. Number of times we run on each function");
do{
// Put all the function's block in a list
std::list<BasicBlock *> basicBlocks;
for (Function::iterator i=F.begin();i!=F.end();++i) {
basicBlocks.push_back(&*i);
}
while(!basicBlocks.empty()){
NumBasicBlocks ++;
// Basic Blocks' selection
if((int)llvm::cryptoutils->get_range(100) <= ObfProbRate){
hasBeenModified = true;
BasicBlock *basicBlock = basicBlocks.front();
addBogusFlow(basicBlock, F);
}
// remove the block from the list
basicBlocks.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 jump
Twine * 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 and
not %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 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");
}
}
// 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 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::cryptoutils->get_range(10); random < 10; ++random){
switch(llvm::cryptoutils->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::cryptoutils->get_range(10); random < 10; ++random){
switch(llvm::cryptoutils->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::cryptoutils->get_range(3)){ // must be improved
case 0: //do nothing
break;
case 1: currentI->swapOperands();
break;
case 2: // randomly change the predicate
switch(llvm::cryptoutils->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::cryptoutils->get_range(3)){ // must be improved
case 0: //do nothing
break;
case 1: currentI->swapOperands();
break;
case 2: // randomly change the predicate
switch(llvm::cryptoutils->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()
0x04. addBogusFlow函数第二部分
// 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 block
Twine * 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 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);
// 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);
} // end of addBogusFlow()
0x05. doF函数
// If fla annotations
if(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 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<Instruction*> 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){
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
}
*/
}
}
添加新的条件跳转,删除旧的条件跳转,注释里的条件给错了,从代码来看应该是if y < 10 || x*(x-1) % 2 == 0,不过也差不多:
// Replacing all the branches we found
for(std::vector<Instruction*>::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());
(*i)->eraseFromParent(); // erase the branch
}
另外在IDA里这个条件有时候会变成if y >= 10 && x*(x-1) % 2 != 0:
// Erase all the associated conditions we found
for(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
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!