Android LLVM-Obfuscator C/C++ 混淆编译的深入研究

一、 LLVM是什么?
(1)LLVM是lowlevel virtual machine的简称,是一个编译器框架。苹果公司的Xcode 4.0之后用的都是LLVM编译器。
(2)LLVM 诞生于2003.10伊利诺伊大学香槟分校,创始人ChrisLattner,现任苹果公司『开发者工具』部门的主管。
   
二、 LLVM-Obfuscator 是什么?
(1)LLVM-Obfuscator 是瑞士西北应用科技大学安全实验室针对LLVM编译组件开发的代码混淆工具,该工具完全开源,目的是为了增加逆向工程的难度,保证代码的安全性。
(2)Obfuscator-llvm最新版本集成了LLVM-3.4编译器,并且兼容LLVM支持的所有语言(C,C++, Objective-C, Ada and Fortran)和平台(x86, x86-64, PowerPC, PowerPC-64,ARM, Thumb, SPARC, Alpha, CellSPU, MIPS, MSP430, SystemZ,and XCore)。


Github地址:https://github.com/obfuscator-llvm/obfuscator/wiki

1. 混淆方法一: InstructionsSubstitution。

The goal of this obfuscation technique simply consists in replacing standard binary operators (like addition, subtraction or boolean operators) by functionally equivalent, but more complicated sequences of instructions. When several equivalent instructions sequence are available, one is chosen at random.

This kind of obfuscation is rather straightforward and does not add a lot of security, as it can easily be removed by re-optimizing the generated code. However, provided the pseudo-random generator is seeded with different values, instructions substitutions bring diversity in the produced binary.

Currently, only operators on integers are available, as substituting operators on floating-point values bring rounding errors and unnecessary numerical inaccuracy.

[html]  view plain  copy
  1. -mllvm -sub: activate instructions substitution  
  2. -mllvm -funcSUB="func1,func2,func3": if instructions substitution is activated, apply it only on functions func1, func2 and func3  
  3. -mllvm -perSUB=20: if instructions substitution is activated, apply it with a probability of 20% on each function  
2. 混淆方法二: BogusControlFlow。
This method modifies a function call graph by adding a basic block before the current basic block. This new basic block contains an opaque predicate and then makes a conditional jump to the original basic block.

The original basic block is also cloned and filled up with junk instructions chosen at random.


[html]  view plain  copy
  1. -mllvm -bcf: activates the bogus control flow pass  
  2. -mllvm -funcBCF="func1,func2,func3": if the pass is activated, applies it only on functions func1, func2, func3  
  3. -mllvm -perBCF=20: if the pass is activated, applies it on all functions with a probability of 20%. Default: 100  
  4. -mllvm -boguscf-loop=3: if the pass is activated, applies it 3 times on a function. Default: 1  
  5. -mllvm -boguscf-prob=40: if the pass is activated, a basic bloc will be obfuscated with a probability of 40%. Default: 30  
3. 混淆方法三: ControlFlow Flattening。
The purpose of this pass is to completely flatten the control flow graph of a program.

For a detailed explanation of the control flow flattening technique, see for instance the paper of T László and Á Kiss, Obfuscating C++ programs via control flow flattening, Annales Univ. Sci. Budapest., Sect. Comp. 30 (2009) 3-19.

Note however that our algorithm fully flattens the control flow, which is not the case of the one of László and Kiss.

[html]  view plain  copy
  1. -mllvm -fla: activates control flow flattening  
  2. -mllvm -funcFLA="func1,func2,func3": if control flow flattening is activated, apply it only on functions func1, func2 and func3  
  3. -mllvm -perFLA=20: if control flow flattening is activated, apply it with a probability of 20% on each function  
4. 如何用开源 source code 编译出混淆器O-LLVM ?
[cpp]  view plain  copy
  1. $ git clone -b llvm-3.5 https://github.com/obfuscator-llvm/obfuscator.git  
  2. $ mkdir build  
  3. $ cd build  
  4. $ cmake -DCMAKE_BUILD_TYPE:String=Release ../obfuscator/  
  5. $ make -j5  
编译后的结果只有bin 和 lib 是有用的,其余的都可以删除:

三、 LLVM-Obfuscator for iOS: Xcode + Obfuscator
略。
四、 LLVM-Obfuscator for Android:NDK+Obfuscator
Android: 从NDK-r8d以后(目前最新是r10)就已经在toolchains中集成了Clang-LLVM编译器,因此可以通过修改NDK的配置文件来集成O-llvm:

1. NDK-Build 默认采用GCC编译器。ndk-build 其实就是对GNU Make的封装,它的目的是调用系统NDK编译脚本,它等价于 make -f $NDK/build/core/build-local.mk。
    ndk-build  命令行参数:

