混淆是一種用來隱藏程式意圖的技術,具體的實現技術可能差別比較大,最有效的技術可以增加逆向工程和破解的難度,防止智慧財產權被竊取。已經有很多第三方的軟體可以用來混淆我們的Android應用,常見的有:
- Proguard
- DashO
- Dexguard
- DexProtector
- ApkProtect
- Shield4j
- Stringer
- Allitori
這些混淆器在程式碼中起作用的層次是不一樣的。Android編譯的大致流程如下:
1 |
Java Code(.java) -> Java Bytecode(.class) -> Dalvik Bytecode(classes.dex) |
有的混淆器是在編譯之前直接作用於java原始碼,有的作用於java位元組碼,有的作用於Dalvik位元組碼。
Android NDK使得開發者可以繞過虛擬機器從而進一步提高程式效能,或者更直接的與核心和硬體互動。Google對NDK的描述是:“NDK是允許開發者使用原生C/C++語言開發app的一套工具集。這樣有利於某些型別的app複用C/C++編寫的已有程式碼庫,當然大部分app不需要使用Android NDK”。
相對於Dalvik虛擬機器層次的混淆而言,原生語言(C/C++)元件的程式碼混淆選擇並不多,Obfuscator-LLVM工程是一個值得關注的例外。這個專案專注於LLVM編譯器,這一點使得它可移植性很高,相容LLVM支援的所有語言(C,C++, Objective-C, Ada and Fortran)和平臺(x86, x86-64, PowerPC, PowerPC-64,ARM, Thumb, SPARC, Alpha, CellSPU, MIPS, MSP430, SystemZ,and XCore)。0vercl0k在o-llvm釋出之前發表了一篇論文,解釋了使用LLVM編譯器的優點以及簡單的程式碼轉換。
我使用O-LLVM和NDK已經有一段時間了。在瞭解到TowelRoot也在使用O-LLVM時,我決定寫一篇文章來介紹它。TowelRoot是一款Android一鍵Root工具,關於它是如何利用Linux核心bug來達到root目的的可以參見這篇文章。TowelRoot使用O-LLVM主要用來防止其他人拷貝並利用它來實現非法目的,同時防止被重打包後並出售。
下面我們就來講解如何開始使用O-LLVM來混淆原生程式碼,實現類似TowelRoot的目的。
使用NDK O-LLVM二進位制疊加包
我已經在OSX和Linux平臺上把混淆器基於NDK打包成二進位制疊加包,你也可以參照本文最後一節的步驟自己從原始碼進行編譯。混淆器的二進位制疊加包下載地址:
下載正確的二進位制疊加包,將它解壓到你電腦的NDK目錄中。
配置O-LLVM NDK工程
現在讓我們對NDK工程進行配置,使其支援O-LLVM混淆器。我們工程目錄結構如下所示:
1 2 3 4 5 6 |
➜ AndroidObfuscation-NDK git:(master) tree . . ├── jni │ ├── Android.mk │ ├── Application.mk │ └── obfuscationTest.c |
工程的Application.mk內容如下:
1 2 3 4 5 6 7 8 9 |
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) APP_ABI := armeabi NDK_TOOLCHAIN_VERSION := clang3.4-obfuscator include $(BUILD_EXECUTABLE) |
混淆器的各種程式碼轉換可以參見Obfuscator Wiki。可以通過LOCAL_CFLAGS標籤把這些轉換標記設定給混淆器。記住混淆器的轉換標記需要以-mllvm開頭,這樣clang編譯器可以傳遞它。
Android.mk的配置示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := obfuscated LOCAL_SRC_FILES := obfuscationTest.c LOCAL_LDLIBS := -static LOCAL_CFLAGS := -mllvm -sub -mllvm -fla -mllvm -bcf include $(BUILD_EXECUTABLE) |
現在可以編譯我們的工程了:
1 2 3 4 |
➜ AndroidObfuscation-NDK git:(master) ndk-build [armeabi] Compile thumb : obfuscated <= obfuscationTest.c [armeabi] Executable : obfuscated [armeabi] Install : obfuscated => libs/armeabi/obfuscated |
使用了上面的配置和指令碼結構的例子工程可以參見AndroidObfuscation-NDK。
上面我預先構建的二進位制疊加包包含了由yag00貢獻的試驗性的字串混淆技術。你可以通過給LOCAL_CFLAGS傳遞“-mllvm -xse”標記來使能字串混淆功能。
1 2 3 4 5 6 7 |
➜ AndroidObfuscation-NDK git:(master) cat jni/obfuscationTest.c #include <stdio.h> int main(void){ printf("Hello, world\n"); return 0; } |
這個例子中,在使用字串混淆功能之前效果如下:
1 2 |
➜ AndroidObfuscation-NDK git:(master) strings libs/armeabi/obfuscated | grep Hello Hello, world |
使用字串混淆功能之後:
1 |
➜ AndroidObfuscation-NDK git:(master) strings libs/armeabi/obfuscated | grep Hello |
從原始碼構建適用於NDK的O-LLVM
1 2 3 4 5 6 |
git clone -b llvm-3.4 https://github.com/obfuscator-llvm/obfuscator.git cd obfuscator mkdir build cd build cmake -DCMAKE_BUILD_TYPE:String=Release ../obfuscator/ make -j5 |
構建o-llvm的完整指南參見這裡,不過上面的說明應該足夠了。
1 |
cp -r $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4 $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4-obfuscator |
開啟檔案
1 |
$NDK_PATH/toolchains/arm-linux-androideabi-clang3.4-obfuscator/setup.mk |
將檔案裡面的:
1 2 |
TARGET_CC := $(LLVM_TOOLCHAIN_PREFIX)clang$(HOST_EXEEXT) TARGET_CXX := $(LLVM_TOOLCHAIN_PREFIX)clang++$(HOST_EXEEXT) |
修改為(記得修改o-llvm為你自己電腦上面的路徑)
1 2 3 |
LLVM_TOOLCHAIN_PATH := <PATH_TO_OBFUSCATOR_REPO>/build/bin/ TARGET_CC := $(LLVM_TOOLCHAIN_PATH)clang$(HOST_EXEEXT) TARGET_CXX := $(LLVM_TOOLCHAIN_PATH)clang++$(HOST_EXEEXT) |