【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )
文章目錄
- 前言
- 一、DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析
前言
上一篇博客 【Android 逆向】整體加固脫殼 ( DEX 優(yōu)化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源碼分析 ) 中 , DexPrepare.cpp 中的 dvmOptimizeDexFile() 方法是用于優(yōu)化 dex 文件的 , 其中調用了 /bin/dexopt 可執(zhí)行程序優(yōu)化 dex 文件 ; 在 /dalvik/dexopt/OptMain.cpp 源碼中的 main 函數(shù)的 dex 優(yōu)化分支中 , 調用了 fromDex() 函數(shù) , 在該函數(shù)中 , 又調用了 DexPrepare.cpp 中的 dvmContinueOptimizati() 方法 , 執(zhí)行真正的 dex 優(yōu)化操作 ;
一、DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析
先判斷 DEX 文件是否合法 , 如果文件的長度比 DEX 文件頭長度還小 , 這個 DEX 文件肯定不合法 , 直接返回 ;
/* 快速測試,這樣我們就不會在空文件上報錯 */if (dexLength < (int) sizeof(DexHeader)) {ALOGE("too small to be DEX");return false;}調用 mmap() 函數(shù)對當前 dex 文件內(nèi)容進行映射 ;
mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);調用 rewriteDex() 方法 , 重寫 dex 文件 , 其中 第一個參數(shù) ((u1*) mapAddr) + dexOffset 是映射到內(nèi)存中的起始地址 , 第二個參數(shù) dexLength 是 dex 文件的長度 ;
/** 重寫文件。字節(jié)重新排序,結構重新排列,* 類驗證和字節(jié)碼優(yōu)化都被執(zhí)行* 在這里。* * 從理論上講,文件可能會改變大小,位可能會四處移動。* 在實踐中,這將是煩人的處理,所以文件* 布局的設計使其始終可以就地重寫。* * 這將創(chuàng)建類查找表作為處理的一部分。* * 第一個參數(shù) ((u1*) mapAddr) + dexOffset 是映射到* 內(nèi)存中的起始地址* * 第二個參數(shù) dexLength 是 dex 文件的長度*/success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,doVerify, doOpt, &pClassLookup, NULL);DexPrepare.cpp 中 dvmContinueOptimizati() 方法源碼 :
/** 進行實際的優(yōu)化。這是在dexopt進程中執(zhí)行的。* * 為了更好地利用磁盤/內(nèi)存,我們希望提取一次并執(zhí)行* 優(yōu)化到位。如果文件必須展開或收縮* 為了匹配本地結構填充/對齊預期,我們需要* 將重寫作為提取的一部分,而不是提取* 放入臨時文件并將其恢復。(b)結構調整* 當前對所有平臺都是正確的,但這并不是預期的* 更改,因此我們應該可以將其提取出來。)* * 成功時返回“true”。*/ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) {DexClassLookup* pClassLookup = NULL;RegisterMapBuilder* pRegMapBuilder = NULL;assert(gDvm.optimizing);ALOGV("Continuing optimization (%s, isb=%d)", fileName, isBootstrap);assert(dexOffset >= 0);/* 快速測試,這樣我們就不會在空文件上報錯 , 先判斷 DEX 文件是否合法 , 如果文件的長度比 DEX 文件頭長度還小 , 這個 DEX 文件肯定不合法 , 直接返回 ; */if (dexLength < (int) sizeof(DexHeader)) {ALOGE("too small to be DEX");return false;}if (dexOffset < (int) sizeof(DexOptHeader)) {ALOGE("not enough room for opt header");return false;}bool result = false;/** 把這個放到一個全球數(shù)據(jù)庫里,這樣我們就不必到處傳遞了。我們可以* 還向DexFile添加一個字段,但因為它只屬于DEX* 可能沒有意義的創(chuàng)造。*/gDvm.optimizingBootstrapClass = isBootstrap;{/** 映射整個文件(這樣我們就不必擔心頁面* 對齊)。期望輸出文件包含* 我們的DEX數(shù)據(jù)加上一個小標題的空間。*/bool success;void* mapAddr;/* 調用 mmap 函數(shù)對當前 dex 文件內(nèi)容進行映射 */mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);if (mapAddr == MAP_FAILED) {ALOGE("unable to mmap DEX cache: %s", strerror(errno));goto bail;}bool doVerify, doOpt;if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {doVerify = false;} else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {doVerify = !gDvm.optimizingBootstrapClass;} else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {doVerify = true;}if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {doOpt = false;} else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {doOpt = doVerify;} else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {doOpt = true;}/** 重寫文件。字節(jié)重新排序,結構重新排列,* 類驗證和字節(jié)碼優(yōu)化都被執(zhí)行* 在這里。* * 從理論上講,文件可能會改變大小,位可能會四處移動。* 在實踐中,這將是煩人的處理,所以文件* 布局的設計使其始終可以就地重寫。* * 這將創(chuàng)建類查找表作為處理的一部分。* * 第一個參數(shù) ((u1*) mapAddr) + dexOffset 是映射到* 內(nèi)存中的起始地址* * 第二個參數(shù) dexLength 是 dex 文件的長度*/success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,doVerify, doOpt, &pClassLookup, NULL);if (success) {DvmDex* pDvmDex = NULL;u1* dexAddr = ((u1*) mapAddr) + dexOffset;if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {ALOGE("Unable to create DexFile");success = false;} else {/** 如果配置為這樣做,則生成寄存器映射輸出* 對于所有已驗證的類。登記冊地圖是* 在驗證期間生成,現(xiàn)在將序列化。*/if (gDvm.generateRegisterMaps) {pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);if (pRegMapBuilder == NULL) {ALOGE("Failed generating register maps");success = false;}}DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;updateChecksum(dexAddr, dexLength, pHeader);dvmDexFileFree(pDvmDex);}}/* 取消映射讀寫版本,強制寫入磁盤 */if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {ALOGW("msync failed: %s", strerror(errno));// weird, but keep going} #if 1/** 這會導致clean shutdown失敗,因為我們已經(jīng)加載了類* 這一點很重要。對于優(yōu)化器來說,這不是問題,* 因為簡單地退出流程更有效。* 為valgrind執(zhí)行清潔關機時排除此代碼。*/if (munmap(mapAddr, dexOffset + dexLength) != 0) {ALOGE("munmap failed: %s", strerror(errno));goto bail;} #endifif (!success)goto bail;}/* 獲取起始偏移量,并調整deps start以進行64位對齊 */off_t depsOffset, optOffset, endOffset, adjOffset;int depsLength, optLength;u4 optChecksum;depsOffset = lseek(fd, 0, SEEK_END);if (depsOffset < 0) {ALOGE("lseek to EOF failed: %s", strerror(errno));goto bail;}adjOffset = (depsOffset + 7) & ~(0x07);if (adjOffset != depsOffset) {ALOGV("Adjusting deps start from %d to %d",(int) depsOffset, (int) adjOffset);depsOffset = adjOffset;lseek(fd, depsOffset, SEEK_SET);}/** 附加依賴項列表。*/if (writeDependencies(fd, modWhen, crc) != 0) {ALOGW("Failed writing dependencies");goto bail;}/* 計算deps長度,然后調整64位對齊的opt start */optOffset = lseek(fd, 0, SEEK_END);depsLength = optOffset - depsOffset;adjOffset = (optOffset + 7) & ~(0x07);if (adjOffset != optOffset) {ALOGV("Adjusting opt start from %d to %d",(int) optOffset, (int) adjOffset);optOffset = adjOffset;lseek(fd, optOffset, SEEK_SET);}/** 附加任何優(yōu)化的預計算數(shù)據(jù)結構。*/if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {ALOGW("Failed writing opt data");goto bail;}endOffset = lseek(fd, 0, SEEK_END);optLength = endOffset - optOffset;/* compute checksum from start of deps to end of opt area */if (!computeFileChecksum(fd, depsOffset,(optOffset+optLength) - depsOffset, &optChecksum)){goto bail;}/** 輸出“opt”標題,并填寫所有值和正確的* 神奇的數(shù)字。*/DexOptHeader optHdr;memset(&optHdr, 0xff, sizeof(optHdr));memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);optHdr.dexOffset = (u4) dexOffset;optHdr.dexLength = (u4) dexLength;optHdr.depsOffset = (u4) depsOffset;optHdr.depsLength = (u4) depsLength;optHdr.optOffset = (u4) optOffset;optHdr.optLength = (u4) optLength; #if __BYTE_ORDER != __LITTLE_ENDIANoptHdr.flags = DEX_OPT_FLAG_BIG; #elseoptHdr.flags = 0; #endifoptHdr.checksum = optChecksum;fsync(fd); /* ensure previous writes go before header is written */lseek(fd, 0, SEEK_SET);if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)goto bail;ALOGV("Successfully wrote DEX header");result = true;//dvmRegisterMapDumpStats();bail:dvmFreeRegisterMapBuilder(pRegMapBuilder);free(pClassLookup);return result; }源碼路徑 : /dalvik/vm/analysis/DexPrepare.cpp
總結
以上是生活随笔為你收集整理的【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 逆向】整体加固脱壳 (
- 下一篇: 【Android 逆向】整体加固脱壳 (