2. NDK工具链toolchains组织结构

3. 如何将O-LLVM 集成到NDK 交叉编译工具链中?
(1)对于独立编译方式,先用 make-standalone-toolchain.sh脚本创建 standalone-toolchain, 然后将LLVM-Obfuscator源码编译生成的 /bin 和/lib 拷贝到standalone-toolchain 对应的folder下面。然后利用 android. toolchain.cmake 脚本配置交叉编译环境,然后在shell 脚步中执行编译命令:
[html]  view plain  copy
  1. cmake -DOS=ANDROID   
  2.       -DANDROID_ABI=armeabi   
  3.       -DANDROID_STANDALONE_TOOLCHAIN=standalon-toolchain   
  4.       -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake .  
  5. make -j8  
编译错误一直没有解决:(直接放弃集成独立编译方式)
-ld: library not found for –lSystem
-ld:no archive symbol table (run ranlib)
-“-soname” is not supported by LLVM/Clang
(2)对于ndk-build方式, 首先按照NDK 文件结构和命名规则在toolchain folder 下创建obfuscator-llvm, 并将原有的LLVM-3.4 下面的setup.mk, config.mk, setup-common.mk 直接拷贝到obfuscator-llvm 中,不用任何修改。并把源码编译出来的混淆器lib和bin 文件夹(参见上述 “如何用开源 source code 编译出混淆器O-LLVM”),按照 LLVM-3.4 的目录结构 /prebuild/darwin-x86_64 也拷贝到obfuscator-llvm 中。
然后分别创建arm-linux-androideabi-obfuscator, mipsel-linux-android-obfuscator, x86-obfuscator 文件夹,注意命名必须保持一致,只有后缀“obfuscator” 可以变化,其它都不得修改。并把与之对应的arm-linux-androideabi-clang3.4, mipsel-linux-android-clang3.4, x86-clang3.4文件夹下的config.mk和 setup.mk 都拷贝过来,但需要在setup.mk中修改LLVM_NAME, TARGET_CC 和 TARGET_CXX, 其它都不用动。到此就一切OK了。
[html]  view plain  copy
  1. LLVM_NAME :obfuscator-llvm-3.4  
  2. TARGET_CC := $(LLVM_TOOLCHAIN_PREFIX)clang$(HOST_EXEEXT)  
  3. TARGET_CXX := $(LLVM_TOOLCHAIN_PREFIX)clang++$(HOST_EXEEXT)  

配置好NDK 的ToolChain之后,进行混淆编译只需要在Application.mk中指定编译器名字即可:
[html]  view plain  copy
  1. NDK_TOOLCHAIN_VERSION :obfuscator  
然后在Android.mk 中设置混淆参数
[html]  view plain  copy
  1. LOCAL_CFLAGS += -mllvm -sub -mllvm -bcf -mllvm -fla  
最后命令行下直接执行:
[html]  view plain  copy
  1. $ ndk-build APP_ABI="armeabi armeabi-v7a mips”  
已知问题:
-只能编译ARM,MIPS,无法编译x86,编译器编译报错

