1. 程式人生 > >Andorid APK反逆向解決方案:梆梆加固原理探尋

Andorid APK反逆向解決方案:梆梆加固原理探尋

一、序言

目前Android市場充斥著大量的盜版軟體,開發者的官方應用被“打包黨”們惡意篡改。如何使程式程式碼免受盜版篡改就成了開發者面臨的頭等大事,今天我們將分析一個不錯的解決方案---梆梆加固(http://www.secneo.com/appProtect/)。

通過對App進行加固保護。梆梆可以有效防止移動應用在運營推廣過程中被破解、盜版、二次打包、注入、反編譯等破壞,保障程式的安全性、穩定性,對移動應用的整體邏輯結構進行保護,保證了移動應用的使用者體驗。

二、梆梆加固逆向分析過程

首先我們通過對APK加固前後文件結構的比較,來了解梆梆加固對APK檔案所做的處理。為了使分析過程足夠簡單,我新建一個最簡單的測試程式,並上傳到梆梆加固,整個加固過程大概需要4天的時間才可以完成,這是一個漫長的等待過程。

該測試程式包含了Activity、Service、ContentProvider、BroadcastRecevier四大元件、Application、普通類、Jni呼叫等7類物件,目的就是全面的瞭解梆梆的加固效果。

1、apk加固前後靜態檔案結構及動態執行時對比分析

(1) 加固前後靜態檔案結構變化(左為加固前,右為加固後)

加固後apk新增以下檔案:

                  assets\meta-data\manifest.mf  //APK檔案列表SHA1-Digest
                  assets\meta-data\rsa.pub        //RSA公鑰資訊
                  assets\meta-data\rsa.sig         //數字簽名檔案
                  assets\classes.jar                    //已加密原classes.dex檔案
                  assets\com.example.hellojni    //ARM平臺二進位制可執行檔案
                  assets\com.example.hellojni.x86  //x86功能同上
                  libs\armeabi\libsecexe.so        //ARM平臺共享庫檔案
                  libs\x86\libsecexe.so               //x86功能同上

加固後修改檔案:

                 AndroidMainfest.xml  //(如果應用配置有Application資訊,則該檔案加固前後相同,如果應用未配置Application資訊,則該檔案加固前後不相同,梆梆會配置Application資訊為自己實現類)
                 classes.dex

對classes.dex進行反編譯,觀察程式碼樹結構變化:(左為加固前,右為加固後)

(2)加固前後動態執行時變化

執行原程式,系統僅建立一個相關程序,但是加固的程式,系統會為其同時建立三個相關程式程序:

程序啟動順序:597程序建立605程序,605程序又建立了607程序

通過檢視maps檔案獲取597程序對映檔案資訊

通過map檔案可以看出,597程序為主程序,android各元件在該程序中執行。

605和607程序並無與apk檔案相關檔案資訊,通過cmdline檢視啟動引數:

初步懷疑該程序為assets\com.example.hellojni 可執行檔案執行結果。

2、梆梆加固保護效果分析

我們通過逆向分析加固後的app,來看看梆梆加固對app的保護效果。

程式程式碼的第一執行點是Application物件,首先檢視TestApplication類物件。

程式的Util類完成大部分的java層邏輯,

ACall類主要完成對libsecexe.so JNI的呼叫:

檢視libsecexe.so檔案匯出函式,發現所有函式名都經過加密處理,與我們平時jni呼叫產生的函式名並不同。平時jni產生的函式名應該為這樣格式Java_com_secapk_wrapper_ACall_{函式名}

抗靜態分析:

Util類通過MyClassLoader完成對加密classes.jar的動態載入,記憶體中解密classes.jar,完成動態載入。

jni方法對應so函式名的混淆。

抗動態除錯: 

當使用IDA動態除錯該程式時,程式無法建立連線除錯。

梆梆加固可以有效常用的逆向分析方法。

三、梆梆加固技術實現關鍵點猜想

(1)如何使DexClassLoader動態載入元件具有生命週期?

根據APK檔案是否在AndroidManifest.xml配置Applicaiton資訊,梆梆加固會做不同的處理:

通過上傳Applicaiton不同配置的APK檔案,我們發現:

當APK配置有Applicaition資訊時,梆梆加固重寫Application類

當APK未配置Application資訊時,梆梆加固新建類,並在AndroidManifest.xml中配置自己Application類

因此Applicaiton就是程式的第一執行點。

我們知道DexClassLoader載入的類是沒有元件生命週期的,也就是說即使DexClassLoader通過對dex的 動態載入完成了對元件類的載入,              當系統啟動該元件時,還會出現載入類失敗的異常。我已經在“Android APK加殼技術方案”中提出了一種使DexClassLoader載入元件類具有生命週期的方法。

執行加固後的程式並通過Mat記憶體分析工具檢視類載入情況:

如上圖所示,元件類的載入類已經被修改為com.secapk.wrapper.MyClassLoader類,可以得出結論,該方式和我提出方式基本相同,通過修改系統元件類ClassLoader來實現。

(2)如何混淆native方法在so庫函式對應關係?

jni方法註冊方式有兩種:

1、通過javah產生函式頭,該種方式產生的方法具有固定的格式。該方式使逆向分析人員比較容易獲取java層native方法對應的本地方法。

2、在JNI_OnLoad方法中手動註冊jni方法,不易查詢對應關係。

使用第二種方式可以實現混淆java層native方法和so函式的對應關係。

  1. #include <string.h>  
  2. #include <jni.h>  
  3. JNIEXPORT jstring JNICALL abcdefghijklmn( JNIEnv* env,jobject thiz )   
  4. {   
  5.     return (*env)->NewStringUTF(env, "Hello from JNI !");   
  6. }   
  7. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)   
  8. {   
  9.     JNIEnv* env = NULL;   
  10.     jint result = -1;   
  11.     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {   
  12.         return JNI_ERR;   
  13.     }   
  14.     JNINativeMethod gMethods[] = {   
  15.         { "stringFromJNI""()Ljava/lang/String;", (void*)abcdefghijklmn },   
  16.     };   
  17.     jclass clazz = (*env)->FindClass(env, "com/example/hellojni/HelloJni");   
  18.     if (clazz == NULL) {   
  19.         return JNI_ERR;   
  20.     }   
  21.     if ((*env)->RegisterNatives(env, clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0){   
  22.         return JNI_ERR;   
  23.     }   
  24.     /* success -- return valid version number */
  25.     result = JNI_VERSION_1_4;   
  26.     return result;   
  27. }   

以上程式碼中的字串都是明文(比如“stringFromJNI”),如果這些文明字串都換成密文的話,再通過執行時解密,相應的對應關係更不易看出。

(3)如何使DexClassLoader載入加密的dex檔案?

雖然不瞭解梆梆加固是怎麼做的,不過通過分析它的執行邏輯,我推測了一種可行的實現方案:瞭解該方案需要對 Android DexClassLoader的整個載入流程需要有清 晰的瞭解。

首先推斷assets\classes.jar是一個加密的jar包。

正常的DexClassLoader載入的流程如下:會有一個DexOpt產生odex過程

但是梆梆加固後的應用DexClassLoader載入過程並沒有該過程的log資訊。

推斷加密的jar包裡面含有odex檔案,如果不是odex檔案的話,DexClassLoader肯定會在執行時釋放未加密的odex檔案到目錄,這樣的話被保護的邏輯也就洩露了。

DexClassLoader載入過程會在java層和C層產生不同的資料結構,java層並沒有實質性的資料, 所有的資料都在c層,我們可用通過底層程式碼完成dex資料的解析。底層dex分析是可以支援byte[]陣列的,解密 odex資料,傳遞過去就行了。這樣java層就可以呼叫了。

以下是大概虛擬碼實現步驟:

  1. int loadDex(char * dexFileName) 
  2.     char *dvm_lib_path = "/system/lib/libdvm.so"
  3. void * handle; 
  4. DvmGlobals gDvm; 
  5.     handle = dlopen( dvm_lib_path, int mode); 

1、讀取dexFileName檔案內容並解密到byte陣列。

2、呼叫dexFileParse函式解析byte陣列為DexFile

\dalvik\libdex\DexFile.c

DexFile* dexFileParse(const u1* data, size_t length, int flags)//dlsym(handle, "dexFileParse");

3、呼叫allocateAuxStructures轉換DexFile為DvmDex,(由於該方法為static方法,因此需要按照其邏輯自行實現)

\dalvik\vm\DvmDex.c

static DvmDex* allocateAuxStructures(DexFile* pDexFile) 

4、新增DvmDex到gDvm.userDexFiles

\dalvik\vm\Init.c

struct DvmGlobals gDvm; //gDvm = dlsym(handle, "gDvm");

5、修改MyDexClassLoader中的mDexs物件的mCookie值,mCookie主要用於對映底層DvmDex資料DexClassLoader.mDexs[0].mCookie值

(4)so如何實現程式的反除錯?

同linux反除錯基本原理相同,這裡提供一種方式就是在JNI_Onload中呼叫ptrace方 法,ptrace被廣泛用於除錯(比如IDA)和程序程式碼注入(比如LBE,金山等許可權管理功能實現),一個程序只能被一個程序ptrace,如果你自己 呼叫ptarce,這樣其它程式就無法通過ptrace除錯或者 向您程式程序注入程式碼。

ptrace(PTRACE_TRACEME,0 ,0 ,0);

通過本人實驗,該種方式可以實現so的反除錯。

三、總結

通過以上分析,梆梆加固的確可以有效防止移動應用在運營推廣過程中被破解、盜版、二次打包、注入、反編譯等破壞,不過如果Android惡 意軟體也通過這種方式加固保護,這將會給移動安全分析人員帶來巨大的挑戰,因為安全分析人員經常使用的程式碼靜態邏輯分析和動態除錯分析在該情況下都失效了。

梆梆官方聲稱不會對惡意軟體進行加固,的確在加固的過程中發現存在安全軟體掃描資訊和雲測試處理流程,不過這些措施只能減少而不能徹底杜絕惡意軟體通過梆梆加固保護。如何不被惡意軟體利用是梆梆需要解決的問題。


From: http://mobile.51cto.com/aprogram-400933_all.htm