文章目录

  • 前言
  • 一、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;}
#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);}/** 附加任何优化的预计算数据结构。*/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_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() 函数分析 )相关推荐

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

    文章目录 前言 一.DexPrepare.cpp 中 rewriteDex() 方法分析 二.DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 ) 三.D ...

  2. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 二./bin/dexopt 源码分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ...

  3. 【Android 逆向】整体加固脱壳 ( 脱壳点简介 | 修改系统源码进行脱壳 )

    文章目录 一.脱壳点简介 二.修改系统源码进行脱壳 一.脱壳点简介 在上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 rewriteD ...

  4. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )

    文章目录 前言 一.RawDexFile.cpp 中 dvmRawDexFileOpen() 方法分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DexClassLoader 加载 ...

  5. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | 查找 DexFile 对应的C代码 | dalvik_system_DexFile.cpp 分析 )

    文章目录 前言 一.查找 DexFile 对应的 C++ 代码 1.根据 Native 文件命名惯例查找 C++ 代码 2.根据方法名查找 二.dalvik_system_DexFile.cpp 源码 ...

  6. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexFile loadDexFile 函数 | 构造函数 | openDexFile 函数 )

    文章目录 前言 一.DexFile.loadDexFile 函数分析 二.DexFile 构造函数分析 三.DexFile.openDexFile 函数分析 前言 上一篇博客 [Android 逆向] ...

  7. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 中根据 File 加载 DexFile | loadDexFile 分析 )

    文章目录 前言 一.根据 File 加载 DexFile 二.DexPathList.loadDexFile 函数分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DexClassLo ...

  8. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )

    文章目录 前言 一.DexPathList 构造函数分析 二.DexPathList.makeDexElements 函数分析 三.Element 类分析 前言 上一篇博客 [Android 逆向]整 ...

  9. Android逆向与安全——360 dex加固与脱壳

    前言 现在市面上对APP的安全合规管控越来越严格了,也就要求了APP在上架之前一定要做合规检测和加固处理.对APP就是加固的好处,可以提高APP的安全性,提高APP被逆向分析破解的门槛,同时通过加固保 ...

最新文章

  1. php 自定义表格并统计,PHP 使用Echarts生成数据统计报表的实现
  2. java ear war_[转] 基于Java的打包jar、war、ear包的作用与区别详解
  3. 抽屉开关_技术天地|380伏抽屉开关指示灯更换流程
  4. Java 8可选:如何使用它
  5. Ubuntu各大分支版本功能介绍及下载地址
  6. html 后# 号的作用,及html 如何传参
  7. Sharepoint学习笔记—ECM系列--3 从.CSV文件导入术语集(Term Sets)
  8. 中山大学计算机是A类学科吗,中山大学a类学科有哪些?附中大a类学科名单
  9. android 重新启动应用程序,在AsyncTask完成后重新启动完整的Android应用程序
  10. python--(pickle)文件读写
  11. python怎么用turtle画圆_在Python中用turtle函数画同心圆
  12. Solr中的数据导入
  13. 转:随机过程好书推荐
  14. Python转exe神器pyinstaller
  15. Vue中点击复制文本功能
  16. 计算机删除登录用户,Windows10账户删除怎么登陆 如何删除账户
  17. dll 不是 PML.NET callable问题解决办法
  18. 从零配置专属neovim - 1.配置设计概述
  19. 实现n*n乘法口诀表
  20. 微信小程序实现短信认证功能

热门文章

  1. 距离QCon纽约还有3个礼拜:新的演讲、播客节目和研讨会
  2. [Jmeter系列]Jmeter源码编译步骤(转)
  3. Android:EditText 属性
  4. Windows 10 安装
  5. 简单的Tab切换组件
  6. ubuntu下磁道坏区的检测与修复
  7. 2009-08 台湾印象
  8. 微软发布 Mobile Express for Microsoft Dynamics CRM 4.0
  9. 为什么JS是单线程?JS中的Event Loop(事件循环)?JS如何实现异步?setimeout?
  10. 解题报告 『生活大爆炸版石头剪刀布(模拟)』