odex是OptimizedDEX的缩写,是优化过的dex文件

odex两种存在方式:

1. 从apk程序中提取,和apk文件放在一起,后缀 odex,此类文件多是AndroidRom系统文件

2. 在cache/dalvik-cache缓存文件,后缀 dex

a)      Eg:system@app@calcuator.apk@classes.dex 安装在/system/app目录下calcuator.apk程序的odex文件

odex作用:

因为Dalvik每次加载从apk中读取classes.dex文件会消耗cpu时间,odex则已经包含了需要加载的库文件列表,Dalvik虚拟机加载时根据需要加载的库对照dex文件即可。

部分Android系统的ROM将系统odex文件与app放在同一目录,系统在启动加载这些程序会更省时间。

一、生成odex文件:

使用Android系统源码工具生成dex

将build/tools/dexpreopt/dexopt-wrapper下的dexopt-wrapper拷入真机adb push命令

给予777权限,将需要生成dex文件拷入到手机cd跳转该目录执行如下命令

adb pull将文件拷出得到odex文件,文件位置默认为此时cmd的路径位置

分析odex文件:

文件结构体

Dalvik虚拟机将dex文件映射到内存后

500struct DexFile {
501    /* directly-mapped "opt" header */
502    const DexOptHeader* pOptHeader;          // odex文件头
503
504    /* pointers to directly-mapped structs and arrays in base DEX */
505    const DexHeader*    pHeader;                        
506    const DexStringIdpStringIds;
507    const DexTypeId*    pTypeIds;
508    const DexFieldId*   pFieldIds;
509    const DexMethodIdpMethodIds;
510    const DexProtoId*   pProtoIds;
511    const DexClassDefpClassDefs;
512    const DexLink*      pLinkData;
513
514    /*
515     * 辅助数据段,记录文件被优化后添加的一些信息
517     */
518    const DexClassLookup* pClassLookup;
519    const void*         pRegisterMapPool;       // RegisterMapClassPool
520
521    /* points to start of DEX file data */
522    const u1*           baseAddr;
523
524    /* track memory overhead for auxillary structures */
525    int                 overhead;
526
527    /* additional app-specific data structures associated with the DEX */
528    //void*               auxData;
529};

DexFile结构中存入其他结构的指针,描述的是加载到内存的数据结构,还有些数据是不会加载到内存的

odex文件结构

struct ODEXFile{

DexOptHeader  header;// odex文件头

DEXFile dexfile;// dex文件

Dependence  deps;//依赖库列表

ChunkDexClassLoopup       lookup;// 类查询结构

ChunkRegisterMapPool  mappool;// 映射池

ChunkEnd     end;// 结束标志

}

二、odex文件解析
       DexOptHeader在DexFile.h文件中466/*

467 * Header added by DEX optimization pass.  Values are always written in
468 * local byte and structure padding.  The first field (magic + version)
469 * is guaranteed to be present and directly readable for all expected
470 * compiler configurations; the rest is version-dependent.
471 *
472 * Try to keep this simple and fixed-size.
473 */
474struct DexOptHeader {

475    u1  magic[8];           /* odex版本标示 目前固定“64 65 79 0A 30 33 36 00” dey 036 */

476
477    u4  dexOffset;          /* dex文件头偏移 目前固定为“28 00 00 00”*/
478    u4  dexLength;          /* dex文件总长度 */
479    u4  depsOffset;         /* odex依赖库列表偏移 */
480    u4  depsLength;         /* 依赖库列表总长度 */
481    u4  optOffset;          /* 辅助数据偏移 */
482    u4  optLength;         /* 辅助数据总长度 */
483
484    u4  flags;              /* 标志,标识了Dalvik虚拟机加载odex时的优化与验证选项 */
485    u4  checksum;           /* 依赖库与辅助数据的校验和*/
486
487    /* pad for 64-bit alignment if necessary */
488};
        DexOptheader结构以下为DEXFile。
        DEXFile下为Dependences结构,Dependences结构不会加载到内存,并且Android源码没有明确定义。
