前言 最近接触Android逆向,就开始熟悉dex格式,顺便记录下数据结构,方便下次直接查询.
dex格式分析 
dex:  什么是DEX 
dex结构:  详细的dex数据结构 
引言   很久之前曾接触过Android,那时候对逆向感到畏惧,当真正的开始接触他的时候,感觉其实也就那样.需要很大的耐心去看. 
 
DEX   Dex是Android平台上可执行文件的类型。包含应用程序的全部操作指令以及运行时数据。由于dalvik是一种针对嵌入式设备而特殊设计的java虚拟机,所以dex文件与标准的class文件在结构设计上有着本质的区别.当java程序编译成class后,还需要使用dx工具将所有的class文件整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑,实验表明,dex文件是传统jar文件大小的50%左右.
DEX结构   下面就逐步开始描述这个dex文件,首先先上一张整体dex格式图,以便有一个宏观的了解.   通过图我们可以直观的发现整个结构的分布. 其数据结构为:1 struct DexFile  
{  
    DexHeader           Header;  
    DexStringId          StringIds[stringIdsSize];  
    DexTypeId           TypeIds[typeIdsSize];  
    DexProtoId           ProtoIds[protoIdsSize];  
    DexFieldId            FieldIds[fieldIdsSize];  
    DexMethodId       MethodIds[methodIdsSize];  
    DexClassDef         ClassDefs[classDefsSize];  
    DexData                Data[];  
    DexLink                 LinkData;  
}; 
名称 
格式 
描述 
 
 
header 
header_item 
文件头。 
 
string_ids 
string_id_item[] 
字符串索引表,记录了各个字符所在的偏移值,使用UTF-16编码。 
 
type_ids 
type_id_item[] 
类型数据索引,记录了各个类型的字符串索引。 
 
proto_id 
proto_id_item[] 
函数原型数据索引,记录了方法声明的字符串,返回类型和参数列表。 
 
field_ids 
field_id_item[] 
字段数据索引,记录了所属类,声明类型和方法名等信息。 
 
method_ids 
method_id_item[] 
类方法索引,记录了方法所属类,声明类型以及方法名等信息。 
 
class_defs 
class_def_item[] 
类定义数据,记录了指定类的各类信息,包括接口,超类,类数据偏移量等。 
 
data 
type_id_item[] 
数据区,保存着各个类的数据 
 
link_data 
ubyte[] 
静态连接数据 
 
 
  header的结构如下所示.1 typedef struct _DexHeader  
{  
    u1  magic[8];                       // dex 版本标识,"dex.035"  
    u4  checksum;                       // adler32 检验  
    u1  signature[kSHA1DigestLen];      // SHA-1 哈希值,20个字节  
    u4  fileSize;                       // 整个 dex 文件大小  
    u4  headerSize;                     // DexHeader 结构大小,0x70  
    u4  endianTag;                      // 字节序标记,小端 "0x12345678",大端"0x78563412"  
    u4  linkSize;                       // 链接段大小  
    u4  linkOff;                        // 链接段偏移  
    u4  mapOff;                         // DexMapList 的偏移  
    u4  stringIdsSize;                  // DexStringId 的个数  
    u4  stringIdsOff;                   // DexStringId 的偏移         字符串  
    u4  typeIdsSize;                    // DexTypeId 的个数  
    u4  typedeIdsOff;                   // DexTypeId 的偏移            类型  
    u4  protoIdsSize;                   // DexProtoId 的个数  
    u4  protoIdsOff;                    // DexProtoId 的偏移           声明  
    u4  fieldIdsSize;                   // DexFieldId 的个数  
    u4  fieldIdsOff;                    // DexFieldId 的偏移           字段  
    u4  methodIdsSize;                  // DexMethodId 的个数  
    u4  methodIdsOff;                   // DexMethodId 的偏移          方法  
    u4  classDefsSize;                  // DexClassDef 的个数  
    u4  classDefsOff;                   // DexClassDef 的偏移          类  
    u4  dataSize;                       // 数据段的大小  
    u4  dataOff;                        // 数据段的偏移  
}DexHeader, *PDexHeader; 
magic: 它代表dex中的文件标识,一般被称为魔数。是用来识别dex这种文件的,它可以判断当前的dex文件是否有效,可以看到它用了8个1字节的无符号数来表示. 
checksum: 它是dex文件的校验和,通过它可以判断dex文件是否被损坏或者被篡改。它占用4个字节. 
signature[kSHA1DigestLen]: signature字段用于检验dex文件,其实就是把整个dex文件用SHA-1签名得到的一个值。这里占用20个字节. 
fileSize: 表示整个文件的大小,占用4个字节。 
headerSize: 表示DexHeader头结构的大小,占用4个字节。 
endianTag: 代表 字节序标记,用于指定dex运行环境的cpu,预设值为0x12345678 
linkSize ,linkOff: 这两个字段,它们分别指定了链接段的大小和文件偏移,通常情况下它们都为0。linkSize为0的话表示静态链接 
mapOff: 它指定了DexMapList的文件偏移 
 
