美文网首页
O-llvm源码解析

O-llvm源码解析

作者: 纯情_小火鸡 | 来源:发表于2020-01-21 16:50 被阅读0次

    预备知识:

    IR文件结构以及相关语法:

    ir.jpg
    1. 以@开头为全局标识符(函数,全局变量);以%开头为局部变量。
    2. %a = alloca i32, align 4 ,alloca相当于malloc,用于内存分配且自动释放;i32为占有几位,此为4个字节;align字节对齐。
    3. label 严格的讲它也是一种数据类型(type),但它可以标识入口,相当于代码标签。
    4. 函数的声明使用declare,函数的定义使用define。
    5. 数组类型用[count x ix]表示,其中count表示数组的大小,ix表示数组中每一个元素对应的数据类型,比如字符串”Hello IR”表示为[9 x i8],9表示该字符串包含9个元素(末尾包含一个\0),每个元素大小为i8即c语言中的char类型大小。
    6. if语句在IR中是使用比较指令icmp和跳转指令br来实现。



    Mach-O文件结构:

    • 头部(header structure):其结构体包含cup类型,文件类型,加载命令数,命令总字节数等。
    • 加载命令(load command):包括command类型,command大小,对应虚拟内存的起始地址,段在文件中的偏移,大小等。
    • 段(segment):可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。这些地址被分成 __TEXT段、____DATA段 和 __LINKEDIT段。
      • _TEXT 包含 Mach header,被执行的代码和只读常量(如C 字符串),只读可执行(r-x)。
      • _DATA 包含全局变量,静态变量等,可读写(rw-)。
      • _LINKEDIT 包含了加载程序的『元数据』,比如函数的名称和地址,只读(r–)
      • section中包括最重要的_text 主程序代码,在内存中的起始地址,section大小,文件偏移等。
        • _objc_classname 类名。
        • _objc_methname 方法名。
        • _objc_classlist每个类的虚拟地址。
    • 链接信息。一个完整的用户级Mach-o文件的末端是链接信息。其中包含了动态加载器用来链接可执行文件或者依赖库所需使用的符号表,字符串表等。



    Flattening :

    //入口函数
    bool Flattening::runOnFunction(Function &F) {
      Function *tmp = &F;
      //断是否包含了启动fla的命令`-fla`
      if (toObfuscate(flag, tmp, "fla")) {
        if (flatten(tmp)) {
          ++Flattened;
        }
      }
    }
    
    //平坦化核心函数
    bool Flattening::flatten(Function *f) {
        // 保存所有原始basicblock
      vector<BasicBlock *> origBB;
      for (Function::iterator i = f->begin(); i != f->end(); ++i) {
        BasicBlock *tmp = &*i;
        origBB.push_back(tmp);
      }
      //当basicblock的数目小于等于1,直接退出
      if (origBB.size() <= 1) {
        return false;
      }
        //移除容器中第一个BB  
      origBB.erase(origBB.begin());
      Function::iterator tmp = f->begin(); //++tmp;
        //判断该basicblock是否包含有跳转指令;如果有,再进一步判断该指令是否为条件跳转,若是的话则获取该条件跳转指令的地址,并调用splitBasicblock将第一个basicblock拆分
      br = cast<BranchInst>(insert->getTerminator());
      if ((br != NULL && br->isConditional()) ||
          insert->getTerminator()->getNumSuccessors() > 1) {
        BasicBlock::iterator i = insert->end();
        --i;
        if (insert->size() > 1) {
          --i;
        }
        BasicBlock *tmpBB = insert->splitBasicBlock(i, "first");
        origBB.insert(origBB.begin(), tmpBB);
      }
      
        //将第一个basicblock与下一个basicblock的跳转关系删除  
      insert->getTerminator()->eraseFromParent();
      //然后在第一个basicblock的末尾创建一个变量switchVar并赋予它一个随机的值,接着创建三个新的basicblock块,分别为“loopEntry”、“loopEnd”以及“swDefault”,并且设置好它们之间的跳转关系
      switchVar =
          new AllocaInst(Type::getInt32Ty(f->getContext()), 0, "switchVar", insert);
        // Create main loop
      loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert);
      loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert);
        // Move first BB on top
      insert->moveBefore(loopEntry);
      BranchInst::Create(loopEntry, insert);
    
      // loopEnd jump to loopEntry
      BranchInst::Create(loopEntry, loopEnd);
      BasicBlock *swDefault =
          BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd);
      BranchInst::Create(loopEnd, swDefault);
          /**
                 insert
                    |
                    v
                 loopEntry  <---
                    |           |
                    v           |
                 swDefault      |
                    |           |
                    v           |
                 loopEnd   -----
                
        **/
        //...
        //将容器中的每一个bb都添加到switch中  
      for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
           ++b) {
        BasicBlock *i = *b;
        ConstantInt *numCase = NULL;
        // Move the BB inside the switch (only visual, no code logic)
        i->moveBefore(loopEnd);
        // Add case to switch
        numCase = cast<ConstantInt>(ConstantInt::get(
            switchI->getCondition()->getType(),
            llvm::cryptoutils->scramble32(switchI->getNumCases(), scrambling_key)));
        switchI->addCase(numCase, i);
      }
      //重新设置switchVar
        for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
                                ++b) {
            if (i->getTerminator()->getNumSuccessors() == 1) {
                //如果不是条件跳转指令(比如for循环),将该basicblock添加到switch-case中
              if (numCase == NULL) {
            numCase = cast<ConstantInt>(
                ConstantInt::get(switchI->getCondition()->getType(),
                                 llvm::cryptoutils->scramble32(
                                     switchI->getNumCases() - 1, scrambling_key)));
          }
    
          // Update switchVar and jump to the end of loop
          new StoreInst(numCase, load->getPointerOperand(), i);
          BranchInst::Create(loopEnd, i);
          continue;
        }
            } 
            if (i->getTerminator()->getNumSuccessors() == 2) {
              //如果是条件跳转
                        //。。。
            }
      }
    }
    



    BogusControlFlow :

    //入口函数  
    bool runOnFunction(Function &F) override {
        // Check if the percentage is correct
        //bcf(BogusControlFlow)循环运行的次数
        if (ObfTimes <= 0) {
          return false;
        }
        // Check if the number of applications is correct
        //每个basic block被混淆的几率,它们的默认值分别为1和70%。可通过设置参数boguscf-loop、 boguscf-prob修改它们的默认值
        if (!((ObfProbRate > 0) && (ObfProbRate <= 100))) {
                    "-bcf_prob=x must be 0 < x <= 100";
          return false;
        }
              bogus(F);
              doF(*F.getParent());
    
      } // end of runOnFunction()
    
      void bogus(Function &F) {
        do {
          //将本function的所有不是异常指令的basicblock存放到一个list容器中
          std::list<BasicBlock *> basicBlocks;
          for (Function::iterator i = F.begin(); i != F.end(); ++i) {
            BasicBlock *BB = &*i;
            if (!BB->isEHPad() && !BB->isLandingPad()) {
              basicBlocks.push_back(BB);
            }
                 while (!basicBlocks.empty()) {
            NumBasicBlocks++;
            // Basic Blocks' selection
            if ((int)llvm::cryptoutils->get_range(100) <= ObfProbRate) {
              hasBeenModified = true;
              ++NumModifiedBasicBlocks;
              NumAddedBasicBlocks += 3;
              FinalNumBasicBlocks += 3;
              // 循环调用addBogusFlow对选中的basicblock进行增加虚假控制流
              BasicBlock *basicBlock = basicBlocks.front();
              addBogusFlow(basicBlock, F);
            }
            // 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())
          }
      }
        
      virtual void addBogusFlow(BasicBlock *basicBlock, Function &F) {
      //获取本basicblock中第一个不是Phi、debug、terminator的指令的地址
       if (basicBlock->getFirstNonPHIOrDbgOrLifetime())
        i1 = (BasicBlock::iterator)basicBlock->getFirstNonPHIOrDbgOrLifetime();
        //然后调用splitBasicBlock函数。该函数根据上述指令的地址将一个basicblock进行拆分
        BasicBlock *originalBB = basicBlock->splitBasicBlock(i1, *var);
            //对original basicblock进行拷贝生成一个名为“altered basicblock”的basicblock,并对该basicblock加入一些垃圾指令,该方法解析见下一个方法    
        BasicBlock *alteredBB = createAlteredBasicBlock(originalBB, *var3, &F);
            //清除first basicblock和altered basicblock跟父节点的关系,修改终结器以调整控制流程
        alteredBB->getTerminator()->eraseFromParent();
        basicBlock->getTerminator()->eraseFromParent();
        // 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);    
        // 增加一条比较语句 1.0 = = 1.0 ,为真时跳转到original basicblock,为假则跳转到altered basicblock
        FCmpInst *condition =
            new FCmpInst(*basicBlock, FCmpInst::FCMP_TRUE, LHS, RHS, *var4);
        BranchInst::Create(originalBB, alteredBB, (Value *)condition, basicBlock);
        //在altered模块的尾部增加一条跳转指令,当它执行完毕之后跳转到original basicblock模块(实际上它并不会执行)
        BranchInst::Create(originalBB, alteredBB);
        BasicBlock::iterator i = originalBB->end();
        //拿到最后一条指令地址,调用调用splitBasicblock分割,擦出关系之后,也采用以上的判断跳转,为真执行真实指令;为假执行虚假指令
        BasicBlock *originalBBpart2 = originalBB->splitBasicBlock(--i, *var5);
        originalBB->getTerminator()->eraseFromParent();
        //。。。
        BranchInst::Create(originalBBpart2, originalBB, condition2, originalBB);
      }
      
      //虚假指令核心方法
        /*
        此函数返回与给定块相似的基本块。
        它被插入到给定的基本块之后。
        指令相似,但在之间添加了垃圾指令
        克隆的。 克隆指令的phi节点,元数据,用法和
        调试位置已调整为适合克隆的基本块
        */
        virtual BasicBlock *createAlteredBasicBlock(BasicBlock *basicBlock,
                                                  const Twine &Name = "gen",
                                                  Function *F = 0) {
             BasicBlock *alteredBB = llvm::CloneBasicBlock(basicBlock, VMap, Name, F);
        DEBUG_WITH_TYPE("gen", errs() << "bcf: Original basic block cloned\n");
        // 重映射操作数
        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) {
            // 获取操作数的值
            // 该方法返回函数局部值(Argument,Instruction,BasicBlock)的映射值,或计算并存储常数的值
            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 ...
        }
        //在块的中间添加随机指令。 这部分可以改进
           for (BasicBlock::iterator i = alteredBB->begin(), e = alteredBB->end();
             i != e; ++i) {
                     BinaryOperator *op, *op1 = NULL;
            //在找到二进制运算符的情况下,我们通过随机插入一些指令来对此部分进行稍微修改 
            unsigned opcode = i->getOpcode();
                     // Binary int
                    if (opcode == Instruction::Add || opcode == Instruction::Sub || ...) {
              for (int random = (int)llvm::cryptoutils->get_range(10); random < 10;
                   ++random) {
                //根据随机数设置指令
                op = BinaryOperator::CreateNeg(i->getOperand(0), *var, &*i);
                  op1 = BinaryOperator::Create(Instruction::Add, op,
                                               i->getOperand(1), "gen", &*i);
              }
            }
            // Binary float 同上
                    // Condition (with int)
            // Conditions (with float)
           }
          //总的来说就是遍历该basicblock中的所有OpCode,若包含有Add、Sub、UDiv、SDiv、URem、SRem、Shl、LShr、AShr、And、Or、Xor以及FAdd、FSub、FMul、FDiv、FRem指令,则用随机生成一些指令来进行替换。由于该block在程序运行时并不会执行,因此无需担心插入的指令对原始程序运行的结果产生影响
      }
        
      //覆盖FunctionPass方法以将转换应用于整个模块。 这部分混淆了模块的所有始终正确的谓词。更确切地说,谓词的条件是FCMP_TRUE。 它还删除所有功能的“基本块”和指令的名称。
      bool doF(Module &M) {
            //已经不使用将FCMP_TRUE谓词替换为(y <10 || x *(x + 1)% 2 == 0)的方式。而是通过随机从Instruction::BinaryOps数组中获取Instruction,并且从CmpInst::Predicate数组中获取谓词进行分支替换
            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("");
            Instruction *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) {
                  //遍历每一个module中的function,拿到每条instruction,判断谓词是否为FCMP_TRUE,是的话加入到集合中
                  if (cond->getPredicate() == ICmpInst::ICMP_EQ && cond->getName().startswith("BCFPlaceHolderPred")) {
                    toDelete.push_back(cond); // The condition
                    toEdit.push_back(tbb);    // The branch using the condition
                  }
                }
              }
            }
            //对需要操作的指令进行替换
            for (std::vector<Instruction *>::iterator i = toEdit.begin();
             i != toEdit.end(); ++i) {
                //...
            }
          }
        }
      }
    

    这样看来,我们还可以在指令拆分,虚假代码插入的流程,以及随机数生成新的指令操作数的地方,都可以去优化改进,定制出更难破解的BogusControlFlow模块。



    Substitution :

    //入口函数
    bool Substitution::runOnFunction(Function &F) {
      // Check if the percentage is correct
      if (ObfTimes <= 0) {
        errs() << "Substitution application number -sub_loop=x must be x > 0";
        return false;
      }
      if (ObfProbRate > 100) {
        errs() << "InstructionSubstitution application instruction percentage "
                  "-sub_prob=x must be 0 < x <= 100";
        return false;
      }
        //判断是否包含了启动sub的命令
      Function *tmp = &F;
      // Do we obfuscate
      if (toObfuscate(flag, tmp, "sub")) {
        errs() << "Running Instruction Substitution On " << F.getName() << "\n";
        substitute(tmp);
        return true;
      }
    
      return false;
    }
    
    /*
    目前,sub支持五种指令的替换,分别是“Add”、“Sub”、“And”、“Or”以及“Xor”指令。
    “Add”指令支持4种替换方法,分别是a = b - (-c)、a = -(-b + (-c))、r = rand (); a = b + r; a = a + c; a = a – r 、r = rand (); a = b - r; a = a + b; a = a + r 。
    “Sub”指令支持3种替换方法,分别是a = b + (-c)、r = rand (); a = b + r; a = a - c; a = a – r 、r = rand (); a = b - r; a = a - c; a = a + r 。
    “And” 指令支持2种替换方法,分别是a = b & c => a = (b^~c)& b 、a = a & b <=> !(!a | !b) & (r | !r) 。
    “Or” 指令支持2种替换方法,分别是a = b | c => a = (b & c) | (b ^ c) 、a | b => [(!a & r) | (a & !r) ^ (!b & r) |(b & !r) ] | [!(!a | !b) & (r |!r)] 。
    “Xor” 指令支持2种替换方法,分别是a = a ^ b => a = (!a & b) | (a & !b) 、a = a ^ b <=> (a ^ r) ^ (b ^ r) <=> (!a & r | a & !r) ^ (!b & r | b & !r) 。
    在substitute函数的switch-case中,程序会随机的调用这些替换方法
    */
    bool Substitution::substitute(Function *f) {
      Function *tmp = f;
      // Loop for the number of time we run the pass on the function
      int times = ObfTimes;
      do {
        for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
          for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
            if (inst->isBinaryOp() && cryptoutils->get_range(100) <= ObfProbRate) {
              switch (inst->getOpcode()) {
              case BinaryOperator::Add:
                //funcAdd是个函数数组,里面存储了NUMBER_ADD_SUBST个替换add指令的函数,get_range是个获取随机数的函数,通过这种方法,可使替换的add具有一定的随机性。对于其他的指令,也是采用类似add指令的方式进行替换
                (this->*funcAdd[llvm::cryptoutils->get_range(NUMBER_ADD_SUBST)])(
                    cast<BinaryOperator>(inst));
                ++Add;
                break;
                    //...
              }              // End switch
            }                // End isBinaryOp
          }                  // End for basickblock
        }                    // End for Function
      } while (--times > 0); // for times
      return false;
    }
        //funcAdd对应的四个替换方法
      Substitution() : FunctionPass(ID) {
        this->flag = true;
        funcAdd[0] = &Substitution::addNeg;
        funcAdd[1] = &Substitution::addDoubleNeg;
        funcAdd[2] = &Substitution::addRand;
        funcAdd[3] = &Substitution::addRand2;
      }
    
    // Implementation of a = b - (-c)
    void Substitution::addNeg(BinaryOperator *bo) {
      BinaryOperator *op = NULL;
      // Create sub
      if (bo->getOpcode() == Instruction::Add) {
        op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo);
        op =
            BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo);
        bo->replaceAllUsesWith(op);
      }
    }
    



    SplitBasicBlocks:

    
    void SplitBasicBlock::split(Function *f) {
      std::vector<BasicBlock *> origBB;
        //设置每个bb分割次数
        int splitN = SplitNum;
    
      //保存该function下所有bb
      for (Function::iterator I = f->begin(), IE = f->end(); I != IE; ++I) {
        origBB.push_back(&*I);
      }
    
      for (std::vector<BasicBlock *>::iterator I = origBB.begin(),
                                               IE = origBB.end();
           I != IE; ++I) {
        BasicBlock *curr = *I;
    
        //如果分割刀size为1 || 包含PHI跳转控制
        if (curr->size() < 2 || containsPHI(curr)) {
          continue;
        }
    
        // Check splitN and current BB size
        if ((size_t)splitN > curr->size()) {
          splitN = curr->size() - 1;
        }
    
        // 生成分割点集合
        std::vector<int> test;
        for (unsigned i = 1; i < curr->size(); ++i) {
          test.push_back(i);
        }
    
        // 打乱顺序
        if (test.size() != 1) {
          shuffle(test);
          std::sort(test.begin(), test.begin() + splitN);
        }
    
        // 分割
        BasicBlock::iterator it = curr->begin();
        BasicBlock *toSplit = curr;
        int last = 0;
        for (int i = 0; i < splitN; ++i) {
          for (int j = 0; j < test[i] - last; ++j) {
            ++it;
          }
          last = test[i];
          if (toSplit->size() < 2)
            continue;
          toSplit = toSplit->splitBasicBlock(it, toSplit->getName() + ".split");
        }
    
        ++Split;
      }
    }
    

    相关文章

      网友评论

          本文标题:O-llvm源码解析

          本文链接:https://www.haomeiwen.com/subject/ktmezctx.html