整理出来的结构
struct DexOptHeader{
        u4 modWhen;            // 时间戳
        u4 crc;        // 校验
        u4 DALVIK_VM_BUILD;            // Dalvik虚拟机版本号
        u4 numDeps;            // 依赖库的个数
        struct{ 
               u4 len;        // name字符串长度
               u1 name[len];  // 依赖库的名称,依赖库的完整路径
               kSHA1DigestLen signature;      // SHA-1 哈希值
        }table[numDeps];       // numDeps决定了table连续的个数
};
        Dependences结构的具体操作函数位置 dalvik\vm\analysis\DexPrepare.cpp 中的writeDependencies()1358/*
1359 * Write the dependency info to "fd" at the current file position.
1360 */
1361static int writeDependencies(int fd, u4 modWhen, u4 crc)
1362{
1363    u1* buf = NULL;
1364    int result = -1;
1365    ssize_t bufLen;
1366    ClassPathEntry* cpe;
1367    int numDeps;
1368
1369    /*
1370     * Count up the number of completed entries in the bootclasspath.
1371     */
1372    numDeps = 0;
1373    bufLen = 0;
1374    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1375        const char* cacheFileName =
1376            dvmPathToAbsolutePortion(getCacheFileName(cpe));
1377        assert(cacheFileName != NULL); /* guaranteed by Class.c */
1378
1379        ALOGV("+++ DexOpt: found dep '%s'", cacheFileName);
1380
1381        numDeps++;
1382        bufLen += strlen(cacheFileName) +1;
1383    }
1384
1385    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
1386
1387    buf = (u1*)malloc(bufLen);
1388
1389    set4LE(buf+0, modWhen);              // 写入时间戳                  注意:modWhenhe和crc通过
1390    set4LE(buf+4, crc);                  // 写入crc校验         dexZipGetEntryInfo()获取的     
1391    set4LE(buf+8, DALVIK_VM_BUILD);      // 写入Dalvik虚拟机版本号
1392    set4LE(buf+12, numDeps);             // 写入依赖库的个数
1393
1394    // TODO: do we want to add dvmGetInlineOpsTableLength() here?  Won't
1395    // help us if somebody replaces an existing entry, but it'd catch
1396    // additions/removals.
1397
1398    u1* ptr = buf + 4*4;                 // 跳过前四个字段
1399    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {           // 循环写入依赖库
1400        const char* cacheFileName =
1401            dvmPathToAbsolutePortion(getCacheFileName(cpe));
1402        assert(cacheFileName != NULL); /* guaranteed by Class.c */
1403
1404        const u1* signature = getSignature(cpe);      // 计算SHA-1 哈希值
1405        int len = strlen(cacheFileName) +1;
1406
1407        if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
1408            ALOGE("DexOpt: overran buffer");
1409            dvmAbort();
1410        }
1411
1412        set4LE(ptr, len);
1413        ptr += 4;
1414        memcpy(ptr, cacheFileName, len);         // 写入依赖库的名字
1415        ptr += len;
1416        memcpy(ptr, signature, kSHA1DigestLen);          // 写入SHA-1哈希值
1417        ptr += kSHA1DigestLen;
1418    }
1419
1420    assert(ptr == buf + bufLen);
1421
1422    result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
1423
1424    free(buf);
1425    return result;
1426}
    dexZipGetEntryInfo()函数位于 /dalvik/libdex/ZipArchive.cpp       根据结构体分析二进制即可

Dalvik版本号:Android2.2.3            19

Android2.3~2.3.7 23

Android4.0~4.1          27

    Dependences结构下有3个Chunk块。由/dalvik/vm/analysis/DexPrepare.cpp中的writeOptData()写入1474
