ollvm源码分析 - Pass之Substitution

ollvm源码分析 - Pass之Substitution

概述

  • ollvm这个项目的Substitution这个Pass源码比较简单,功能也很明确,那就是进行操作符替换,那要替换哪些操作符呢?我直接抛出来吧,ollvm中替换了 加 减 与 或 异或这几种
  • 那到底怎么替换的呢?直接抛出总结的结果吧
    • 加法拆分

      1. a = b + c => a = b - (-c)
      2. a = b + c => a = -(-b + (-c))
      3. a = b + c => {
        r = rand();
        a = b + r;
        a = a + c;
        a = a - r;
        }
      4. a = b + c => {
        r = rand();
        a = b - r;
        a = a + c;
        a = a + r;
        }
    • 减法拆分

      1. a = b - c => a = b + (-c)
      2. a = b - c => {
        r = rand();
        a = b + r;
        a = a - c;
        a = a - r;
        }
      3. a = b - c => {
        r = rand();
        a = b - r;
        a = a - c;
        a = a + r;
        }
    • 与 拆分

      1. a = b & c => a = (b^~c) & b
      2. a = a && b => !(!a | !b) && (r | !r)
    • 或 拆分

      1. a = b | c => a = (b & c) | (b ^ c)
      2. a = a || b => [(!a && r) || (a && !r) ^ (!b && r) ||(b && !r) ] || [!(!a || !b) && (r ||!r)]
    • 异或拆分

      1. a = a ^ b => (a ^ r) ^ (b ^ r) <=> (!a && r || a && !r) ^ (!b && r || b && !r)
      2. a = a ~ b => a = (!a && b) || (a && !b)
  • 上面这几种就是ollvm项目中目前进行指令替换的地方以及如何进行替换的了

