欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )

发布时间:2025/6/17 50 豆豆
生活随笔 收集整理的这篇文章主要介绍了 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 ) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

  • 前言
  • 一、DexPrepare.cpp 中 rewriteDex() 方法分析
  • 二、DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 )
  • 三、DexFile.cpp 中 dexFileParse() 方法分析 ( 脱壳点 )

前言


上一篇博客 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 ) 中 , 分析了 DexPrepare.cppdvmContinueOptimizati() 方法 , 在其中调用了 rewriteDex() 方法 , 重写 DEX 文件 ;

本篇博客继续分析 DexPrepare.cpprewriteDex() 方法 ;





一、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 文件在内存中的首地址 )的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。