# ollvm源码分析 - Pass之Substitution

25 篇文章 8 订阅
3 篇文章 2 订阅

#### 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操作变换的函数指针
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;
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()) {
138                 cast<BinaryOperator>(inst));
140             break;
141           case BinaryOperator::Sub:
144             (this->*funcSub[llvm::cryptoutils->get_range(NUMBER_SUB_SUBST)])(
145                 cast<BinaryOperator>(inst));
146             ++Sub;
147             break;
... 中间省略了无关代码
171           case Instruction::And:
172             (this->*
173              funcAnd[llvm::cryptoutils->get_range(2)])(cast<BinaryOperator>(inst));
174             ++And;
175             break;
176           case Instruction::Or:
177             (this->*
178              funcOr[llvm::cryptoutils->get_range(2)])(cast<BinaryOperator>(inst));
179             ++Or;
180             break;
181           case Instruction::Xor:
182             (this->*
183              funcXor[llvm::cryptoutils->get_range(2)])(cast<BinaryOperator>(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就是一个随机函数，范围随机
• 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

• 遍历代码块中所有指令，按照预定的变换规则，替换指定类型的操作符
• 通过指令替换，把原本简单的数学或者逻辑操作复杂化，达到降低代码可读性，防破解的目的
• 0
点赞
• 0
收藏
觉得还不错? 一键收藏
• 0
评论
08-15 2139
10-29 1万+
07-11 962
02-27 2419
10-20 1万+
10-21 1万+
10-24 9958
10-19 8745
10-19 7679

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。