Dalvik虚拟机【2】——Dex文件格式

  • 整个文件的布局
  • 文件头
  • 索引区
    • string_ids
    • type_ids
    • proto_ids
    • field_ids
    • method_ids
  • 数据区
    • class_def

      • class_data_item

        • code_item
    • data
  • 附录
    • 测试用的Dex的java代码
    • 参考资料

整个文件的布局

整个Dex文件可以分成三部分,文件头、索引区、数据区,如下图所示。

  • 文件头

    记录Dex文件的概览、包含文件大小、校验码以及其他字段的偏移和大小

  • 索引区

    记录字符串常量、类型、方法原形、域、方法的信息的索引

  • 数据区

    包含类的定义区class_defs,该部分记录类信息;以及数据区data,该部分包含实际信息(字符串、代码)等;link_data是链接数据区,主要和依赖库有关


使用 010 Editor 编辑器加上它官方的Dex模板可以方便、直观查看Dex文件内容.

文件头

偏移地址 字段名字 大小(byte) 说明
0 Magic[8] 8 魔数,用于识别Dex文件,内容为”dex\n035\0”
8 checksum 4 文件校验码
C Signature[20] SHA-1签名
20 file_size 4 Dex文件总长度
24 header_size 4 文件头大小,一般固定为0x70
28 endan_tag 4 大小端标志,标志dex文件格式为小端,一般固定为0x12345678
2C link_size 4 链接段的大小
30 link_off 4 链接段的基址
34 map_off 4 map item数据的基址
38 string_ids_size 4 字符串常量列表的个数
3C string_ids_off 4 字符串常量列表的基址
40 type_ids_size 4 类型的个数
44 type_ids_off 4 类型的基址
48 proto_ids_size 4 方法原形的个数
4C proto_ids_off 4 方法原形的基址
50 field_ids_size 4 域的个数
54 field_ids_off 4 域的基址
58 method_ids_size 4 方法的个数
5C method_ids_off 4 方法的基址
60 class_defs_size 4 class_def的个数
64 class_defs_off 4 class_def的基址
68 data_size 4 数据段的大小
6C data_off 4 数据段的基址

索引区

string_ids

这区域存储了字符串常量的索引信息。字符串常量并不仅仅是代码中定义的字符串,还包括所有的类名、方法名、类型名等信息.

该区域存储的是一个基址,也就是字符串实际内容在Dex文件的地址(在data区),它被Dalvik虚拟机读取后会转换成如下数据结构

// /android4.0.4/dalvik/libdex/DexClass.h
/*
 * Direct-mapped "string_id_item".
 */
struct DexStringId {
    u4 stringDataOff;      /* file offset to string_data_item */
};

根据偏移地址stringDataOff读取到字符串常量,该常量在内存的数据结构string_data_item为

struct string_data_item {
    uleb128 utf16_size; //字符串长度
    ubyte data; //字符串
}

用010 Editor查看一个测试用的Dex文件的string_ids区域如下

type_ids

这区域存储了dex 文件里的所有数据类型的索引信息,包括class类型,数组类型(array types)、基本类型(primitive types) 。其数据结构为

/*
 * Direct-mapped "type_id_item".
 */
struct DexTypeId {
    u4  descriptorIdx;      /* index into stringIds list for type descriptor */
};

和string_ids不一样,descriptorIdx并不是偏移地址,而是string_ids里的index序号,如descriptorIdx=8,则代表string_ids[8]=”V”,则代表类型为Void。如下图,根据文件头找到typd_ids的基址为A8H,size为7,从A8H第一个4字节为0x0003,即是string_idsp[3],即是”LFoo”,代表class类型Foo

proto_ids

proto是方法原形,包含方法的输入参数和输出参数,每个大小为12字节。其数据结构为

/*
 * Direct-mapped "proto_id_item".
 */
struct DexProtoId {
    u4  shortyIdx;          /* index into stringIds for shorty descriptor */
    u4  returnTypeIdx;      /* index into typeIds list for return type */
    u4  parametersOff;      /* file offset to type_list for parameter types */
};
  • shorty_idx

    跟 type_ids 一样,它的值是一个string_ids的index号,最终是一个简短的字符串描述 ,

  • returnTypeIdx

    返回类型,值是type_ids的index序号

  • parametersOff

    参数的基址

如下图

field_ids

这个区域里有dex文件引用的所有field(就是类的属性,包括静态的)索引,每个大小为12字节。其数据结构为

