項目原因研究Android的So庫代碼混淆,增加IDA工具查看的復雜度。簡單接觸了ollvm 混淆組件。網(wǎng)上查看了很多人的文章,簡單做個隨筆備忘。
1、ollvm下載編譯
我的是macbook環(huán)境。
參考obfuscator官網(wǎng):https://github.com/obfuscator-llvm/obfuscator/wiki
執(zhí)行下面的命令下載并編譯:
$ 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官網(wǎng)下載,并設置環(huán)境變量。
編譯成功后,生成的文件主要在build/bin 和 build/lib 這2個文件夾。
2、整合到NDK
網(wǎng)上別人有介紹在NDK目錄手動創(chuàng)建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庫已經(jīng)集成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文件,發(fā)現(xiàn)函數(shù)內(nèi)的代碼有產(chǎn)生混淆修改。
這里介紹下OLLVM的混淆參數(shù),上面的官網(wǎng)github的wiki有說明這些參數(shù):
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語句,出現(xiàn)了很多干擾無用的分支,增加閱讀難度。
-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 等,原表達式可以替換成任意相等式,或者通過隨機數(shù)在多個相等式中做選擇。
SUB模式目前只支持整數(shù)運算操作,支持 + , – , & , | 和 ^ 操作,還是比較局限的。編譯時,使用 -mllvm -sub 參數(shù)即可。下面參數(shù)可與-mllvm -sub參數(shù)配合使用。
-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模式會在原代碼塊的前后隨機插入新的代碼塊,新插入的代碼塊不是確定的,然后新代碼塊再通過條件判斷跳轉(zhuǎn)到原代碼塊中。
更要命地是,原代碼塊可能會被克隆并插入隨機的垃圾指令。這么多不確定性,就導致對同一份代碼多次做BCF模式的混淆時,得到的是不同的混淆效果。可見,BCF混淆模式還是很強大的,不同于FLA那種較確定的混淆模式。使用BCF模式編譯時配置參數(shù) -mllvm -bcf即可,此外,BCF模式還支持其它幾個參數(shù),下面參數(shù)與-mllvm -bcf參數(shù)配合使用。
-mllvm -perBCF=20:?對所有函數(shù)都混淆的概率是20%,默認100%
-mllvm -bcf: activates the bogus control flow pass
-mllvm -bcf_loop=3:?對函數(shù)做3次混淆,默認1次
-mllvm -bcf_prob=40: ?代碼塊被混淆的概率是40%,默認30%
備注:
參數(shù)前都需要有-mllvm,比如,CMakeList.txt中添加:
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mllvm -fla -mllvm -bcf -mllvm -sub ")
有的時候,由于效率或其他原因的考慮,我們只想給指定的函數(shù)混淆,OLLVM也提供了對這一特性的支持。比如,想對函數(shù)func()使用bcf混淆,只需要給函數(shù)func()增加bcf屬性即可。
int func() ?__attribute__ ((__annotate__ (("bcf"))))
fla,sub和bcf三個屬性可以搭配使用。如果不想對func()函數(shù)使用bcf屬性,那標記為“nobcf”即可。
4、混淆代碼中的字符串常量
上海交大密碼與計算機安全實驗室GoSSIP小組開源了他們設計的基于LLVM 4.0的孤挺花混淆框架,實現(xiàn)了一個用于字符串加密的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中的合適的地方插入以下加粗的兩條函數(shù)聲明,即編譯時的編譯參數(shù)-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()構(gòu)造函數(shù)中添加隨機數(shù)因子的初始化:
加粗的那一行代碼就是了。
void PassManagerBuilder::populateModulePassManager(
????legacy::PassManagerBase &MPM) {
...
?MPM.add(createForceFunctionAttrsLegacyPass());
MPM.add(createStringObfuscation(StringObf));
MPM.add(createSplitBasicBlock(Split));
...
}
參數(shù)用法:
編譯時候添加選項開啟字符串加密: ??-mllvm -sobf
開啟控制流扁平化:?-mllvm -fla
開啟指令替換:?-mllvm -sub
指定隨機數(shù)生成器種子: ?-mllvm -seed=0xdeadbeaf
最后命令調(diào)用:
#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