DexStringId 1 struct DexStringId { 
u4 stringDataOff;        /* string_data_item 偏移地址*/ 
}; 
struct string_data_item 
{
    uleb128 utf16_size;
    ubyte   data; 
} 
 
其中 data 保存的就是字符串的值。string_ids 是比较关键的,后续的区段很多都是直接指向 string_ids 的 index
DexTypeId 1 struct DexTypeId{
    u4 descriptorIdx;   /*指向DexStringId列表的索引*/
} 
 
DexProtoId 1 struct DexProtoId{
    u4 shortyIdx;           /*指向DexStringId列表的索引*/
    u4 returnTypeIdx;       /*指向DexTypeId列表的索引*/
    u4 parametersOff;       /*指向DexTypeList的位置偏移*/
} 
 
shorty_idx: 跟 type_ids 一样,它的值是一个 string_ids 的 index 号,最终是一个简短的字符串描述,用来说明该 method 原型。 
return_type_idx: 它的值是一个 type_ids 的 index 号 ,表示该 method 原型的返回值类型。 
parameters_off: 指向 method 原型的参数列表 type_list,若 method 没有参数,值为0。参数列表的格式是 type_list,下面会有描述。DexTypeList 1 struct DexTypeList{
    u4 size;        /*DexTypeItem的个数*/
    DexTypeItem list[1];    /*DexTypeItem结构*/
}
struct DexTypeItem{
    u2 typeIdx;             /*指向DexTypeId列表的索引*/
} 
 
 
 
DexFieldId 1 struct DexFieldId{
    u2 classIdx;        /*类的类型,指向DexTypeId列表的索引*/
    u2 typeIdx;     /*字段类型,指向DexTypeId列表的索引*/
    u4 nameIdx;     /*字段名,指向DexStringId列表的索引*/
} 
 
class_idx: 表示 field 所属的 class 类型,class_idx 的值是 type_ids 的一个 index,并且必须指向一个 class 类型。 
type_idx: 表示本 field 的类型,它的值也是 type_ids 的一个 index 。 
name_idx: 表示本 field 的名称,它的值是 string_ids 的一个 index 。 
 
DexMethodId 1 struct DexMethodId{
    u2 classIdx;        /*类的类型,指向DexTypeId列表的索引*/
    u2 protoIdx;        /*声明类型,指向DexProtoId列表的索引*/
    u4 nameIdx;     /*方法名,指向DexStringId列表的索引*/
} 
 
class_idx: 表示 method 所属的 class 类型,class_idx 的值是 type_ids 的一个 index,并且必须指向一个 class 类型。ushort类型也是为什么我们说一个 dex 只能有 65535 个方法的原因,多了必须分包。 
proto_idx: 表示 method 的类型,它的值也是 type_ids 的一个 index。 
name_idx: 表示 method 的名称,它的值是 string_ids 的一个 index。 
 
