大家好,今天来为大家分享深入解析:Dalvik系统中的Class、DEX及ODEX文件结构的一些知识点,和的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!
//唯一值:0xCAFEBABE
u4魔法;
//class文件的版本号与Java编译器有关
u2 次要版本;
u2 主要版本;
//常量池,长度为字符串个数加1,constant_pool[0]保留给JVM使用
u2 常量池计数;
cp_infoconstant_pool[constant_pool_count - 1];
//类的类型和名称,共有三种类型0x0001:ACC_PUBLIC
//0x0010:ACC_FINAL0x0200:ACC_INTERFACE
u2 访问标志;
u2 这个_类;
u2 超类;
//类接口的个数、变量的个数、方法的个数(无论静态还是非静态)、
//属性个数及其名称以字符串形式存储在常量池中
u2 接口数;
u2 接口[interfaces_count - 1];
u2 字段数;
field_info 字段[fields_count - 1];
u2 方法计数;
method_info 方法[methods_count - 1];
u2 属性计数;
attribute_info 属性[attributes_count - 1];
}Class文件实际操作,先写一个简单的Java文件:
包com.example;
公共类我的类{
公共静态无效主(字符串[] args){
字符串s="你好世界";
System.out.println(s);
}
}然后调用javac MyClass.java生成MyClass.class
调用javap -verbose MyClass.class查看
class文件struct.jpg
常量池
Tips:constant_pool_count的值是常量池数组的长度+1,就像上图中常量的第一个元素一样
从#1 开始,0 默认为VM。
常量池的元素类型表示如下:
cp_info {
//特别注意,这里引入的cp_info是相关元素类型的通用表达式。
u1 标签; //标签长度为1字节。不管cp_info是什么,第一个字节必须代表标签
u1信息[]; //其他信息,不同标签长度不同
}标签值:
tag=7==info表示cp_info是CONSTANT_Class_info。结构体tag=9==info表示CONSTANT_Fieldrefs_info。结构体tag=10==info表示CONSTANT_Methodrefs_info。结构体tag=8==info代表CONSTANT_String_info。结构体tag=1==info代表我们看几个CONSTANT_Utf8_info结构体的例子,字符串结构体:
CONSTANT_Utf8_info {
u1 标签; //值为1
u2长度; //以下是UTF8字符串存放的地方
u1 字节[长度];
}类信息结构
CONSTANT_Class_info {
u1 标签; //标签值为7,代表CONSTANT_Class_info
u2 名称_索引; //name_index表示表示自己的类名的字符串信息位于常量池数组中的哪个位置,即索引
}
dex文件
类文件显然有很多可以优化的地方。例如,每个类文件都有一个常量池。如果存在重复的字符串,会造成资源的浪费,所以Dalvik的dex文件对此进行了优化。
dex文件结构我们首先看一下dex文件中的数据结构。
下面很多代码都是在Android源码的DexFile.h中定义的
类型含义u1 相当于uint8_t,1 个字节的无符号数u2 相当于uint16_t,2 个字节的无符号数u4 相当于uint32_t,4 个字节的无符号数u8 相当于uint64_t,8 个字节的无符号数相当于uint64_t 符号数sleb128 有符号LEB128,变长1~5 个字节uleb128 无符号LEB128,变长1~5 个字节uleb128p1 无符号LEB128 值加1,变长1~5 个字节sleb128 在dex 文件中唯一数据类型,每个字节有7 个有效位,最高位值为1 表示使用第二个字节,以此类推,但最大长度为5 个字节。如果被读到
第5个字节的最高位仍然是1,表明dex文件无效。 Dalvik虚拟机在验证dex时会失败并使用变长方式表示字符串长度返回dex文件。字符串的长度可能是1 个字节(小于256)或4 个字节(大于1G)。大多数字符串的长度都小于256字节,因此需要使用一种既能表示1字节长度的编码,也能表示4字节长度的编码,其中1字节长度占大多数。最多。可以满足这种表示的编码方式有很多,但dex文件中使用的是uleb128方式。 leb128编码是一种变长编码。每个字节用7位来表示原始数据,最高位用来指示是否有后续字节。
检查dex方法类,将其转换为dex文件。该工具是sdk build_tools下的dx命令。 dx --dex --debug --verbose-dump--output=test.dex com/test/TestMain.class 查看dex文件,使用build-tools下的dexdump命令查看,dexdump -d -l plain test .dex :010 -1010的整体结构比较简单,由七个结构组成:
dex头指定了dex文件的一些属性,并记录了dex文件中其他六个部分的物理偏移量。 string_idstype_idsproto_idsfield_idsmethod_idsclass_defdatalink_datadexHeader结构体的组成
结构DexHeader {
000 u1 魔法[8]; //dex版本标识
u4 校验和; //adler32检查
u1 签名[KSHA1DIGEESTLEN]; //SHA-1哈希值长度为20,在DexFile.h中定义
020 u4 文件大小; //整个文件大小
u4 标头大小; //DexHeader结构体大小70 00 00 00
u4 字节序标签; //字节顺序标记默认78 56 34 12,即0x12345678,表示little-Endian字节顺序
u4 链接大小; //链接段大小
030 u4 链接断开; //连接段偏移量
u4映射; //DexMapList的文件偏移量,其中mapoff等于dataOff
u4 stringIdsSize; //DexStringId的个数
u4 stringIDsOff; //DexStringId的文件偏移量
040 u4 类型Ids大小; //DexTypeID的数量
u4 类型IdsOff; //DexTypeId的文件偏移量
u4 protoIdsSize; //DexProtoId的数量
u4 protoIdsOff; //DexProtoId的文件偏移量
050 u4 字段Ids大小; //DexFieldId的个数
u4 字段IdsOff; //DexFieldId的文件偏移量
u4 方法IdsSize; //DexMethodId的个数
u4 方法IdsOff; //DexMethonId的文件偏移量
060 u4 类定义大小; //DexClassDef的个数
u4 类定义关闭; //DexClassDef的文件偏移量
u4 数据大小; //数据段的大小
u4 数据关闭; //数据段的文件偏移量
}提示:
从上面的结构也可以看出,Android 65K方法数问题的根本原因并不在于Dex文件方法索引长度限制。
dex文件整体结构
提示:
书上有一些不清楚的地方(Android软件安全与逆向分析)。上面说Dalvik虚拟机解析dex文件的内容,最终映射成DexMapList数据结构。
,是否意味着Dalvik虚拟机参与了Dex文件的生成过程?
我分析了一个简单的Android程序,并使用十六进制编辑器C32Asm打开从apk中提取的dex文件。
上图的dex文件是完整的DexHeader数据,注释里写得很清楚了。观察到mapOff值为0x00059178。注意这里的小端字节顺序。寻找
Dex地图列表
红框中画的是每个元素的头部。第一个0x12代表16个DexMapItem结构。
DexMapItem 结构: struct DexMapItem{
u2型; //类型、枚举常量
u2 未使用; //未使用,用于字节对齐
u4尺寸; //指定类型的个数
u4偏移; //指定类型数据的文件偏移量
}
//类型枚举类型
/* 地图项类型代码*/
枚举{
kDexTypeHeaderItem=0x0000,
kDexTypeStringIdItem=0x0001,
kDexTypeTypeIdItem=0x0002,
kDexTypeProtoIdItem=0x0003,
kDexTypeFieldIdItem=0x0004,
kDexTypeMethodIdItem=0x0005,
kDexTypeClassDefItem=0x0006,
kDexTypeMapList=0x1000,
kDexType类型列表=0x1001,
kDexTypeAnnotationSetRefList=0x1002,
kDexTypeAnnotationSetItem=0x1003,
kDexTypeClassDataItem=0x2000,
kDexTypeCodeItem=0x2001,
kDexTypeStringDataItem=0x2002,
kDexTypeDebugInfoItem=0x2003,
kDexTypeAnnotationItem=0x2004,
kDexTypeEncodedArrayItem=0x2005,
kDexTypeAnnotationsDirectoryItem=0x2006,
};举个甜甜的栗子,在DexMapList中找到StringIdItem,number:0x39EE,offset:0x0070,在0x0070中找到第一个
0x0070 看一下DexStringId的结构:
结构DexStringId{
u4 字符串数据关闭; //字符串数据偏移量
}偏移量是0x0012c120,找到它
0x0012c120
现在已经找到了字符串,让我们找到一个更复杂的字符串。在DexMapList中找到MethodIdItem。编号为0x000038DD,偏移量为0x00026E98。找到它。
0x00026E98
看一下DexMethodId的结构: /*
* 直接映射“method_id_item”。
*/
结构DexMethodId {
u2 类Idx; /* 用于定义类的typeIds 列表的索引*/
u2 原型Idx; /* 索引到方法原型的protoIds */
u4 名称Idx; /* 方法名称的stringIds 索引*/
};
dex文件结构分析
Odex 文件以两种方式存在:
从Apk文件中提取出来的文件,存放在与Apk文件相同的目录下,文件后缀为odex。这些大多是Android ROM系统程序; dalvik-cache 缓存文件。该类型odex文件仍然以dex为后缀,保存在cache/dalvik-cache目录下,保存格式为"apk路径@apk名称@classes.dex";由于Android程序的Apk文件是Zip压缩包格式,因此Dalvik虚拟机每次加载时都需要从Apk中读取它们。 classes.dex文件,这个会消耗大量CPU时间,而使用odex
优化后的dex文件已经包含加载dex所需的依赖库文件列表。 Dalvik虚拟机只需要检测并加载所需的依赖库即可执行相应的dex文件,大大缩短了读取dex文件的时间。
所需时间。
odex文件
odex 文件头dex 文件依赖库辅助数据。 odex文件的写入和读取并没有像dex文件那样定义全系列的数据结构。 Dalvik虚拟机将dex文件映射到内存后,就是DexFile格式和结构。如下:
/*
* 表示DEX 文件的结构。
*
* 代码应将DexFile 视为不透明,使用此处提供的API 调用
* 访问特定的结构。
*/
结构DexFile {
/* 直接映射"opt" 标头*/
const DexOptHeader* pOptHeader;
/* 指向基础DEX 中直接映射的结构体和数组的指针*/
const DexHeader* pHeader;
const DexStringId* pStringIds;
const DexTypeId* pTypeIds;
const DexFieldId* pFieldIds;
const DexMethodId* pMethodIds;
const DexProtoId* pProtoIds;
const DexClassDef* pClassDefs;
const DexLink* pLinkData;
/*
* 这些是从“辅助”部分绘制的,可能不是
* 包含在文件中。
*/
const DexClassLookup* pClassLookup;
const void* pRegisterMapPool; //注册MapClassPool
/* 指向DEX文件数据的开始*/
常量u1* 基地址;
/* 跟踪辅助结构的内存开销*/
int 开销;
/* 与DEX 相关的其他特定于应用程序的数据结构*/
//无效* auxData;
};前面的DexOptHeader是odex的头部,DexLink下面的部分是‘auxillarysection’,也就是辅助数据部分,记录了文件优化后添加的一些信息。但是DexFile
该组织描述了加载到黄金内存中的数据结构,有些数据不会加载到内存中。冯胜强老师整理的odex文件结构的定义如下:
结构ODEXFile {
DexOptHeader 标头; //odex文件头
DexFile; DexFile; //dex文件
依赖deps; //依赖库列表
ChunkDexClassLookup 查找; //类查询结构
ChunkRegisterMapPool 映射池; //映射池
块结束结束; //结束标记
}
odex文件整体结构
ODEXFile的文件头DexOptHeader在DexFile.h文件中定义如下:
结构DexOptHeader{
u1魔法[8]; //odex版本标识,目前固定值为64 65 79 0A 30 33 36 00
u4 dexOffset; //dex文件头偏移量,当前0x28=40,等于odex文件头大小
u4 dex长度; //dex文件总长度
u4 depsOffset; //odex依赖库列表偏移量
u4 deps长度; //依赖库列表总长度
u4 optOffset; //辅助数据偏移量
u4 选择长度; //辅助数据总长度
u4 标志; //Dalvik虚拟机加载odex时的标志、优化和验证选项
【深入解析:Dalvik系统中的Class、DEX及ODEX文件结构】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
终于找到关于Dalvik的资料了!一直对这些虚拟机机制挺好奇的。
有15位网友表示赞同!
Class、DEX、ODEX,这三个词好像听过但具体不太清楚... 学习学习!
有5位网友表示赞同!
想了解Android底层,这些文件结构应该很重要吧?
有6位网友表示赞同!
看来需要重温一下Dalvik虚拟机的工作原理了~
有9位网友表示赞同!
之前只知道这些文件有用,不知道具体是什么样的。期待这篇文章能详细解释!
有19位网友表示赞同!
学习Android开发的过程中一直对这些文件感到困惑,希望能在这里找到答案。
有20位网友表示赞同!
看标题就知道和程序结构有关了,感觉很有趣啊!
有11位网友表示赞同!
平时接触都是appapk文件,不知道这些文件在实际应用中具体的功能是怎样的?
有18位网友表示赞同!
最近想研究一下Android的运行机制,这本书看起来很合适。
有7位网友表示赞同!
Dalvik虚拟机真是个重要的概念,学习这篇文章应该能提升我的Android开发水平。
有5位网友表示赞同!
分享这种专业资料真的很棒!能让更多人了解Dalvik虚拟机的运作方式。
有12位网友表示赞同!
感觉这篇分析应该会很详细,可以让我们深入了解文件结构的奥秘。
有19位网友表示赞同!
希望作者能用清晰的语言和图示来解释这些复杂的概念!
有13位网友表示赞同!
学习这种底层知识太重要了,对理解Android开发更有帮助。
有13位网友表示赞同!
期待文章能带给我新的见解和启发!
有9位网友表示赞同!
这个标题看起来很专业,我能掌握到最新的技术知识吗?
有10位网友表示赞同!
我对机器的学习原理一直感兴趣,这篇分析应该可以让我更深入地了解到虚拟机的运作方式。
有14位网友表示赞同!
希望这篇文章能够帮助我更好地理解Android程序的运行机制!
有17位网友表示赞同!
以前只知道Dalvik虚拟机,现在才知道还有这些文件结构,真是太惊喜了!
有13位网友表示赞同!
学习Android开发一定要了解这些基础知识才能更深入地去实践。
有14位网友表示赞同!