-
-
基于LLVM IR的源代码混淆的实现
-
发表于:
2016-7-21 00:24
13776
-
源代码混淆有两种方法:一种是对C/C++的AST进行操作,由于最终输出的还是源代码,因此通用性强,只不过没有资料可供参考,较难实现。另一种是对各编译器的IR进行操作,如LLVM IR或GCC的gimple等,限制于各编译器目标cpu的实现,通用性比较差。基于LLVM IR的源代码混淆由于有可供参考的资料,实现比较容易。
一、控制流平坦化
该实现已经在控制流平坦化的实现(http://bbs.pediy.com/showthread.php?t=209203)一文中说明,实际上控制流平坦化有点像虚拟机保护,case块相当于vm的handle,case值相当于vm的opcode。
二、虚假分支
生成条件跳转指令,有两种方法:一种称为opaque predicate,另一种称为bogus control flow。假设基本块block0,opaque predicate后变为:if(恒等式){block0;}else{垃圾代码}或if(恒不等式){垃圾代码}else{block0;};bogus control flow后变为if(随机条件){block0;}else{block1;},else的基本块block1复制于block0。
三、多重分支
把跳转指令BranchInst变为switch指令SwitchInst。先生成一个switch变量v,当指令是无条件跳转指令时,设置v为一个常量;当指令是条件跳转时,设置v为用SelectInst生成两个常量语句。常量在SwitchInst中所对应的case块的后继块就是跳转指令的后继块,其余的case块的后继块则可以随机选择一个基本块作为为后继块。
四、基本块分割
把基本块分成多个基本块。有两种方法:一种是根据概率对基本块的当前指令进行分割;另一种是计算基本块的总指令数,标记为a,随机生成小于a的数,标记为b,基本块b条指令后进行分割,a减掉b,进行多次迭代直至a等于1。
五、基本块置换
让基本块的前驱块不一定在前面,后继块不一定在后面。对入口块进行块分割,随机化除入口块的其它基本块,放在入口块后面。
六、常量展开
该实现也在控制流平坦化实现(http://bbs.pediy.com/showthread.php?t=209203)一文中说明。
七、字符串加密
有两种方法:一种是字符串的各个元素都有自己的加密方法,这样解密方法就是常量展开,先随机生成加密字符串,然后对目标字符串各个元素以加密字符串常量展开。另一种是共用同样的加密算法,其操作必须可逆,如xor、sub、add,在用加密算法生成加密字符串时,用栈记录每次运算的常量值和运算的可逆运算(add对应sub,sub对应add,xor对应xor),最后根据记录栈生成解密算法。
八、常量数组随机化
让常量在数组中的索引随机化。假设常量数组a[],索引值i,随机生成索引数组b[]、c[]、d[](可生成更多),循环用z[b[c[d[i]]]]=a[i]生成随机后的常量数组z[],然后用z[b[c[d[i]]]]替换a[i]。
九、常量隐藏
1、数组元素
把常量放在常量数组,进行常量数组随机化,然后进行数组访问。
2、生成函数
记录多个常量,每个常量就是一个函数参数,再把与常量相关的操作搬进函数内,当然常量要替换成函数参数。
十、表达式变换
对逻辑操作not、and、or、xor,可以先把操作数抽取分成多个更小的数进行操作,最后用or连接起来,当然这四个操作可以用与非门或是或非门来完成。还有其它常见的运算可以把操作数看成大数然后进行大数运算,还有一些运算可以转变为SIMD指令进行运算。至于浮点数,有很多浮点数的软件实现可供参考。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)