DexClassDef 1 struct DexClassDef{
    u4 classIdx;        /*类的类型,指向DexTypeId列表的索引*/
    u4 accessFlags;     /*访问标志*/
    u4 superclassIdx;   /*父类类型,指向DexTypeId列表的索引*/
    u4 interfacesOff;   /*接口,指向DexTypeList的偏移*/
    u4 sourceFileIdx;   /*源文件名,指向DexStringId列表的索引*/
    u4 annotationsOff;  /*注解,指向DexAnnotationsDirectoryItem结构*/
    u4 classDataOff;    /*指向DexClassData结构的偏移*/
    u4 staticValuesOff; /*指向DexEncodedArray结构的偏移*/
} 
 
class_idx: 描述具体的 class 类型,值是 type_ids 的一个 index 。值必须是一个 class 类型,不能是数组类型或者基本类型。 
access_flags: 描述 class 的访问类型,诸如 public , final , static 等。在 dex-format.html 里 “access_flags Definitions” 有具体的描述 。 
superclass_idx: 描述 supperclass 的类型,值的形式跟 class_idx 一样 。 
interfaces_off: 值为偏移地址,指向 class 的 interfaces,被指向的数据结构为 type_list 。class 若没有 interfaces 值为 0。 
source_file_idx: 表示源代码文件的信息,值是 string_ids 的一个 index。若此项信息缺失,此项值赋值为 NO_INDEX=0xffff ffff。 
annotions_off: 值是一个偏移地址,指向的内容是该 class 的注释,位置在 data 区,格式为 annotations_direcotry_item。若没有此项内容,值为 0 。 
class_data_off: 值是一个偏移地址,指向的内容是该 class 的使用到的数据,位置在 data 区,格式为 class_data_item。若没有此项内容值为 0。该结构里有很多内容,详细描述该 class 的 field、method, method 里的执行代码等信息,后面会介绍 class_data_item。 
static_value_off: 值是一个偏移地址 ,指向 data 区里的一个列表 (list),格式为 encoded_array_item。若没有此项内容值为 0。 
 
1 struct DexTypeList
{
    uint       size;
    type_item  list [size] 
}
struct type_item
{
    ushort type_idx   //-->type_ids
}
struct DexAnnotationsDirectoryItem
{
    uint class_annotations_off;        //-->annotation_set_item
    uint fields_size;
    uint annotated_methods_size;
    uint annotated_parameters_size;
    
    field_annotation field_annotations[fields_size];
    method_annotation method_annotations[annotated_methods_size];
    parameter_annotation parameter_annotations[annotated_parameters_size];
}
struct field_annotation
{
    uint field_idx;
    uint annotations_off;    //-->annotation_set_item
}
struct method_annotation
{
    uint method_idx;
    uint annotations_off;    //-->annotation_set_item
}
struct parameter_annotation
{
    uint method_idx;
    uint annotations_off;    //-->annotation_set_ref_list
} 
 
class_annotations_off: 这个偏移指向了 annotation_set_item 具体的可以看 dex-format.html 上的介绍. 
fields_size: 表示属性的个数 
annotated_methods_size: 表示方法的个数 
annotated_parameters_size: 表示参数的个数 
 
1 
struct DexClassData{
    DexClassDataHeader          header;         /*指定字段与方法的个数*/
    DexField*           staticFields;       /*静态字段,DexField结构*/
    DexField*           instanceFields; /*实例字段,DexField结构*/
    DexMethod*          directMethods;      /*直接方法,DexMethod结构*/
    DexMethod*          virtualMethods;     /*虚方法,DexMethod结构*/
}
struct DexClassDataHeader{
    u4 staticFieldsSize;    /*静态字段个数*/
    u4 instanceFieldsSize;  /*实例字段个数*/
    u4 directMethodsSize;   /*直接方法个数*/
    u4 virtualMethodsSize;  /*虚方法个数*/
}
struct encoded_field
{
    uleb128 filed_idx_diff; 
    uleb128 access_flags;  
}
struct encoded_method
{
    uleb128 method_idx_diff; 
    uleb128 access_flags; 
    uleb128 code_off;
} 
 
