【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )

简介: 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )

文章目录

前言

一、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;
}


目录
相关文章
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
1595 4
|
7月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1007 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
8月前
|
存储 消息中间件 人工智能
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
571 10
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
4742 77
|
缓存 编解码 Android开发
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
464 1
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
1064 21
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
287 8
|
缓存 Android开发 数据格式
Android ListView性能优化,异步加载图片
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/48184383 ListView性能优...
1302 0
|
缓存 算法 Android开发
Android 性能优化——之图片的优化
Android 性能优化——之图片的优化  在Android性能优化中,我们会发现占内存最大的和对性能影响最大的往往是图片资源,其次是控件资源。相对来说,其他的资源的影响会小一点。这里我就先对图片资源的优化进行一下讲解,如果有什么说的不对的,希望大神指正一下。
1285 0

热门文章

最新文章