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文件映射到内存后
500structDexFile {
501/* directly-mapped "opt" header */
502const DexOptHeader*pOptHeader; // odex文件头
503
504/* pointers to directly-mapped structs and arrays in base DEX */
505const DexHeader*pHeader;
506const DexStringId*pStringIds;
507const DexTypeId*pTypeIds;
508const DexFieldId*pFieldIds;
509const DexMethodId*pMethodIds;
510const DexProtoId*pProtoIds;
511const DexClassDef*pClassDefs;
512const DexLink*pLinkData;
513
514/*
515* 辅助数据段,记录文件被优化后添加的一些信息
517*/
518const DexClassLookup*pClassLookup;
519const void*pRegisterMapPool; // RegisterMapClassPool
520
521/* points to start of DEX file data */
522const u1*baseAddr;
523
524/* track memory overhead for auxillary structures */
525intoverhead;
526
527/* additional app-specific data structures associated with the DEX */
528//void* auxData;
529};
DexFile结构中存入其他结构的指针,描述的是加载到内存的数据结构,还有些数据是不会加载到内存的
DexOptHeader header;// odex文件头
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*/
474structDexOptHeader {
475u1magic[8]; /* odex版本标示 目前固定“64 65 79 0A 30 33 36 00” dey 036 */
476
477u4dexOffset; /* dex文件头偏移 目前固定为“28 00 00 00”*/
478u4dexLength; /* dex文件总长度*/
479u4depsOffset; /* odex依赖库列表偏移*/
480u4depsLength; /* 依赖库列表总长度*/
481u4optOffset; /* 辅助数据偏移 */
482u4optLength; /* 辅助数据总长度 */
483
484u4flags; /* 标志,标识了Dalvik虚拟机加载odex时的优化与验证选项*/
485u4checksum; /* 依赖库与辅助数据的校验和*/
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*/
1361staticintwriteDependencies(intfd, u4modWhen, u4crc)
1362{
1363u1*buf = NULL;
1364intresult = -1;
1365ssize_tbufLen;
1366ClassPathEntry*cpe;
1367intnumDeps;
1368
1369/*
1370* Count up the number of completed entries in the bootclasspath.
1371*/
1372numDeps = 0;
1373bufLen = 0;
1374for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1375const char*cacheFileName =
1376dvmPathToAbsolutePortion(getCacheFileName(cpe));
1377assert(cacheFileName!= NULL); /* guaranteed by Class.c*/
1378
1379ALOGV("+++ DexOpt: found dep '%s'", cacheFileName);
1380
1381numDeps++;
1382bufLen+= strlen(cacheFileName) +1;
1383}
1384
1385bufLen+= 4*4+ numDeps* (4+kSHA1DigestLen);
1386
1387buf= (u1*)malloc(bufLen);
1388
1389set4LE(buf+0, modWhen); // 写入时间戳注意:modWhenhe和crc通过
1390set4LE(buf+4, crc); // 写入crc校验dexZipGetEntryInfo()获取的
1391set4LE(buf+8, DALVIK_VM_BUILD); // 写入Dalvik虚拟机版本号
1392set4LE(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
1398u1*ptr= buf+ 4*4; // 跳过前四个字段
1399for (cpe= gDvm.bootClassPath; cpe->ptr!= NULL; cpe++) { // 循环写入依赖库
1400const char* cacheFileName =
1401dvmPathToAbsolutePortion(getCacheFileName(cpe));
1402assert(cacheFileName != NULL); /* guaranteed by Class.c */
1403
1404const u1*signature = getSignature(cpe); // 计算SHA-1 哈希值
1405intlen = strlen(cacheFileName) +1;
1406
1407if (ptr + 4+ len + kSHA1DigestLen > buf + bufLen) {
1408ALOGE("DexOpt: overran buffer");
1409dvmAbort();
1410}
1411
1412set4LE(ptr, len);
1413ptr += 4;
1414memcpy(ptr, cacheFileName, len); // 写入依赖库的名字
1415ptr += len;
1416memcpy(ptr, signature, kSHA1DigestLen); // 写入SHA-1哈希值
1417ptr += kSHA1DigestLen;
1418}
1419
1420assert(ptr == buf + bufLen);
1421
1422result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
1423
1424free(buf);
1425return 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*/
1482staticboolwriteOptData(int fd, const DexClassLookup* pClassLookup,
1483const RegisterMapBuilder* pRegMapBuilder)
1484{
1485/* pre-computed class lookup hash table */
1486if (!writeChunk(fd, (u4) kDexChunkClassLookup,
1487pClassLookup, pClassLookup->size))
1488{
1489return false;
1490}
1491
1492/* register maps (optional) */
1493if (pRegMapBuilder != NULL) {
1494if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
1495pRegMapBuilder->data, pRegMapBuilder->size))
1496{
1497return false;
1498}
1499}
1500
1501/* write the end marker */
1502if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
1503return false;
1504}
1505
1506return true;
1507}
数据是通过writeChunk()写入的,writeChunk()源码1429/*
1430* Write a block of data in "chunk" format.
1431*
1432* header结构体占8字节,type字段为1一个kDexChunk开头的常量
1433*
1434*/
1435staticboolwriteChunk(int fd, u4type, const void*data, size_tsize)
1436{
1437union { /* save a syscall by grouping these together */
1438charraw[8];
1439struct {
1440u4type;
1441u4size;
1442}ts;
1443}header;
1444
1445assert(sizeof(header) == 8);
1446
1447ALOGV("Writing chunk, type=%.4s size=%d", (char*) &type, size);
1448
1449header.ts.type = type;
1450header.ts.size = (u4) size;
1451if (sysWriteFully(fd, &header, sizeof(header),
1452"DexOpt opt chunk header write") != 0)
1453{
1454return false;
1455}
1456
1457if (size > 0) {
1458if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
1459return false;
1460}
1461
1462/* if necessary, pad to 64-bit alignment */
1463if ((size & 7) != 0) {
1464intpadSize = 8- (size & 7);
1465ALOGV("size was %d, inserting %d pad bytes", size, padSize);
1466lseek(fd, padSize, SEEK_CUR);
1467}
1468
1469assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
1470
1471return true;
1472}
writeChunk()方法中传入的type字段
188/* auxillary data section chunk codes */
189enum{
190kDexChunkClassLookup = 0x434c4b50, /* CLKP */
191kDexChunkRegisterMaps = 0x524d4150, /* RMAP */
192
193kDexChunkEnd = 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*/
456structDexClassLookup {
457int size; //本结构的字节数
458int numEntries; // 接下来table结构的项数,通常值为2
459struct {
460u4classDescriptorHash; // 类的哈希值
461intclassDescriptorOffset; //类的描述
462int 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;
}
转载于:https://www.cnblogs.com/heixiang/p/10967142.html
5.2 odex文件相关推荐
- 更改后缀为.dex文件为.odex文件 让你的程序瘦身 运行更稳定
今天无聊就把自己安装的软件在dalvik_cache对应的后缀为.dex文件重命名为.odex文件 然后把修改好的.odex文件移动到DATA中的APP里面[如果是复制过去的话 这里面.dex文件可以 ...
- android 中的 odex 文件
有很多时候,我们在修改bug的时候,发现代码明明修改了, 编译也ok了,但是将编译的结果 push 到手机上去之后,发现经常没效,纳闷了-- 经常是odex 在作怪: ODEX是安卓上的应用程序apk ...
- Android odex文件反编译
odex 是经过优化的dex文件,且独立存在于apk文件.odex 多用于系统预制应用或服务.通过将apk中的dex文件进行 odex,可以加载 apk 的启动速度,同时减小空间的占用.请参考ODEX ...
- .odex文件的反编译
0x00 问题呈现 在分析某手机自带应用时,为了在JEB中反编译,将其adb pull到了电脑上.解压后发现如下文件: APK解压目录列表 惊奇的发现该APK包中没有dex文件,一开始特别疑惑没有de ...
- 【Android安全】vdex、odex文件
Android中的vdex.odex文件 参考:https://blog.csdn.net/linxinfa/article/details/107669242 以下内容未验证 vdex文件 为何要搞 ...
- android odex文件权限定制
在做Felica FN认证的时候,遇到一个奇怪的需求:要求生成的odex文件权限是640 刚开始拿到这个问题的时候,还以为是在编译CI里有哪个地方可以配置,查了一下编译相当的mk文件,没有找到,然后查 ...
- android odex文件作用,安卓odex详细介绍
首先解释一下什么是Odex?Odex全称optimize dalvik package,Odex是安卓上的应用程序apk中提取出x来的可运行文件,是通过apk安装包的中的dex优化过的,再把apk包里 ...
- android odex 作用,Android ROM中Odex文件的作用及介绍
细心的网友可能发现Android的ROM中有很多odex文件,相对于 APK中的dex文件而言这个odex有什么作用呢? Android123提示大家,如果你仔细观察会发现文件名时一一对应的,同时那些 ...
- Android中odex 文件
聊完DexClassLoader问知不知道odex,这个以前也注意过. odex是安卓上的应用程序apk中提取出来的可运行文件,是通过apk安装包的中的dex优化过的,再把apk包里的dex文件删除. ...
最新文章
- 遍历二叉树的各种操作(非递归遍历)
- 基于若依框架的二次开发_浅谈若依框架
- 社交营销产品设计思考
- 切片slice(python)
- Redis简介及配置文件介绍
- java singleton inner class_Java面向对象设计模式-单例模式
- UITableViewCell点击不能push解决方法
- Hive中Join的 MR 底层原理
- zabbix通过jmx监控tomcat
- OpenCASCADE Expression Interpreter by Flex Bison
- 1bit等于多少字节,换算方法?
- als算法参数_推荐算法之ALS
- 2021-02-26
- java中laber字体颜色设置,重写jxl中可用的颜色实现自定义颜色
- 【日常】从批量合并 PDF 到 PyPDF2 的使用
- 宠物智能家居风口下,为何智能猫砂盆才是探路灯?
- python-----列表(list)下
- 使用C#语言编写记事本程序
- 易语言时间转化linux格式,易语言取时间年月日格式化
- 关于BP相关的若干赋值参数