文章目录
前言
一、DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析
前言
上一篇博客 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 ) 中 , DexPrepare.cpp 中的 dvmOptimizeDexFile() 方法是用于优化 dex 文件的 , 其中调用了 /bin/dexopt 可执行程序优化 dex 文件 ; 在 /dalvik/dexopt/OptMain.cpp 源码中的 main 函数的 dex 优化分支中 , 调用了 fromDex() 函数 , 在该函数中 , 又调用了 DexPrepare.cpp 中的 dvmContinueOptimizati() 方法 , 执行真正的 dex 优化操作 ;
一、DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析
先判断 DEX 文件是否合法 , 如果文件的长度比 DEX 文件头长度还小 , 这个 DEX 文件肯定不合法 , 直接返回 ;
/* 快速测试,这样我们就不会在空文件上报错 */ if (dexLength < (int) sizeof(DexHeader)) { ALOGE("too small to be DEX"); return false; }
调用 mmap() 函数对当前 dex 文件内容进行映射 ;
mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
调用 rewriteDex() 方法 , 重写 dex 文件 , 其中 第一个参数 ((u1*) mapAddr) + dexOffset 是映射到内存中的起始地址 , 第二个参数 dexLength 是 dex 文件的长度 ;
/* * 重写文件。字节重新排序,结构重新排列, * 类验证和字节码优化都被执行 * 在这里。 * * 从理论上讲,文件可能会改变大小,位可能会四处移动。 * 在实践中,这将是烦人的处理,所以文件 * 布局的设计使其始终可以就地重写。 * * 这将创建类查找表作为处理的一部分。 * * 第一个参数 ((u1*) mapAddr) + dexOffset 是映射到 * 内存中的起始地址 * * 第二个参数 dexLength 是 dex 文件的长度 */ success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, doVerify, doOpt, &pClassLookup, NULL);
DexPrepare.cpp 中 dvmContinueOptimizati() 方法源码 :
/* * 进行实际的优化。这是在dexopt进程中执行的。 * * 为了更好地利用磁盘/内存,我们希望提取一次并执行 * 优化到位。如果文件必须展开或收缩 * 为了匹配本地结构填充/对齐预期,我们需要 * 将重写作为提取的一部分,而不是提取 * 放入临时文件并将其恢复。(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; /* * 把这个放到一个全球数据库里,这样我们就不必到处传递了。我们可以 * 还向DexFile添加一个字段,但因为它只属于DEX * 可能没有意义的创造。 */ gDvm.optimizingBootstrapClass = isBootstrap; { /* * 映射整个文件(这样我们就不必担心页面 * 对齐)。期望输出文件包含 * 我们的DEX数据加上一个小标题的空间。 */ bool success; void* mapAddr; /* 调用 mmap 函数对当前 dex 文件内容进行映射 */ 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; } /* * 重写文件。字节重新排序,结构重新排列, * 类验证和字节码优化都被执行 * 在这里。 * * 从理论上讲,文件可能会改变大小,位可能会四处移动。 * 在实践中,这将是烦人的处理,所以文件 * 布局的设计使其始终可以就地重写。 * * 这将创建类查找表作为处理的一部分。 * * 第一个参数 ((u1*) mapAddr) + dexOffset 是映射到 * 内存中的起始地址 * * 第二个参数 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 { /* * 如果配置为这样做,则生成寄存器映射输出 * 对于所有已验证的类。登记册地图是 * 在验证期间生成,现在将序列化。 */ 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失败,因为我们已经加载了类 * 这一点很重要。对于优化器来说,这不是问题, * 因为简单地退出流程更有效。 * 为valgrind执行清洁关机时排除此代码。 */ if (munmap(mapAddr, dexOffset + dexLength) != 0) { ALOGE("munmap failed: %s", strerror(errno)); goto bail; } #endif if (!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); } /* * 附加任何优化的预计算数据结构。 */ 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”标题,并填写所有值和正确的 * 神奇的数字。 */ 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_ENDIAN optHdr.flags = DEX_OPT_FLAG_BIG; #else optHdr.flags = 0; #endif optHdr.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; }