OLLVM笔记_3—Substitution学习

指令替换(Instruction Substitution)是OLLVM中相对简单但很实用的混淆技术。LLVM的思路是将简单的二元运算指令替换为等价但更复杂的指令序列,通过增加指令数量和复杂度来干扰静态分析,同时保持程序的功能完全不变。

原理

指令替换的本质是利用数学等价性,将一个简单的运算转换为多个复杂运算的组合(类似VMP 万用门)。

比如将a + b替换为a - (-b),或者将a & b替换为(a^~b) & a。这些替换在数学上完全等价,但会让代码看起来更复杂。

当前学习的OLLVM源码中支持对以下指令进行替换:AddSubAndOrXor

源码分析

源码路径:lib/Transforms/Obfuscation/Substitution.cpp

同样,指令平替也是针对函数,并且在构造函数中能看到函数指针数组。

这一些实际上就是作为指令平替的函数,有点类似于VMP的handle,每个指令对应一个handle,llvm将对应的操作指令替换为自己函数执行。

核心逻辑在bool Substitution::substitute(Function *f)函数中。

可以看到逻辑非常简单,就是通过for循环遍历函数中所有指令。如果是符合条件的指令,就随机从对应的混淆函数指针数组中选取一个对指令进行混淆。

替换的逻辑实际上没什么好讲解的,因为只是重新通过万用门的方式进行实现后替换,这里展示一下ADD替换后的效果:

总结

SUB的源码确实比BCF简单多了,读完后发现局限性很明显。OLLVM只对加减与或异或这5种操作做混淆,乘除、移位、浮点运算都没覆盖。而且现有的混淆方案也比较单一,每种运算就那么几个替换函数,复杂度有限。

对于魔改的话,自己有一些思路:

  1. 扩展运算类型覆盖:添加对乘法、除法、移位运算的混淆支持,让覆盖面更全
  2. 增加替换方案复杂度:现有的替换太简单,可以设计更复杂的等价变换,比如用多层嵌套、循环展开等
  3. 引入类型转换混淆:把整数运算转成浮点运算再转回来,增加类型分析的难度
  4. 组合式混淆:不只是单个指令替换,可以把多个相关指令一起替换成更复杂的指令块
  5. 动态复杂度:根据函数重要性动态调整混淆强度,重要函数用更复杂的替换

实战中如果遇到那种全是浮点数运算、各种类型转换、还配合BCF插花指令让IDA无法F5的样本,确实会很恶心。不过这种极端混淆也会带来巨大的性能开销,需要在保护强度和执行效率间找平衡。