struct DexFieldId {
    u2  classIdx;           /* index into typeIds list for defining class */
    u2  typeIdx;            /* index into typeIds for field type */
    u4  nameIdx;            /* index into stringIds for field name */
};
  • classIdx

    该field所属的class,它的值是type_ids的index号

  • typeIdx

    该field的类型,它的值也是type_ids的index号

  • nameIdx

    该field的名称,它的值是string_ids的index号

如下图

method_ids

这个区域有dex文件所有方法的索引,格式和field_ids类似,每个大小为12字节。其数据结构为

/*
 * Direct-mapped "method_id_item".
 */
struct DexMethodId {
    u2  classIdx;           /* index into typeIds list for defining class */
    u2  protoIdx;           /* index into protoIds for method prototype */
    u4  nameIdx;            /* index into stringIds for method name */
};
  • classIdx

    该方法所属的class,它的值是type_ids的index号

  • protoIdx

    该方法的原形,它的值是proto_ids的index号

  • nameIdx

    该方法的名称,它的值是string_ids的index号

如下图

数据区

class_def

存储着Dex文件每一个类的相关信息,每个大小为32字节,其数据结构为

/*
 * Direct-mapped "class_def_item".
 */
struct DexClassDef {
    u4  classIdx;           /* index into typeIds for this class */
    u4  accessFlags;
    u4  superclassIdx;      /* index into typeIds for superclass */
    u4  interfacesOff;      /* file offset to DexTypeList */
    u4  sourceFileIdx;      /* index into stringIds for source file name */
    u4  annotationsOff;     /* file offset to annotations_directory_item */
    u4  classDataOff;       /* file offset to class_data_item */
    u4  staticValuesOff;    /* file offset to DexEncodedArray */
};
  • classIdx

    这个类的类型,值为type_ids的index

  • accessFlags

    qccess标志,指示public、private等

  • superclassIdx

    父类的类型,值为type_ids的index

  • interfacesOff

    DexTypeList的偏移地址,该结构代表这个类所拥有的interface,若没有,值为0

  • sourceFileIdx

    源文件名称,值为string_ids的index

  • annotationsOff

    annotations_directory_item的偏移地址,在data区,代表注释,若没有注释,值为0

  • classDataOff

    class_data_item的偏移地址,在data区,代表一个类的详细信息,包含field、method、method所执行的代码等,后面会详细介绍

  • staticValuesOff

    值为偏移地址,指向类的static field

如下图

class_data_item

DexClassDef的classDataOff字段指示了class_data_item在Dex文件中的地址,读入内存后的数据结构为DexClassData,在DexClass.h文件中

/* expanded form of class_data_item. Note: If a particular item is
 * absent (e.g., no static fields), then the corresponding pointer
 * is set to NULL. */
struct DexClassData {
    DexClassDataHeader header;//记录staticFields、instanceFields、directMethods、virtualMethods的size
    DexField*          staticFields;//类的static域
    DexField*          instanceFields;//类的实例域
    DexMethod*         directMethods;//类的方法
    DexMethod*         virtualMethods;//类的virtual方法
};

DexClassDataHeader、DexField、DexMethod数据结构如下

/* expanded form of a class_data_item header */
struct DexClassDataHeader {
    u4 staticFieldsSize;
    u4 instanceFieldsSize;
    u4 directMethodsSize;
    u4 virtualMethodsSize;
};

/* expanded form of encoded_field */
struct DexField {
    u4 fieldIdx;    /* index to a field_id_item */
    u4 accessFlags;
};

/* expanded form of encoded_method */
struct DexMethod {
    u4 methodIdx;    /* index to a method_id_item */
    u4 accessFlags;
    u4 codeOff;      /* file offset to a code_item */
};

fieldIdx值为索引区的field_ids的index,methodIdx值为索引区的method_ids的index,重点看codeOff,值为偏移地址,指示code_item

code_item

code_item记录类的方法的运行时相关信息,其数据结构在DexFile.h中声明:

struct DexCode {
    u2  registersSize;//寄存器的个数
    u2  insSize;//输入参数的个数
    u2  outsSize;//本段代码调用其他方法需要的参数
    u2  triesSize;//try item结构的个数,
    u4  debugInfoOff;       /* file offset to debug info stream */
    u4  insnsSize;          /* 指令列表的大小,以16-bit为单位 */
    u2  insns[1];//指令(就是字节码)
    /* followed by optional u2 padding */
    /* followed by try_item[triesSize] */
    /* followed by uleb128 handlersSize */
    /* followed by catch_handler_item[handlersSize] */
};
  • insnsSize和insns[1]

    insnsSize指示了指令个数,insns[1]就是实际的指令,大小并不是1,而是insnsSize

  • try_item 和 catch_handler_item

    在insns[1]后面可能还存在try_item和catch_handler_item,这2个用于捕获Java的异常,常见的Java代码有try catch

