在理解android的類加載后,我們可以愉快對apk來脫殼了。脫殼重要的是斷點:
斷點:在哪個位置脫殼,這里着重指的是在哪個方法
先介紹斷點,我們只要知道加殼是用哪個方法來加載dex的,hook這個方法就可以追蹤到dex了。這個方法就是我們要的斷點!
dvmDexFileOpenPartial:——int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
apk為dex文件,android在執行前會先將dex轉化為odex文件,由函數dvmContinueOptimization(/dalvik/vm/analysis/DexPrepare.cpp)執行。在dvmContinueOptimization中先調用mmap(這也是一個斷點)將原Dex文件整體映射到內存;然后調用dvmDexFileOpenPartial去嘗試生成DexFile(既然會生成DexFile,當然會調用dexFileParse函數)。以上步驟在dex轉odex時才會執行,若加固的apk本來就是odex,則此斷點失效。
dexFileParse:——DexFile* dexFileParse(const u1* data, size_t length, int flags)
odex會生成dex的內存描述DexFile(dalvik淺析三:類加載),此函數的前2個參數與dvmDexFileOpenPartial相同(自行查看源碼)。沒什么好說的了,記住在生成DexFile時調用。看雪上已經將提取dex代碼集成到libdvm中了:【原創】讓開下,讓我脫下殼。
上面提及的是在dex加載過程(優化,解析)中,我們可以下斷的函數(當然其他還有很多,不一一舉例)。但這個有缺點是需要動態調試時才能dump出dex,這需要我們過加固apk的反調試,有點繁瑣。新方法是修改libdvm代碼去自動脫殼,參考上面看雪中的做法,下面還會詳細解析。以下是dump dex的ida script command:
static main(void) { auto fp, begin, end, dexbyte; fp = fopen("C:\\dump.dex", "wb"); begin = r0; end = r0 + r1; for ( dexbyte = begin; dexbyte < end; dexbyte ++ ) fputc(Byte(dexbyte), fp); }
下面來看看dex運行過程中可以下斷點函數(native):——/dalvik/vm/native/dalvik_system_DexFile.cpp基於源碼4.4
const DalvikNativeMethod dvm_dalvik_system_DexFile[] = { 520 { "openDexFileNative", "(Ljava/lang/String;Ljava/lang/String;I)I", 521 Dalvik_dalvik_system_DexFile_openDexFileNative }, 522 { "openDexFile", "([B)I", 523 Dalvik_dalvik_system_DexFile_openDexFile_bytearray }, 524 { "closeDexFile", "(I)V", 525 Dalvik_dalvik_system_DexFile_closeDexFile }, 526 { "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;", 527 Dalvik_dalvik_system_DexFile_defineClassNative }, 528 { "getClassNameList", "(I)[Ljava/lang/String;", 529 Dalvik_dalvik_system_DexFile_getClassNameList }, 530 { "isDexOptNeeded", "(Ljava/lang/String;)Z", 531 Dalvik_dalvik_system_DexFile_isDexOptNeeded }, 532 { NULL, NULL, NULL }, 533};
上面的幾個函數在加載dex或運行dex會執行,Dalvik_dalvik_system_DexFile_openDexFile_bytearray對於加殼的來說很熟悉吧(看我加殼中提到的文章)。這幾個函數已經不能像先前一樣直接到dex的addr和len來dump了。但你可以從中得到DexFile,然后在組合出dex文件。在這里你需要了解以下結構體:
DexFile、DvmDex、DexOrJar;
/* 521 * The set of DEX files loaded by custom class loaders. 522 */ 523 HashTable* userDexFiles; //dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)
知道userDexFiles的作用,它管理着用戶加載的DexFile。
ok,有了上面的知識我們可以寫代碼來脫殼,你可以用xposed來函數,也可以修改源碼直接dump。
修改源碼脫殼:
用上面看雪上的代碼來解釋,它修改了dexFileParse源碼,在其中直接添加脫殼代碼(具體的脫殼代碼執行請看源碼)。代碼寫好了,如何編譯呢(編譯libdvm.so: makefile,mm)。當然事情到這里還沒有結束,若修改了內存的dex結構體,那我們dump出來東西也無法執行。這時候需要我先進行修復,看參考資料3、4。dannerWorking這方面還沒去研究待續。
參考資料:
1 安卓動態調試七種武器之孔雀翎 – Ida Pro