美文网首页
Android NDK混淆

Android NDK混淆

作者: 小子不坏_sprite | 来源:发表于2018-08-30 11:20 被阅读0次

    项目原因研究Android的So库代码混淆,增加IDA工具查看的复杂度。简单接触了ollvm 混淆组件。网上查看了很多人的文章,简单做个随笔备忘。


    1、ollvm下载编译

    我的是macbook环境。

    参考obfuscator官网:https://github.com/obfuscator-llvm/obfuscator/wiki

    执行下面的命令下载并编译:

    $ 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

    cmake找不到的话,上cmake.org官网下载,并设置环境变量。

    编译成功后,生成的文件主要在build/bin 和 build/lib 这2个文件夹。

    2、整合到NDK

    网上别人有介绍在NDK目录手动创建llvm之类的新文件夹,拷贝build/bin 和 build/lib2个文件夹,有编译问题,懒得去折腾。

    我的做法是:

    找到Android SDK目录中的 ../ndk-bundle/toolchains/llvm 文件夹,先备份下llvm文件夹,然后将obfuscator编译好的build/bin 和 build/lib2个文件夹直接拷贝到../llvm/prebuilt/darwin-x86_64/文件夹下,直接覆盖替换。

    我使用最新的Android Studio编辑器,编译so库已经集成cmake,不需要去修改config.mk 与 setup.mk也是正常的。

    3、使用OLLVM

    先尝试在自己so库工程的CMakeList.txt中加入:

    SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mllvm -fla")SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mllvm -fla")

    Release编译修改成下面的配置:

    SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mllvm -fla")SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mllvm -fla")

    再使用IDA工具查看so文件,发现函数内的代码有产生混淆修改。

    这里介绍下OLLVM的混淆参数,上面的官网github的wiki有说明这些参数:

    1、-fla   :for the control flow flattening pass

    2、-sub  :for the instruction substitution pass

    3、-bcf  : for the bogus control flow pass

    -fla

    表示使用控制流平展模式,最直观的感受就是简单的if-else语句,被嵌套成了while-switch语句,出现了很多干扰无用的分支,增加阅读难度。

    -mllvm -fla: activates control flow flattening

    -mllvm -split: activates basic block splitting. Improve the flattening when applied together.

    -mllvm -split_num=3: if the pass is activated, applies it 3 times on each basic block. Default: 1

    -sub

    表示使用指令替换模式,主要是将正常的运算操作(+,-,&,|等)替换成功能相等但表述更复杂的形式。

    比如,对于表达式 a = b + c,它的等价式可以有 a = – ( -b – c), a = b – (-c) 或 a = -(-b) + c 等,原表达式可以替换成任意相等式,或者通过随机数在多个相等式中做选择。

    SUB模式目前只支持整数运算操作,支持 + , – , & , | 和 ^ 操作,还是比较局限的。编译时,使用 -mllvm -sub 参数即可。下面参数可与-mllvm -sub参数配合使用。

    -mllvm -sub: activate instructions substitution

    -mllvm -sub_loop=3: if the pass is activated, applies it 3 times on a function. Default: 1

     -bcf

    表示使用控制流伪造模式,也是对程序的控制流做操作。BCF模式会在原代码块的前后随机插入新的代码块,新插入的代码块不是确定的,然后新代码块再通过条件判断跳转到原代码块中。

    更要命地是,原代码块可能会被克隆并插入随机的垃圾指令。这么多不确定性,就导致对同一份代码多次做BCF模式的混淆时,得到的是不同的混淆效果。可见,BCF混淆模式还是很强大的,不同于FLA那种较确定的混淆模式。使用BCF模式编译时配置参数 -mllvm -bcf即可,此外,BCF模式还支持其它几个参数,下面参数与-mllvm -bcf参数配合使用。

    -mllvm -perBCF=20: 对所有函数都混淆的概率是20%,默认100%

    -mllvm -bcf: activates the bogus control flow pass

    -mllvm -bcf_loop=3: 对函数做3次混淆,默认1次

    -mllvm -bcf_prob=40:  代码块被混淆的概率是40%,默认30%


    备注:

    参数前都需要有-mllvm,比如,CMakeList.txt中添加:

    SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mllvm -fla -mllvm -bcf -mllvm -sub ")

    有的时候,由于效率或其他原因的考虑,我们只想给指定的函数混淆,OLLVM也提供了对这一特性的支持。比如,想对函数func()使用bcf混淆,只需要给函数func()增加bcf属性即可。

    int func()  __attribute__ ((__annotate__ (("bcf"))))

    fla,sub和bcf三个属性可以搭配使用。如果不想对func()函数使用bcf属性,那标记为“nobcf”即可。

    4、混淆代码中的字符串常量

    上海交大密码与计算机安全实验室GoSSIP小组开源了他们设计的基于LLVM 4.0的孤挺花混淆框架,实现了一个用于字符串加密的pass。

    字符串加密的pass位于如下目录:

        Armariris/include/llvm/Transforms/Obfuscation/StringObfuscation.h

        Armariris/lib/Transforms/Obfuscation/StringObfuscation.cpp

     提取出该文件,放到OLLVM相同目录下,并将头文件也复制到对应目录下.

    在Obfuscation下的cmakelists.txt将StringObfuscation.cpp添加到编译库中,

    add_llvm_library(LLVMObfuscation

    CryptoUtils.cpp

    Substitution.cpp

    StringObfuscation.cpp

    BogusControlFlow.cpp

    Utils.cpp

    SplitBasicBlocks.cpp

    Flattening.cpp

    )

    最后只需要在Transforms/IPO下的PassManagerBuilder.cpp将字符串加密的编译选项添加进去即可

    1. 在PassManagerBuilder.cpp中添加引用:

    #include "llvm/Transforms/Obfuscation/StringObfuscation.h"

    2. 在PassManagerBuilder.cpp中的合适的地方插入以下加粗的两条函数声明,即编译时的编译参数-mllvm -sobf: 

    static cl::optEnableMLSM("mlsm", cl::init(true), cl::Hidden, cl::desc("Enable motion of merged load and store"));

    static cl::opt Seed("seed", cl::init(""),cl::desc("seed for the random"));

    static cl::opt StringObf("sobf", cl::init(false),cl::desc("Enable the string obfuscation"));

    3. 在PassManagerBuilder::PassManagerBuilder()构造函数中添加随机数因子的初始化:

    加粗的那一行代码就是了。

    void PassManagerBuilder::populateModulePassManager(

        legacy::PassManagerBase &MPM) {

    ...

     MPM.add(createForceFunctionAttrsLegacyPass());

    MPM.add(createStringObfuscation(StringObf));

    MPM.add(createSplitBasicBlock(Split));

    ...

    }

    参数用法:

    编译时候添加选项开启字符串加密:   -mllvm -sobf

    开启控制流扁平化: -mllvm -fla

    开启指令替换: -mllvm -sub

    指定随机数生成器种子:  -mllvm -seed=0xdeadbeaf

    最后命令调用:

    #SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mllvm -sobf")

    #SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mllvm -sobf")

    IDA查看so库,可以看到常量字符串被混淆成乱码了。

    5、OLLVM 5.0版本的参考:

    https://github.com/qtfreet00/llvm-obfuscator

    相关文章

      网友评论

          本文标题:Android NDK混淆

          本文链接:https://www.haomeiwen.com/subject/skdkwftx.html