源码分析

  • 还是一个标准的llvm Pass
  •  46 namespace {
     47
     48 struct Substitution : public FunctionPass {
     49   static char ID; // Pass identification, replacement for typeid
          //这里声明了5个指针数组,分别是Add, Sub, And, Or, Xor操作变换的函数指针
     50   void (Substitution::*funcAdd[NUMBER_ADD_SUBST])(BinaryOperator *bo);
     51   void (Substitution::*funcSub[NUMBER_SUB_SUBST])(BinaryOperator *bo);
     52   void (Substitution::*funcAnd[NUMBER_AND_SUBST])(BinaryOperator *bo);
     53   void (Substitution::*funcOr[NUMBER_OR_SUBST])(BinaryOperator *bo);
     54   void (Substitution::*funcXor[NUMBER_XOR_SUBST])(BinaryOperator *bo);
     55   bool flag;
     56
     57   Substitution() : FunctionPass(ID) {}
     58
     59   Substitution(bool flag) : FunctionPass(ID) {
     60     this->flag = flag;
     61     funcAdd[0] = &Substitution::addNeg;
     62     funcAdd[1] = &Substitution::addDoubleNeg;
     63     funcAdd[2] = &Substitution::addRand;
     64     funcAdd[3] = &Substitution::addRand2;
     65
     66     funcSub[0] = &Substitution::subNeg;
     67     funcSub[1] = &Substitution::subRand;
     68     funcSub[2] = &Substitution::subRand2;
     69
     70     funcAnd[0] = &Substitution::andSubstitution;
     71     funcAnd[1] = &Substitution::andSubstitutionRand;
     72
     73     funcOr[0] = &Substitution::orSubstitution;
     74     funcOr[1] = &Substitution::orSubstitutionRand;
     75
     76     funcXor[0] = &Substitution::xorSubstitution;
     77     funcXor[1] = &Substitution::xorSubstitutionRand;
     78   }
     79
     80   bool runOnFunction(Function &F);
     81   bool substitute(Function *f);
     82
     83   void addNeg(BinaryOperator *bo);
     84   void addDoubleNeg(BinaryOperator *bo);
     85   void addRand(BinaryOperator *bo);
     86   void addRand2(BinaryOperator *bo);
     87
     88   void subNeg(BinaryOperator *bo);
     89   void subRand(BinaryOperator *bo);
     90   void subRand2(BinaryOperator *bo);
     91
     92   void andSubstitution(BinaryOperator *bo);
     93   void andSubstitutionRand(BinaryOperator *bo);
     94
     95   void orSubstitution(BinaryOperator *bo);
     96   void orSubstitutionRand(BinaryOperator *bo);
     97
     98   void xorSubstitution(BinaryOperator *bo);
     99   void xorSubstitutionRand(BinaryOperator *bo);
    100 };
    101 }
    
  • 依然是一个标准的Pass, 类的开头定义了5个指针数组,数组在Substitution(bool flag)这个构造函数中进行的初始化,构造函数中可以看到Add操作对应了4个处理函数,Sub操作对应了3个处理函数,And操作对应了2个处理函数,Or操作对应了2个处理函数,Xor操作对应了2个处理函数
  • 先来看主干函数runOnFunction吧,上代码:
    107 bool Substitution::runOnFunction(Function &F) {
    108    // Check if the percentage is correct
    109    if (ObfTimes <= 0) {
    110      errs()<<"Substitution application number -sub_loop=x must be x > 0";
    111          return false;
    112    }
    113
    114   Function *tmp = &F;
    115   // Do we obfuscate
    116   if (toObfuscate(flag, tmp, "sub")) {
    117     substitute(tmp);
    118         return true;
    119   }
    120
    121   return false;
    122 }
    
    1. 代码还是逻辑依然简洁,先是验证了 -mllvm -sub_loop=x这个编译参数的正确性,必须>0, 这个参数的作用后面会看到
    2. 接着是toObfuscate判定当前函数是否需要进行Substitution,一个是判定flag是否为true, 也就是编译命令中是否有 -mllvm -sub命令,一个是判定当前函数是否有 attribute((annotate(("sub"))))的标记
    3. 然后117行就开始进入substitute进行指令拆分了
  • 接着进入Substitution::substitute进行具体的指令拆分,看下经过精简的代码:
  • 124 bool Substitution::substitute(Function *f) {
    125   Function *tmp = f;
    126
    127   // Loop for the number of time we run the pass on the function
    128   int times = ObfTimes;
    129   do {
    130     for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
    131       for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
    132         if (inst->isBinaryOp()) {
    133           switch (inst->getOpcode()) {
    134           case BinaryOperator::Add:
    137             (this->*funcAdd[llvm::cryptoutils->get_range(NUMBER_ADD_SUBST)])(
    138                 cast(inst));
    139             ++Add;
    140             break;
    141           case BinaryOperator::Sub:
    144             (this->*funcSub[llvm::cryptoutils->get_range(NUMBER_SUB_SUBST)])(
    145                 cast(inst));
    146             ++Sub;
    147             break;
                    ... 中间省略了无关代码
    171           case Instruction::And:
    172             (this->*
    173              funcAnd[llvm::cryptoutils->get_range(2)])(cast(inst));
    174             ++And;
    175             break;
    176           case Instruction::Or:
    177             (this->*
    178              funcOr[llvm::cryptoutils->get_range(2)])(cast(inst));
    179             ++Or;
    180             break;
    181           case Instruction::Xor:
    182             (this->*
    183              funcXor[llvm::cryptoutils->get_range(2)])(cast(inst));
    184             ++Xor;
    185             break;
    186           default:
    187             break;
    188           }              // End switch
    189         }                // End isBinaryOp
    190       }                  // End for basickblock
    191     }                    // End for Function
    192   } while (--times > 0); // for times
    193   return false;
    194 }
    
    1. 外层ObfTimes次循环代表对变换操作重复执行ObfTimes轮,每轮的变换操作逻辑相同,就是叠加变换
    2. 直接是2个for循环,外层循环遍历当前函数的所有代码块,内层循环遍历每个代码块的每条指令,然后133行根据指令的Opcode来判定是否需要执行替换操作
    3. 从 134行,141行,171行,176行,181行看,分别对Add, Sub, And, Or, Xor 5种操作进行替换操作,每次操作都是从之前定义的处理函数数组中随机选择一个处理函数,进行操作,其中llvm::cryptoutils->get_range就是一个随机函数,范围随机
  • 因为所有的变换操作代码都非常相似,所以就直接选择一个Add操作处理函数来看下,直接看addNeg(BinaryOperator *bo)这个函数:
  • 196 // Implementation of a = b - (-c)
    197 void Substitution::addNeg(BinaryOperator *bo) {
    198   BinaryOperator *op = NULL;
    199
    200   // Create sub
    201   if (bo->getOpcode() == Instruction::Add) {
    202     op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo);
    203     op =
    204         BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo);
    210     bo->replaceAllUsesWith(op);
    211   }
    216 }
    
    1. 这个函数是实现了a=b+c => a=b-(-c)的转换
    2. 先是把第二个操作数取负数,也就是 c => -c
    3. 然后创建一个减法操作,和原始的第一个参数拼接起来,b - (-c)操作形成
    4. 然后210行把新操作替换进老的 BinaryOperator

总结

  • 遍历代码块中所有指令,按照预定的变换规则,替换指定类型的操作符
  • 通过指令替换,把原本简单的数学或者逻辑操作复杂化,达到降低代码可读性,防破解的目的

你可能感兴趣的:(Android,llvm)