【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )
文章目錄
- 前言
- 一、DexPrepare.cpp 中 rewriteDex() 方法分析
- 二、DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脫殼點 )
- 三、DexFile.cpp 中 dexFileParse() 方法分析 ( 脫殼點 )
前言
上一篇博客 【Android 逆向】整體加固脫殼 ( DEX 優化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函數分析 ) 中 , 分析了 DexPrepare.cpp 中 dvmContinueOptimizati() 方法 , 在其中調用了 rewriteDex() 方法 , 重寫 DEX 文件 ;
本篇博客繼續分析 DexPrepare.cpp 中 rewriteDex() 方法 ;
一、DexPrepare.cpp 中 rewriteDex() 方法分析
第一個參數 u1* addr 是加載到內存中的 dex 文件的首地址 ;
第二個參數 int len 是內存中的 dex 文件的字節長度 ;
static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)dvmDexFileOpenPartial 函數是 脫殼點 函數 , 通過該函數定位脫殼點 , 然后進行脫殼操作 ;
/** 既然可以直接讀取DEX文件,那么創建一個DexFile結構* 為了它。*/if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {ALOGE("Unable to create DexFile");goto bail;}DexPrepare.cpp 中 rewriteDex() 方法源碼 :
/** 對內存映射的DEX文件執行就地重寫。* * 如果這是從短期子進程(dexopt)調用的,我們可以* 瘋狂地加載類和分配內存。當天氣好的時候* 調用以準備字節數組中提供的類,我們可能需要* 要保守一點。* * 如果“ppClassLookup”為非空,則為指向新分配的* DexClassLookup將在成功時返回。* * 如果“ppDvmDex”為非空,則將創建新分配的DvmDex結構* 成功返回。*/ static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,DexClassLookup** ppClassLookup, DvmDex** ppDvmDex) {DexClassLookup* pClassLookup = NULL;u8 prepWhen, loadWhen, verifyOptWhen;DvmDex* pDvmDex = NULL;bool result = false;const char* msgStr = "???";/* 如果索引的字節順序錯誤,請立即交換它 */if (dexSwapAndVerify(addr, len) != 0)goto bail;/** 既然可以直接讀取DEX文件,那么創建一個DexFile結構* 為了它。*/if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {ALOGE("Unable to create DexFile");goto bail;}/** 創建類查找表。這最終將被追加* 直到最后。奧德克斯。* * 我們從DexFile創建一個臨時鏈接,以便* 類加載,如下所示。*/pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);if (pClassLookup == NULL)goto bail;pDvmDex->pDexFile->pClassLookup = pClassLookup;/** 如果我們不打算驗證或優化這些類,* 加載它們沒有任何價值,所以盡早退出。*/if (!doVerify && !doOpt) {result = true;goto bail;}prepWhen = dvmGetRelativeTimeUsec();/** 加載在此DEX文件中找到的所有類。如果它們無法加載* 由于某些原因,它們不會得到驗證(這是應該的)。*/if (!loadAllClasses(pDvmDex))goto bail;loadWhen = dvmGetRelativeTimeUsec();/** 創建字節碼優化器使用的數據結構。* 我們需要在幾個類中查找方法,因此這可能會導致* 一點類加載。我們通常在VM初始化期間執行此操作,但是* 對于dexopt on core。jar操作的順序變得有點棘手,* 所以我們把它推遲到這里。*/if (!dvmCreateInlineSubsTable())goto bail;/** 驗證并優化DEX文件(命令行)中的所有類* (如果允許的話)。* * 這是最大的努力,所以dexopt真的沒有辦法* 在這一點上失敗。*/verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);verifyOptWhen = dvmGetRelativeTimeUsec();if (doVerify && doOpt)msgStr = "verify+opt";else if (doVerify)msgStr = "verify";else if (doOpt)msgStr = "opt";ALOGD("DexOpt: load %dms, %s %dms, %d bytes",(int) (loadWhen - prepWhen) / 1000,msgStr,(int) (verifyOptWhen - loadWhen) / 1000,gDvm.pBootLoaderAlloc->curOffset);result = true;bail:/** 成功后,歸還來電者要求的物品。*/if (pDvmDex != NULL) {/* break link between the two */pDvmDex->pDexFile->pClassLookup = NULL;}if (ppDvmDex == NULL || !result) {dvmDexFileFree(pDvmDex);} else {*ppDvmDex = pDvmDex;}if (ppClassLookup == NULL || !result) {free(pClassLookup);} else {*ppClassLookup = pClassLookup;}return result; }源碼路徑 : /dalvik/vm/analysis/DexPrepare.cpp
二、DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脫殼點 )
該函數中的 參數 const void* addr 是 dex 文件在內存中的起始地址 ;
在調用的 dexFileParse 函數中 , 也可以獲取到 dex 文件在內存中的首地址 ;
DvmDex.cpp 中 dvmDexFileOpenPartial() 方法源碼 :
/** 為“部分”DEX創建DexFile結構。這是一個在* 被優化的過程。優化標頭未完成* 我們沒有任何輔助數據表,所以我們必須這樣做* 初始化過程略有不同。* * 錯誤時返回非零。*/ int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex) {DvmDex* pDvmDex;DexFile* pDexFile;int parseFlags = kDexParseDefault;int result = -1;/* -- 文件不完整,尚未計算新校驗和if (gDvm.verifyDexChecksum)parseFlags |= kDexParseVerifyChecksum;*/pDexFile = dexFileParse((u1*)addr, len, parseFlags);if (pDexFile == NULL) {ALOGE("DEX parse failed");goto bail;}pDvmDex = allocateAuxStructures(pDexFile);if (pDvmDex == NULL) {dexFileFree(pDexFile);goto bail;}pDvmDex->isMappedReadOnly = false;*ppDvmDex = pDvmDex;result = 0;bail:return result; }源碼路徑 : /dalvik/vm/DvmDex.cpp
三、DexFile.cpp 中 dexFileParse() 方法分析 ( 脫殼點 )
/** 解析優化或未優化的。存儲在內存中的dex文件。這是* 在字節排序和結構對齊修復后調用。* * 成功后,返回新分配的文件。*/ DexFile* dexFileParse(const u1* data, size_t length, int flags) {DexFile* pDexFile = NULL;const DexHeader* pHeader;const u1* magic;int result = -1;if (length < sizeof(DexHeader)) {ALOGE("too short to be a valid .dex");goto bail; /* bad file format */}pDexFile = (DexFile*) malloc(sizeof(DexFile));if (pDexFile == NULL)goto bail; /* alloc failure */memset(pDexFile, 0, sizeof(DexFile));/** Peel off the optimized header.*/if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {magic = data;if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {ALOGE("bad opt version (0x%02x %02x %02x %02x)",magic[4], magic[5], magic[6], magic[7]);goto bail;}pDexFile->pOptHeader = (const DexOptHeader*) data;ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);/* parse the optimized dex file tables */if (!dexParseOptData(data, length, pDexFile))goto bail;/* ignore the opt header and appended data from here on out */data += pDexFile->pOptHeader->dexOffset;length -= pDexFile->pOptHeader->dexOffset;if (pDexFile->pOptHeader->dexLength > length) {ALOGE("File truncated? stored len=%d, rem len=%d",pDexFile->pOptHeader->dexLength, (int) length);goto bail;}length = pDexFile->pOptHeader->dexLength;}dexFileSetupBasicPointers(pDexFile, data);pHeader = pDexFile->pHeader;if (!dexHasValidMagic(pHeader)) {goto bail;}/** 驗證校驗和。這相當快,但確實需要* 觸摸DEX文件中的每個字節。基本校驗和在* 字節交換和索引優化。*/if (flags & kDexParseVerifyChecksum) {u4 adler = dexComputeChecksum(pHeader);if (adler != pHeader->checksum) {ALOGE("ERROR: bad checksum (%08x vs %08x)",adler, pHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ adler32 checksum (%08x) verified", adler);}const DexOptHeader* pOptHeader = pDexFile->pOptHeader;if (pOptHeader != NULL) {adler = dexComputeOptChecksum(pOptHeader);if (adler != pOptHeader->checksum) {ALOGE("ERROR: bad opt checksum (%08x vs %08x)",adler, pOptHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ adler32 opt checksum (%08x) verified", adler);}}}/** 驗證SHA-1摘要。(通常我們不想這樣做--* 摘要用于唯一標識原始DEX文件,以及* 無法在索引被字節交換后計算以進行驗證* (并進行了優化。)*/if (kVerifySignature) {unsigned char sha1Digest[kSHA1DigestLen];const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +kSHA1DigestLen;dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {char tmpBuf1[kSHA1DigestOutputLen];char tmpBuf2[kSHA1DigestOutputLen];ALOGE("ERROR: bad SHA1 digest (%s vs %s)",dexSHA1DigestToStr(sha1Digest, tmpBuf1),dexSHA1DigestToStr(pHeader->signature, tmpBuf2));if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ sha1 digest verified");}}if (pHeader->fileSize != length) {ALOGE("ERROR: stored file size (%d) != expected (%d)",(int) pHeader->fileSize, (int) length);if (!(flags & kDexParseContinueOnError))goto bail;}if (pHeader->classDefsSize == 0) {ALOGE("ERROR: DEX file has no classes in it, failing");goto bail;}/** Success!*/result = 0;bail:if (result != 0 && pDexFile != NULL) {dexFileFree(pDexFile);pDexFile = NULL;}return pDexFile; }
源碼路徑 : /dalvik/libdex/DexFile.cpp
總結
以上是生活随笔為你收集整理的【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 逆向】整体加固脱壳 (
- 下一篇: 【Android 逆向】整体加固脱壳 (