【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;
}


目录
相关文章
|
1月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
21 1
|
2月前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
36 4
|
3月前
|
监控 Android开发 C语言
深度解读Android崩溃日志案例分析2:tombstone日志
深度解读Android崩溃日志案例分析2:tombstone日志
85 0
|
3天前
|
存储 Java Android开发
Android系统 设置第三方应用为默认Launcher实现和原理分析
Android系统 设置第三方应用为默认Launcher实现和原理分析
17 0
|
3天前
|
存储 Java Linux
Android系统获取event事件回调等几种实现和原理分析
Android系统获取event事件回调等几种实现和原理分析
24 0
|
2月前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
31 4
|
2月前
|
安全 搜索推荐 Android开发
Android 与 iOS 的比较分析
【2月更文挑战第5天】 Android 和 iOS 是目前市场上两种最流行的移动操作系统,它们都拥有自己的特点和优势。本文将会分别从操作系统设计、应用生态、安全性等方面对这两种操作系统进行比较和分析,希望能够帮助读者更好地选择适合自己的移动设备。
|
2月前
|
Android开发
安卓逆向 -- Hook多个dex文件
安卓逆向 -- Hook多个dex文件
19 1
|
安全 Android开发
【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )
【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )
239 0
|
安全 Android开发
【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )
【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )
142 0