欢迎来真孝善网,为您提供真孝善正能量书籍故事!

深入解析:Dalvik系统中的Class、DEX及ODEX文件结构

时间:11-13 神话故事 提交错误

大家好,今天来为大家分享深入解析: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的资料了!一直对这些虚拟机机制挺好奇的。

    有15位网友表示赞同!

冷眼旁观i

Class、DEX、ODEX,这三个词好像听过但具体不太清楚... 学习学习!

    有5位网友表示赞同!

我家的爱豆是怪比i

想了解Android底层,这些文件结构应该很重要吧?

    有6位网友表示赞同!

ヅ她的身影若隐若现

看来需要重温一下Dalvik虚拟机的工作原理了~

    有9位网友表示赞同!

■孤独像过不去的桥≈

之前只知道这些文件有用,不知道具体是什么样的。期待这篇文章能详细解释!

    有19位网友表示赞同!

々爱被冰凝固ゝ

学习Android开发的过程中一直对这些文件感到困惑,希望能在这里找到答案。

    有20位网友表示赞同!

ok绷遮不住我颓废的伤あ

看标题就知道和程序结构有关了,感觉很有趣啊!

    有11位网友表示赞同!

信仰

平时接触都是appapk文件,不知道这些文件在实际应用中具体的功能是怎样的?

    有18位网友表示赞同!

♂你那刺眼的温柔

最近想研究一下Android的运行机制,这本书看起来很合适。

    有7位网友表示赞同!

走过海棠暮

Dalvik虚拟机真是个重要的概念,学习这篇文章应该能提升我的Android开发水平。

    有5位网友表示赞同!

陌上花

分享这种专业资料真的很棒!能让更多人了解Dalvik虚拟机的运作方式。

    有12位网友表示赞同!

志平

感觉这篇分析应该会很详细,可以让我们深入了解文件结构的奥秘。

    有19位网友表示赞同!

仰望幸福

希望作者能用清晰的语言和图示来解释这些复杂的概念!

    有13位网友表示赞同!

凝残月

学习这种底层知识太重要了,对理解Android开发更有帮助。

    有13位网友表示赞同!

金橙橙。-

期待文章能带给我新的见解和启发!

    有9位网友表示赞同!

单身i

这个标题看起来很专业,我能掌握到最新的技术知识吗?

    有10位网友表示赞同!

心已麻木i

我对机器的学习原理一直感兴趣,这篇分析应该可以让我更深入地了解到虚拟机的运作方式。

    有14位网友表示赞同!

青袂婉约

希望这篇文章能够帮助我更好地理解Android程序的运行机制!

    有17位网友表示赞同!

惦着脚尖摘太阳

以前只知道Dalvik虚拟机,现在才知道还有这些文件结构,真是太惊喜了!

    有13位网友表示赞同!

浅嫣婉语

学习Android开发一定要了解这些基础知识才能更深入地去实践。

    有14位网友表示赞同!

【深入解析:Dalvik系统中的Class、DEX及ODEX文件结构】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活