-__asm__ 内联汇编指令无法编译
-inline 函数无法编译
五、如何解决NDK + LLVM-Obfuscator 无法编译x86的问题?
因为NDK的eabi=x86没有添加到LLVM-Obfuscator中, 导致在link的时候使用来系统的默认linker,从而报错。
使用下面的patch之后,重新编译LLVM-Obfuscator的源码,生成的混淆编译器可以支持arm/arm64, x86/x86_64和 mipsel (注: mips64会有异常linking mips:isa64 module with previous mips:isa64r6, 估计是clang产生的指令和linker要求的不一样)。
在 obfuscator/tools/clang/lib/Driver/ToolChains.cpp文件中,从1112行开始按下面的(“-”删除一行)(“+”增加一行)修改文件:
[cpp]  view plain  copy
  1. @@ -1112,30 +1112,35 @@   
  2. void Generic_GCC::GCCInstallationDetector::print(raw_ostream &OS) const {  
  3.    // lifetime or initialization issues.  
  4.    static const char *const AArch64LibDirs[] = { "/lib" };  
  5.    static const char *const AArch64Triples[] = { "aarch64-none-linux-gnu",  
  6. -                                                "aarch64-linux-gnu" };  
  7. +                                                "aarch64-linux-gnu",  
  8. +                                                "aarch64-linux-android"  
  9. +  };  
  10.    
  11.    static const char *const ARMLibDirs[] = { "/lib" };  
  12.    static const char *const ARMTriples[] = { "arm-linux-gnueabi",  
  13.                                              "arm-linux-androideabi" };  
  14.    static const char *const ARMHFTriples[] = { "arm-linux-gnueabihf",  
  15. +                                              "arm-linux-androideabihf",  
  16.                                                "armv7hl-redhat-linux-gnueabi" };  
  17.    
  18.    static const char *const X86_64LibDirs[] = { "/lib64""/lib" };  
  19.    static const char *const X86_64Triples[] = {  
  20.      "x86_64-linux-gnu""x86_64-unknown-linux-gnu""x86_64-pc-linux-gnu",  
  21.      "x86_64-redhat-linux6E""x86_64-redhat-linux""x86_64-suse-linux",  
  22. -    "x86_64-manbo-linux-gnu""x86_64-linux-gnu""x86_64-slackware-linux"  
  23. +    "x86_64-manbo-linux-gnu""x86_64-linux-gnu""x86_64-slackware-linux",  
  24. +    "x86_64-linux-android"  
  25.    };  
  26.    static const char *const X86LibDirs[] = { "/lib32""/lib" };  
  27.    static const char *const X86Triples[] = {  
  28.      "i686-linux-gnu""i686-pc-linux-gnu""i486-linux-gnu""i386-linux-gnu",  
  29.      "i386-redhat-linux6E""i686-redhat-linux""i586-redhat-linux",  
  30.      "i386-redhat-linux""i586-suse-linux""i486-slackware-linux",  
  31. -    "i686-montavista-linux"  
  32. +    "i686-montavista-linux""i686-linux-android"  
  33.    };  
  34.    
  35.    static const char *const MIPSLibDirs[] = { "/lib" };  
  36.    static const char *const MIPSTriples[] = { "mips-linux-gnu",  
  37. +                                             "mips-linux-android",  
  38.                                               "mips-mti-linux-gnu" };  
  39.    static const char *const MIPSELLibDirs[] = { "/lib" };  
  40.    static const char *const MIPSELTriples[] = { "mipsel-linux-gnu",  
  41. @@ -1143,23 +1148,28 @@ void Generic_GCC::GCCInstallationDetector::print(raw_ostream &OS) const {  
  42.    
  43.    static const char *const MIPS64LibDirs[] = { "/lib64""/lib" };  
  44.    static const char *const MIPS64Triples[] = { "mips64-linux-gnu",  
  45. +                                               "mips64-linux-android",  
  46.                                                 "mips-mti-linux-gnu" };  
  47.    static const char *const MIPS64ELLibDirs[] = { "/lib64""/lib" };  
  48.    static const char *const MIPS64ELTriples[] = { "mips64el-linux-gnu",  
  49. -                                                 "mips-mti-linux-gnu" };  
  50. +                                                 "mips-mti-linux-gnu",  
  51. +                                                 "mips64el-linux-android"  
  52. +  };  
  53.    
  54.    static const char *const PPCLibDirs[] = { "/lib32""/lib" };  
  55.    static const char *const PPCTriples[] = {  
  56.      "powerpc-linux-gnu""powerpc-unknown-linux-gnu""powerpc-linux-gnuspe",  
  57. -    "powerpc-suse-linux""powerpc-montavista-linuxspe"  
  58. +    "powerpc-suse-linux""powerpc-montavista-linuxspe""powerpc-linux-android"  
  59.    };  
  60.    static const char *const PPC64LibDirs[] = { "/lib64""/lib" };  
  61.    static const char *const PPC64Triples[] = { "powerpc64-linux-gnu",  
  62. +                                              "powerpc64-linux-android",  
  63.                                                "powerpc64-unknown-linux-gnu",  
  64.                                                "powerpc64-suse-linux",  
  65.                                                "ppc64-redhat-linux" };  
  66.    static const char *const PPC64LELibDirs[] = { "/lib64""/lib" };  
  67.    static const char *const PPC64LETriples[] = { "powerpc64le-linux-gnu",  
  68. +                                                "powerpc64le-linux-android",  
  69.                                                  "powerpc64le-unknown-linux-gnu",  
  70.                                                  "powerpc64le-suse-linux",  
  71.                                                  "ppc64le-redhat-linux" };  
  72. @@ -1167,7 +1177,7 @@ void Generic_GCC::GCCInstallationDetector::print(raw_ostream &OS) const {  
  73.    static const char *const SystemZLibDirs[] = { "/lib64""/lib" };  
  74.    static const char *const SystemZTriples[] = {  
  75.      "s390x-linux-gnu""s390x-unknown-linux-gnu""s390x-ibm-linux-gnu",  
  76. -    "s390x-suse-linux""s390x-redhat-linux"  
  77. +    "s390x-suse-linux""s390x-redhat-linux""s390x-linux-android"  
  78.    };  
发布了60 篇原创文章 · 获赞 44 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/beyond702/article/details/52690586