1475 * Write opt data.
1476 *
1477 * We have different pieces, some of which may be optional.  To make the
1478 * most effective use of space, we use a "chunk" format, with a 4-byte
1479 * type and a 4-byte length.  We guarantee 64-bit alignment for the data,
1480 * so it can be used directly when the file is mapped for reading.
1481 */
1482static bool writeOptData(int fd, const DexClassLookup* pClassLookup,
1483    const RegisterMapBuilder* pRegMapBuilder)
1484{
1485    /* pre-computed class lookup hash table */
1486    if (!writeChunk(fd, (u4) kDexChunkClassLookup,
1487            pClassLookup, pClassLookup->size))
1488    {
1489        return false;
1490    }
1491
1492    /* register maps (optional) */
1493    if (pRegMapBuilder != NULL) {
1494        if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
1495                pRegMapBuilder->data, pRegMapBuilder->size))
1496        {
1497            return false;
1498        }
1499    }
1500
1501    /* write the end marker */
1502    if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
1503        return false;
1504    }
1505
1506    return true;
1507}
        数据是通过writeChunk()写入的,writeChunk()源码1429/*
1430 * Write a block of data in "chunk" format.
1431 *
1432 * header结构体占8字节,type字段为1一个kDexChunk开头的常量
1433 * 
1434 */
1435static bool writeChunk(int fd, u4 type, const void* data, size_t size)
1436{
1437    union {             /* save a syscall by grouping these together */
1438        char raw[8];
1439        struct {
1440            u4 type;
1441            u4 size;
1442        } ts;
1443    } header;
1444
1445    assert(sizeof(header) == 8);
1446
1447    ALOGV("Writing chunk, type=%.4s size=%d", (char*) &type, size);
1448
1449    header.ts.type = type;
1450    header.ts.size = (u4) size;
1451    if (sysWriteFully(fd, &header, sizeof(header),
1452            "DexOpt opt chunk header write") != 0)
1453    {
1454        return false;
1455    }
1456
1457    if (size > 0) {
1458        if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
1459            return false;
1460    }
1461
1462    /* if necessary, pad to 64-bit alignment */
1463    if ((size & 7) != 0) {
1464        int padSize = 8 - (size & 7);
1465        ALOGV("size was %d, inserting %d pad bytes", size, padSize);
1466        lseek(fd, padSize, SEEK_CUR);
1467    }
1468
1469    assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
1470
1471    return true;
1472}
 
    writeChunk()方法中传入的type字段
188/* auxillary data section chunk codes */
189enum {
190    kDexChunkClassLookup            = 0x434c4b50,   /* CLKP */
191    kDexChunkRegisterMaps           = 0x524d4150,   /* RMAP */
192
193    kDexChunkEnd                    = 0x41454e44,   /* AEND */
194};
    
    writeOptData ()方法中传入DexClassLookup结构指针,Dalvik虚拟机通过DexClassLookup结构检索dex文件中的类
447/*
448 * Lookup table for classes.  It provides a mapping from class name to
449 * class definition.  Used by dexFindClass().
450 *
451 * We calculate this at DEX optimization time and embed it in the file so we
452 * don't need the same hash table in every VM.  This is slightly slower than
453 * a hash table with direct pointers to the items, but because it's shared
454 * there's less of a penalty for using a fairly sparse table.
455 */
456struct DexClassLookup {
457    int     size;                       // 本结构的字节数
458    int     numEntries;                 // 接下来table结构的项数,通常值为2
459    struct {
460        u4      classDescriptorHash;    // 类的哈希值
461        int     classDescriptorOffset;  // 类的描述
462        int     classDefOffset;         // 指向DexClassDef结构的指针
463    } table[1];// 用来描述类的信息
464};
465

根据上述源码总结出的ChunkDexClassLookup结构声明:

struct ChunkDexClassLookup{

Header header;

DexClassLookup lookup;

}

ChunkRegisterMapPool的结构体是writeOptData()函数向writeChunk()函数传递1个RegisterMapBuilder结构体指针。

RegisterMapBuilder结构体通过dvmGenerateRegisterMaps()函数填充。

dvmGenerateRegisterMaps()调用writeMapsAllClasses()填充所有类的映射信息,

writeMapsAllClasses()调用writeMapsAllMethods()填充所有方法映射信息

writeMapsAllMethods()调用writeMapForMethod()依次填充每个方法的映射信息

并调用computeRegisterMapSize()函数计算填充的每个方法映射信息的长度,用来循环遍历所有的方法

struct ChunkRegisterMapPool{

Header header;

struct{

struct RegisterMapClassPool{

u4 numClasses;

u4 classDataOffset[1];

}classpool;

struct RegisterMapMethodPool{

u2 methodCount;

u4 methodData[1];

};

}lookup;

};

写ChunkEnd结构时,writeOptData()向writeChunk()传递了一个null指针,根据传递的kDexChunkEnd类型来判断。

odex文件最后的8个字节固定为“44 4E 45 41 00 00 00 00”

struct ChunkEnd{

Header header;

}