如下图

data

存储着字符串常量、字节码、map_item等实际内容

附录

测试用的Dex的java代码

Foo.java:

class Foo {
    public static void main(String[] args) {
        System.out.println("Hello, world");
    }
}

参考资料

http://bbs.pediy.com/showthread.php?t=184761

时间: 2024-10-17 10:08:27

Dalvik虚拟机【2】——Dex文件格式的相关文章

Dalvik虚拟机工作原理介绍

Dalvik虚拟机并没有使用目前流行的虚拟机技术,如JIT,但是根据Google的报告,这个功能的缺失并没有令Dalvik虚拟机在性能上有所损失.我们也同时相信,Dalvik虚拟机的性能还有进一步提高的空间. 根据 Google的测算,64M的RAM已经能够令系统正常运转了.其中24M被用于底层系统的初始化和启动,另外20M被用于高层启动高层服务.当然,随着系统服务的增多和应用功能的扩展,其所消耗的内存也势必越来越大,归纳起来,Dalvik虚拟机有如下几个主要特征: DEX是Dalvik虚拟机专

Dalvik虚拟机动态加载DEX/JAR

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWell 在文章Dalvik虚拟机加载类的机制中,我们讨论了Dalvik虚拟机加载类的相关原理,本文就来讨论一下,如何利用Dalvik提供的这些机制进行DEX/JAR的动态加载. 首先要说明的是虽然Dalvik虚拟机可以进行动态加载,却无法像Java虚拟机那样方便动态加载JAR,也不支持

Dalvik虚拟机片面总结

1.Dalvik 基于寄存器,而 JVM 基于栈.2.Dalvik负责进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例3.不同于Java虚拟机运行java字节码,Dalvik虚拟机运行的是其专有的文件格式Dex,ex文件格式可以减少整体文件尺寸4.所有的Android应用的线程都对应一个Linux线程5.Dalvik负责进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行.

java虚拟机与Dalvik虚拟机

Java语言的一个非常重要的特点就是与平台的无关性.而使用Java虚拟机是实现这一特点的关键.一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码.而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译.Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行.其实,Java虚拟机

Android Dex文件格式(二)

第三块: 数据区 索引区中的最终数据偏移以及文件头中描述的map_off偏移都指向数据区, 还包括了即将要解析的class_def_item, 这个结构非常重要,下面就开始解析 class_def_item: 这个结构由dex文件头中的classDefsSize和classDefsOff所指向, 描述Dex文件中所有类定义信息, 每一个DexClassDef中包含一个DexClassData的结构(classDataOff), 每一个DexClassData中包含了一个Class的数据, Cla

Android Dex文件格式(一)

dex是Android平台上(Dalvik虚拟机)的可执行文件, 相当于Windows平台中的exe文件, 每个Apk安装包中都有dex文件, 里面包含了该app的所有源码, 通过反编译工具可以获取到相应的java源码. 为什么需要学习dex文件格式? 最主要的一个原因: 由于通过反编译dex文件可以直接看到java源码, 越来越多的app(包括恶意病毒app)都使用了加固技术以防止app被轻易反编译, 当需要对一个加固的恶意病毒app进行分析或对一个app进行破解时, 就需要了解dex文件格式

Android逆向之旅---解析编译之后的Dex文件格式

一.前言 新的一年又开始了,大家是否还记得去年年末的时候,我们还有一件事没有做,那就是解析Android中编译之后的classes.dex文件格式,我们在去年的时候已经介绍了: 如何解析编译之后的xml文件格式: http://blog.csdn.net/jiangwei0910410003/article/details/50568487 如何解析编译之后的resource.arsc文件格式: http://blog.csdn.net/jiangwei0910410003/article/de

Android Dalvik虚拟机概述

Dalvik虚拟机概述 Dalvik是Google公司自己设计用于Android平台的Java虚拟机.Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一.它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统.Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进

Android 虚拟机学习总结Dalvik虚拟机介绍

1.Dalvik虚拟机与Java虚拟机的最显著区别是它们分别具有不同的类文件格式以及指令集.Dalvik虚拟机使用的是dex(Dalvik Executable)格式的类文件,而Java虚拟机使用的是class格式的类文件.一个dex文件可以包含若干个类,而一个class文件只包括一个类.由于一个dex文件可以包含若干个类,因此它就可以将各个类中重复的字符串和其它常数只保存一次,从而节省了空间,这样就适合在内存和处理器速度有限的手机系统中使用.一般来说,包含有相同类的未压缩dex文件稍小于一个已