一、手把手编译OLLVM(obfuscator-llvm),并应用到Android NDK

一、要获取最新版本的obfuscator-llvm分支,可以使用以下命令:

$ git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ../obfuscator/
$ make -j7

构建完成后,您应该拥有所有二进制文件build/bin。请注意,此源树包含LLVM和Clang。

用cmake编译的过程中,可能会出现如下问题

CMake Error at cmake/modules/AddLLVM.cmake:1163 (add_custom_target):
add_custom_target cannot create target "check-llvm-bindings-ocaml" because
another target with the same name already exists. The existing target is a
custom target created in source directory
"/home/xxx/obfuscator/test". See documentation
for policy CMP0002 for more details.
Call Stack (most recent call first):
cmake/modules/AddLLVM.cmake:1226 (add_lit_target)
test/CMakeLists.txt:150 (add_lit_testsuites)

修改为以下命令再尝试编译可顺利通过

cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../fuscator/

二、将build/bin里的 clang++ 、clang、clang-format、  clang-4.0、拷贝到 android-ndk-r16b/toolchains/llvm/prebuilt/linux-x86_64/bin(覆盖前请自行备份) 。 在CMakeLists.txt 里的 SET(CMAKE_CXX_FLAGS 追加 -mllvm -fla ,CMAKE_C_FLAGS 也可以加,然后 重新编译你的项目。(我这里是用cmake编译,如果你是ndk-build 那么你可以在 LOCAL_CFLAGS、LOCAL_CPPFLAGS 里添加)

重新编译,你可能会发现缺少头文件,从obfuscator 里的build 文件里按文件名一搜,拷贝到报错的目录即可。

基于LLVM IR的源代码混淆的实现

源代码混淆有两种方法:一种是对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指令进行运算。至于浮点数,有很多浮点数的软件实现可供参考。

猜你喜欢

转载自blog.csdn.net/ab6326795/article/details/81262570