5.2 odex文件的更多相关文章

  1. Android odex文件反编译

    odex 是经过优化的dex文件,且独立存在于apk文件.odex 多用于系统预制应用或服务.通过将apk中的dex文件进行 odex,可以加载 apk 的启动速度,同时减小空间的占用.请参考ODEX ...

  2. android反编译odex文件

    关于android的反编译工具,相信大家并不陌生 如APK-TOOL,dex2jar APK-TOOL 用于反编译出布局文件 下载地址http://code.google.com/p/android- ...

  3. DEX、ODEX、OAT文件&Dalvik和ART虚拟机

    https://www.jianshu.com/p/389911e2cdfb https://www.jianshu.com/p/a468e714aca7 ODEX是安卓上的应用程序apk中提取出来的 ...

  4. dex和odex相互转换

    一.dex和odex dex是安卓dalvik虚拟机的可执行文件,可以在导出的apk文件里用解压缩软件直接打开.odex是经过优化过的dex.odex一种是从apk程序中提取出来的,与apk文件存放在 ...

  5. 浅析dex文件加载机制

    我们可以利用DexClassLoader来实现动态加载dex文件,而很多资料也只是对于DexClassLoader的使用进行了介绍,没有深入讲解dex的动态加载机制,我们就借助于Android4.4的 ...

  6. Android开发了解——ODEX

    ODEX是安卓上的应用程序apk中提取出来的可运行文件,即将APK中的classes.dex文件通过dex优化过程将其优化生成一个·dex文件单独存放,原APK中的classes.dex文件会保留.这 ...

  7. android 反编译(dex 和 odex),非脑残转帖,绝对可靠

    Android 反编译 反编译odex文件(比如framework.odex),若是反编译dex,直接第4步 1.因为反编译odex的工具在D:\Develop tools\android反编译工具\ ...

  8. [转]Android应用安装包apk文件的反编译与重编译、重签名

    背景介绍: 最近在做Robotium自动化测试,使用到solo.takeScreenshot()函数以在测试过程中截图,但此函数需要被测试APP具有<uses-permission androi ...

  9. [教程] 【【【【odex教程之jar】】】】/system/framework里面的jar做odex g13

    dexopt-wrapper core.jar core.odex dexopt-wrapper ext.jar ext.odex dexopt-wrapper framework.jar frame ...

随机推荐

  1. matlab-层次分析法

    层次分析法(AHP)是把问题条理化.层次化,构造出一个有层次的结构模型的方法. 比如要选择旅游地,有3个选择方案,苏杭.北戴河和桂林.选择过程需要考虑多个因素,比如景色.费用.居住.饮食和旅途. 1. ...

  2. Hibernate 关联关系(一对多)

    Hibernate 关联关系(一对多) 1. 什么是关联(association) 1.1 关联指的是类之间的引用关系.如果类A与类B关联,那么被引用的类B将被定义为类A的属性.例如: class B ...

  3. ESA2GJK1DH1K基础篇: 测试APP扫描Air202上面的二维码绑定通过MQTT控制设备(兼容SIM800)

    前言 此程序兼容SIM800 如果想绑定SIM800,请把其IMEI号,生成二维码,用手机APP扫描. 实现功能概要 APP通过扫描二维码获取GPRS设备的IMEI号,然后设置订阅的主题:device ...

  4. Kali系统改国内源配置和SSH配置

    一.Kali系统更新源 使用官网的虚拟化镜像安装,默认为英文界面,更新源也是官方源.因为官方服务器在国外,速度不是很理想,现在就来改国内源并且更新系统. 1.使用编辑器打开系统源文本(在终端内操作,先 ...

  5. [TJOI2009]猜数字(洛谷 3868)

    题目描述 现有两组数字,每组k个,第一组中的数字分别为:a1,a2,...,ak表示,第二组中的数字分别用b1,b2,...,bk表示.其中第二组中的数字是两两互素的.求最小的非负整数n,满足对于任意 ...

  6. Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:mav问题

    1.导致问题原因:从装系统,从win7改到win10 由于重装了系统,打开eclipse时,maven验证会出错,点击pom文件,会发现有红色的Cannot read lifecycle mappin ...

  7. vue添加背景音乐

    vue添加背景音乐需要用到HTML中的标签 参考手册:http://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp *在iOS端autopl ...

  8. gunicorn 参数

    gunicorn -w 4 -b 0.0.0.0:8080 yourpyfilename:app --log-level DEBUG --timeout 60gunicorn的命令对应参数含义如下: ...

  9. Qt应用开发所需

    Qt判断当前操作系统? 可使用宏判断,例如: #ifdef Q_OS_MAC //mac ... #endif #ifdef Q_OS_LINUX //linux ... #endif #ifdef ...

  10. .net core中使用efcore

    官网:https://docs.microsoft.com/zh-cn/aspnet/core/data/ef-mvc/intro?view=aspnetcore-2.2#register-the-s ...