5.2 odex文件
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 DexStringId* pStringIds;
507 const DexTypeId* pTypeIds;
508 const DexFieldId* pFieldIds;
509 const DexMethodId* pMethodIds;
510 const DexProtoId* pProtoIds;
511 const DexClassDef* pClassDefs;
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文件的更多相关文章
- Android odex文件反编译
odex 是经过优化的dex文件,且独立存在于apk文件.odex 多用于系统预制应用或服务.通过将apk中的dex文件进行 odex,可以加载 apk 的启动速度,同时减小空间的占用.请参考ODEX ...
- android反编译odex文件
关于android的反编译工具,相信大家并不陌生 如APK-TOOL,dex2jar APK-TOOL 用于反编译出布局文件 下载地址http://code.google.com/p/android- ...
- DEX、ODEX、OAT文件&Dalvik和ART虚拟机
https://www.jianshu.com/p/389911e2cdfb https://www.jianshu.com/p/a468e714aca7 ODEX是安卓上的应用程序apk中提取出来的 ...
- dex和odex相互转换
一.dex和odex dex是安卓dalvik虚拟机的可执行文件,可以在导出的apk文件里用解压缩软件直接打开.odex是经过优化过的dex.odex一种是从apk程序中提取出来的,与apk文件存放在 ...
- 浅析dex文件加载机制
我们可以利用DexClassLoader来实现动态加载dex文件,而很多资料也只是对于DexClassLoader的使用进行了介绍,没有深入讲解dex的动态加载机制,我们就借助于Android4.4的 ...
- Android开发了解——ODEX
ODEX是安卓上的应用程序apk中提取出来的可运行文件,即将APK中的classes.dex文件通过dex优化过程将其优化生成一个·dex文件单独存放,原APK中的classes.dex文件会保留.这 ...
- android 反编译(dex 和 odex),非脑残转帖,绝对可靠
Android 反编译 反编译odex文件(比如framework.odex),若是反编译dex,直接第4步 1.因为反编译odex的工具在D:\Develop tools\android反编译工具\ ...
- [转]Android应用安装包apk文件的反编译与重编译、重签名
背景介绍: 最近在做Robotium自动化测试,使用到solo.takeScreenshot()函数以在测试过程中截图,但此函数需要被测试APP具有<uses-permission androi ...
- [教程] 【【【【odex教程之jar】】】】/system/framework里面的jar做odex g13
dexopt-wrapper core.jar core.odex dexopt-wrapper ext.jar ext.odex dexopt-wrapper framework.jar frame ...
随机推荐
- matlab-层次分析法
层次分析法(AHP)是把问题条理化.层次化,构造出一个有层次的结构模型的方法. 比如要选择旅游地,有3个选择方案,苏杭.北戴河和桂林.选择过程需要考虑多个因素,比如景色.费用.居住.饮食和旅途. 1. ...
- Hibernate 关联关系(一对多)
Hibernate 关联关系(一对多) 1. 什么是关联(association) 1.1 关联指的是类之间的引用关系.如果类A与类B关联,那么被引用的类B将被定义为类A的属性.例如: class B ...
- ESA2GJK1DH1K基础篇: 测试APP扫描Air202上面的二维码绑定通过MQTT控制设备(兼容SIM800)
前言 此程序兼容SIM800 如果想绑定SIM800,请把其IMEI号,生成二维码,用手机APP扫描. 实现功能概要 APP通过扫描二维码获取GPRS设备的IMEI号,然后设置订阅的主题:device ...
- Kali系统改国内源配置和SSH配置
一.Kali系统更新源 使用官网的虚拟化镜像安装,默认为英文界面,更新源也是官方源.因为官方服务器在国外,速度不是很理想,现在就来改国内源并且更新系统. 1.使用编辑器打开系统源文本(在终端内操作,先 ...
- [TJOI2009]猜数字(洛谷 3868)
题目描述 现有两组数字,每组k个,第一组中的数字分别为:a1,a2,...,ak表示,第二组中的数字分别用b1,b2,...,bk表示.其中第二组中的数字是两两互素的.求最小的非负整数n,满足对于任意 ...
- Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:mav问题
1.导致问题原因:从装系统,从win7改到win10 由于重装了系统,打开eclipse时,maven验证会出错,点击pom文件,会发现有红色的Cannot read lifecycle mappin ...
- vue添加背景音乐
vue添加背景音乐需要用到HTML中的标签 参考手册:http://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp *在iOS端autopl ...
- gunicorn 参数
gunicorn -w 4 -b 0.0.0.0:8080 yourpyfilename:app --log-level DEBUG --timeout 60gunicorn的命令对应参数含义如下: ...
- Qt应用开发所需
Qt判断当前操作系统? 可使用宏判断,例如: #ifdef Q_OS_MAC //mac ... #endif #ifdef Q_OS_LINUX //linux ... #endif #ifdef ...
- .net core中使用efcore
官网:https://docs.microsoft.com/zh-cn/aspnet/core/data/ef-mvc/intro?view=aspnetcore-2.2#register-the-s ...