method_idx_diff: 前缀 methd_idx 表示它的值是 method_ids 的一个 index ,后缀 _diff 表示它是于另 外一个 method_idx 的一个差值 ,就是相对于 encodeed_method [] 数组里上一个元素的 method_idx 的差值 。 其实 encoded_filed - > field_idx_diff 表示的也是相同的意思 ,只是编译出来的 Hello.dex 文件里没有使用到 class filed 所以没有仔细讲 ,详细的参考 dex_format.html 的官网文档。 
access_flags: 访问权限,比如 public、private、static、final 等。 
code_off: 一个指向 data 区的偏移地址,目标是本 method 的代码实现。被指向的结构是code_item,有近 10 项元素。 
 
1 struct code_item 
{
    ushort                         registers_size;
    ushort                         ins_size;
    ushort                         outs_size;
    ushort                         tries_size;
    uint                         debug_info_off;
    uint                         insns_size;
    ushort                         insns [insns_size]; 
    ushort                         paddding;             // optional
    try_item                     tries [tyies_size]; // optional
    encoded_catch_handler_list  handlers;             // optional
} 
 
code_item 结构里描述着某个 method 的具体实现.
registers_size: 本段代码使用到的寄存器数目。 
ins_size: method 传入参数的数目 。 
outs_size: 本段代码调用其它 method 时需要的参数个数 。 
tries_size: try_item 结构的个数 。 
debug_off: 偏移地址,指向本段代码的 debug 信息存放位置,是一个 debug_info_item 结构。 
insns_size: 指令列表的大小,以 16-bit 为单位。 insns 是 instructions 的缩写 。 
padding: 值为 0,用于对齐字节 。 
tries 和 handlers: 用于处理 java 中的 exception,常见的语法有 try catch。 
 
1 struct encoded_array_item
{
    encoded_array value;
}
struct encoded_array
{    
    uleb128 size;
    encoded_value values[size];
} 
 
encoded_array_item是class_def_item->static_value_off 偏移指向的区段数据。
size : 表示encoded_value 个数1 struct DexField{
    u4 fieldIdx;        /*指向DexFieldId的索引*/
    u4 accessFlags;     /*访问标志*/
}
struct DexMethod{
    u4 methodIdx;       /*指向DexMethodId的索引*/
    u4 accessFlags;     /*访问标志*/
    u4 codeOff;     /*指向DexCode结构的偏移*/
}
struct DexCode {
    u2  registersSize;//使用寄存器个数
    u2  insSize;//参数个数
    u2  outsSize;//调用其他方法时使用的寄存器个数
    u2  triesSize;//try/catch个数
    u4  debugInfoOff;//指向调试信息的偏移
    u4  insnsSize;//指令集个数,以2字节为单位
    u2  insns[1];//指令集
    /* followed by optional u2 padding */
    /* followed by try_item[triesSize] */
    /* followed by uleb handlersSize */
    /* followed by catch_handler_item[handlersSize] */
}; 
 
 
 
DexMapList 1 struct DexMapList {
    u4  size;               /* 个数 */
    DexMapItem list[1];     /* DexMapItem的结构 */
};
struct DexMapItem {
    u2 type;              /* kDexType开头的类型 */
    u2 unused;            /*未使用,用于字节对齐 */
    u4 size;              /* 类型的个数 */
    u4 offset;            /* 类型的文件偏移 */
};
enum {
    kDexTypeHeaderItem               = 0x0,
    kDexTypeStringIdItem             = 0x1,
    kDexTypeTypeIdItem               = 0x2,
    kDexTypeProtoIdItem              = 0x3,
    kDexTypeFieldIdItem              = 0x4,
    kDexTypeMethodIdItem             = 0x5,
    kDexTypeClassDefItem             = 0x6,
    kDexTypeMapList                  = 0x0,
    kDexTypeTypeList                 = 0x1,
    kDexTypeAnnotationSetRefList     = 0x2,
    kDexTypeAnnotationSetItem        = 0x3,
    kDexTypeClassDataItem            = 0x0,
    kDexTypeCodeItem                 = 0x1,
    kDexTypeStringDataItem           = 0x2,
    kDexTypeDebugInfoItem            = 0x3,
    kDexTypeAnnotationItem           = 0x4,
    kDexTypeEncodedArrayItem         = 0x5,
    kDexTypeAnnotationsDirectoryItem = 0x6,
}; 
 
总结图   在结尾通过一张图把前面的东西串起来,如下所示,便于了解.