標籤:android ndk o-llvm 混淆
@author ASCE1885的 Github 簡書 微博 CSDN
原文連結
Android開發中經常需要對敏感資訊進行加密,避免不了要將密鑰存放在終端裝置上,那麼如何防止密鑰被逆向出來呢?這是一個先有雞還是先有蛋的悖論。相比較將密鑰寫在Java層,將其下移到NDK層是個更好的選擇,本文就來介紹如何對NDK層代碼進行混淆,以更好的保護我們的密鑰。
混淆是一種用來隱藏程式意圖的技術,具體的實現技術可能差別比較大,最有效技術可以增加逆向工程和破解的難度,防止智慧財產權被竊取。已經有很多第三方的軟體可以用來混淆我們的Android應用,常見的有:
- Proguard
- DashO
- Dexguard
- DexProtector
- ApkProtect
- Shield4j
- Stringer
- Allitori
這些混淆器在代碼中起作用的層次是不一樣的。Android編譯的大致流程如下:
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打包成二進位疊加包,你也可以參照本文最後一節的步驟自己從源碼進行編譯。混淆器的二進位疊加包:
- OSX-NDK-Obfuscator.tar.bz2
- android-ndk64-r10-linux-x86_64-obfuscator.tar
下載正確的二進位疊加包,將它解壓到你電腦的NDK目錄中。
配置O-LLVM NDK工程
現在讓我們對NDK工程進行配置,使其支援O-LLVM混淆器。我們工程目錄結構如下所示:
? AndroidObfuscation-NDK git:(master) tree ..├── jni│ ├── Android.mk│ ├── Application.mk│ └── obfuscationTest.c
工程的Application.mk內容如下:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)APP_ABI := armeabiNDK_TOOLCHAIN_VERSION := clang3.4-obfuscatorinclude $(BUILD_EXECUTABLE)
混淆器的各種代碼轉換可以參見Obfuscator Wiki。可以通過LOCAL_CFLAGS標籤把這些轉換標記設定給混淆器。記住混淆器的轉換標記需要以-mllvm開頭,這樣clang編譯器可以傳遞它。
Android.mk的配置樣本如下:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := obfuscatedLOCAL_SRC_FILES := obfuscationTest.cLOCAL_LDLIBS := -staticLOCAL_CFLAGS := -mllvm -sub -mllvm -fla -mllvm -bcfinclude $(BUILD_EXECUTABLE)
現在可以編譯我們的工程了:
? 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”標記來使能字串混淆功能。
? AndroidObfuscation-NDK git:(master) cat jni/obfuscationTest.c#include <stdio.h>int main(void){ printf("Hello, world\n"); return 0;}
這個例子中,在使用字串混淆功能之前效果如下:
? AndroidObfuscation-NDK git:(master) strings libs/armeabi/obfuscated | grep HelloHello, world
使用字串混淆功能之後:
? AndroidObfuscation-NDK git:(master) strings libs/armeabi/obfuscated | grep Hello
從源碼構建適用於NDK的O-LLVM
git clone -b llvm-3.4 https://github.com/obfuscator-llvm/obfuscator.gitcd obfuscatormkdir buildcd buildcmake -DCMAKE_BUILD_TYPE:String=Release ../obfuscator/make -j5
構建o-llvm的完整指南參見這裡,不過上面的說明應該足夠了。
cp -r $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4 $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4-obfuscator
開啟檔案
$NDK_PATH/toolchains/arm-linux-androideabi-clang3.4-obfuscator/setup.mk
將檔案裡面的:
TARGET_CC := $(LLVM_TOOLCHAIN_PREFIX)clang$(HOST_EXEEXT)TARGET_CXX := $(LLVM_TOOLCHAIN_PREFIX)clang++$(HOST_EXEEXT)
修改為(記得修改o-llvm為你自己電腦上面的路徑)
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)
文末攝影鑒賞
使用O-LLVM和NDK對Android應用進行混淆