首页
社区
课程
招聘
[旧帖] [原创]dex文件的格式解析 0.00雪花
发表于: 2016-2-19 16:26 2240

[旧帖] [原创]dex文件的格式解析 0.00雪花

2016-2-19 16:26
2240
新人贴,求转正,绝对本人原创,谢谢。

这里主要介绍dex文件的格式

先看DexFile定义
http://androidxref.com/6.0.0_r1/xref/art/runtime/dex_file.h
class DexFile {
  // Raw header_item.
  struct Header {
    uint8_t magic_[8];
    uint32_t checksum_;  // See also location_checksum_
    uint8_t signature_[kSha1DigestSize];//20
    uint32_t file_size_;  // size of entire file
    uint32_t header_size_;  // offset to start of next section
    uint32_t endian_tag_;
    uint32_t link_size_;  // unused
    uint32_t link_off_;  // unused
    uint32_t map_off_;  // unused
    uint32_t string_ids_size_;  // number of StringIds
    uint32_t string_ids_off_;  // file offset of StringIds array
    uint32_t type_ids_size_;  // number of TypeIds, we don't support more than 65535
    uint32_t type_ids_off_;  // file offset of TypeIds array
    uint32_t proto_ids_size_;  // number of ProtoIds, we don't support more than 65535
    uint32_t proto_ids_off_;  // file offset of ProtoIds array
    uint32_t field_ids_size_;  // number of FieldIds
    uint32_t field_ids_off_;  // file offset of FieldIds array
    uint32_t method_ids_size_;  // number of MethodIds
    uint32_t method_ids_off_;  // file offset of MethodIds array
    uint32_t class_defs_size_;  // number of ClassDefs
    uint32_t class_defs_off_;  // file offset of ClassDef array
    uint32_t data_size_;  // unused
    uint32_t data_off_;  // unused
  };

  // Map item type codes.
  enum {
    kDexTypeHeaderItem               = 0x0000,
    kDexTypeStringIdItem             = 0x0001,
    kDexTypeTypeIdItem               = 0x0002,
    kDexTypeProtoIdItem              = 0x0003,
    kDexTypeFieldIdItem              = 0x0004,
    kDexTypeMethodIdItem             = 0x0005,
    kDexTypeClassDefItem             = 0x0006,
    kDexTypeMapList                  = 0x1000,
    kDexTypeTypeList                 = 0x1001,
    kDexTypeAnnotationSetRefList     = 0x1002,
    kDexTypeAnnotationSetItem        = 0x1003,
    kDexTypeClassDataItem            = 0x2000,
    kDexTypeCodeItem                 = 0x2001,
    kDexTypeStringDataItem           = 0x2002,
    kDexTypeDebugInfoItem            = 0x2003,
    kDexTypeAnnotationItem           = 0x2004,
    kDexTypeEncodedArrayItem         = 0x2005,
    kDexTypeAnnotationsDirectoryItem = 0x2006,
  };

  struct MapItem {
    uint16_t type_;    //kDexType开头的类型
    uint16_t unused_;  //这个字段未使用,用来作字节对齐
    uint32_t size_;    //指定类型的个数
    uint32_t offset_;  //指定类型数据的文件偏移值
  };

  struct MapList {
    uint32_t size_;  //MapItem的个数
    MapItem list_[1];
  };

// Raw string_id_item.
  struct StringId {
    uint32_t string_data_off_;  // offset in bytes from the base address

   private:
    DISALLOW_COPY_AND_ASSIGN(StringId);
  };

  // Raw type_id_item.
  struct TypeId {
    uint32_t descriptor_idx_;  // index into string_ids

   private:
    DISALLOW_COPY_AND_ASSIGN(TypeId);
  };

  // Raw field_id_item.
  struct FieldId {
    uint16_t class_idx_;  // index into type_ids_ array for defining class
    uint16_t type_idx_;  // index into type_ids_ array for field type
    uint32_t name_idx_;  // index into string_ids_ array for field name

   private:
    DISALLOW_COPY_AND_ASSIGN(FieldId);
  };

  // Raw method_id_item.
  struct MethodId {
    uint16_t class_idx_;  // index into type_ids_ array for defining class
    uint16_t proto_idx_;  // index into proto_ids_ array for method prototype
    uint32_t name_idx_;  // index into string_ids_ array for method name

   private:
    DISALLOW_COPY_AND_ASSIGN(MethodId);
  };

  // Raw proto_id_item.
  struct ProtoId {
    uint32_t shorty_idx_;  // index into string_ids array for shorty descriptor
    uint16_t return_type_idx_;  // index into type_ids array for return type
    uint16_t pad_;             // padding = 0
    uint32_t parameters_off_;  // file offset to type_list for parameter types

   private:
    DISALLOW_COPY_AND_ASSIGN(ProtoId);
  };

  // Raw class_def_item.
  struct ClassDef {
    uint16_t class_idx_;  // index into type_ids_ array for this class
    uint16_t pad1_;  // padding = 0
    uint32_t access_flags_;
    uint16_t superclass_idx_;  // index into type_ids_ array for superclass
    uint16_t pad2_;  // padding = 0
    uint32_t interfaces_off_;  // file offset to TypeList
    uint32_t source_file_idx_;  // index into string_ids_ for source file name
    uint32_t annotations_off_;  // file offset to annotations_directory_item
    uint32_t class_data_off_;  // file offset to class_data_item
    uint32_t static_values_off_;  // file offset to EncodedArray

    // Returns the valid access flags, that is, Java modifier bits relevant to the ClassDef type
    // (class or interface). These are all in the lower 16b and do not contain runtime flags.
    uint32_t GetJavaAccessFlags() const {
       ..........
    }
  };

  // Raw type_item.
  struct TypeItem {
    uint16_t type_idx_;  // index into type_ids section
  };

  // Raw type_list.
  class TypeList {
   public:
    uint32_t Size() const {
      return size_;
    }

    const TypeItem& GetTypeItem(uint32_t idx) const {
      DCHECK_LT(idx, this->size_);
      return this->list_[idx];
    }

    // Size in bytes of the part of the list that is common.
    static constexpr size_t GetHeaderSize() {
      return 4U;
    }

    // Size in bytes of the whole type list including all the stored elements.
    static constexpr size_t GetListSize(size_t count) {
      return GetHeaderSize() + sizeof(TypeItem) * count;
    }

   private:
    uint32_t size_;  // size of the list, in entries
    TypeItem list_[1];  // elements of the list
  };

  // Raw code_item.
  struct CodeItem {
    uint16_t registers_size_;
    uint16_t ins_size_;
    uint16_t outs_size_;
    uint16_t tries_size_;
    uint32_t debug_info_off_;  // file offset to debug info stream
    uint32_t insns_size_in_code_units_;  // size of the insns array, in 2 byte code units
    uint16_t insns_[1];
  };

  // Raw try_item.
  struct TryItem {
    uint32_t start_addr_;
    uint16_t insn_count_;
    uint16_t handler_off_;
  };

  ..........
}

每个dex file最开始放的是一个Header,后面所有内容都是依据Header来解析的
uint32_t map_off_; 指向的是一个MapList 结构体
struct MapList {
    uint32_t size_;  //MapItem的个数
    MapItem list_[1];
  };

其实我觉得MapItem list_[1]; 声明成这样子MapItem list_[0];可能更好些,因为它表现的意思是size_字段后面存放的是size_个MapItem,也就是说MapList.list_ 是(size_ * MapItem)的起始地址。

每一个MapItem代表一种类型的描述,类型种类如下:
// Map item type codes.
  enum {
    kDexTypeHeaderItem               = 0x0000,
    kDexTypeStringIdItem             = 0x0001,
    kDexTypeTypeIdItem               = 0x0002,
    kDexTypeProtoIdItem              = 0x0003,
    kDexTypeFieldIdItem              = 0x0004,
    kDexTypeMethodIdItem             = 0x0005,
    kDexTypeClassDefItem             = 0x0006,
    kDexTypeMapList                  = 0x1000,
    kDexTypeTypeList                 = 0x1001,
    kDexTypeAnnotationSetRefList     = 0x1002,
    kDexTypeAnnotationSetItem        = 0x1003,
    kDexTypeClassDataItem            = 0x2000,
    kDexTypeCodeItem                 = 0x2001,
    kDexTypeStringDataItem           = 0x2002,
    kDexTypeDebugInfoItem            = 0x2003,
    kDexTypeAnnotationItem           = 0x2004,
    kDexTypeEncodedArrayItem         = 0x2005,
    kDexTypeAnnotationsDirectoryItem = 0x2006,
  };

以上描述的还是很模糊,下面我们通过实例来说明。

dex文件的产生
可以直接从apk文件中解压出来的class.dex文件做分析。
但是为了使dex文件小一点便于分析,我们自己制作个dex文件,步骤如下
//Bleach.java
public class Bleach {
    public static final String TAG = "111111111";
    public static final String name = "caoming_123456";

    public static final int id1 = 0x333333;
    private static final int id2 = 0x444444;

    public static void main(String[] args) {
        add(id1, id2);
    }

    public static int add(int a, int b) {
        int c = a + b;
        return c;
    }
}

用以下命令生成Bleach.dex文件
$ javac Bleach.java
$ dx --dex  --output=Bleach.dex Bleach.class

dex文件格式的分析
使用十六进制工具(比如sublime)打开文件Bleach.dex,内容是酱紫:
0000000: 6465 780a 3033 3500 ff5d 319c a78a 60ba  dex.035..]1...`.
0000010: f6ea 6645 dfbc 2572 199b c30a 06a2 965b  ..fE..%r.......[
0000020: 4403 0000 7000 0000 7856 3412 0000 0000  D...p...xV4.....
0000030: 0000 0000 9802 0000 1200 0000 7000 0000  ............p...
0000040: 0600 0000 b800 0000 0300 0000 d000 0000  ................
0000050: 0400 0000 f400 0000 0400 0000 1401 0000  ................
0000060: 0100 0000 3401 0000 f001 0000 5401 0000  ....4.......T...      
0000070: b601 0000 c101 0000 c901 0000 d601 0000  ................
0000080: d901 0000 de01 0000 e801 0000 fc01 0000  ................
0000090: 1002 0000 1502 0000 1802 0000 1c02 0000  ................
00000a0: 3102 0000 3602 0000 4602 0000 4b02 0000  1...6...F...K...
00000b0: 5002 0000 5602 0000 0300 0000 0500 0000  P...V...........
00000c0: 0600 0000 0700 0000 0900 0000 0b00 0000  ................
00000d0: 0400 0000 0000 0000 a801 0000 0900 0000  ................
00000e0: 0400 0000 0000 0000 0a00 0000 0400 0000  ................
00000f0: b001 0000 0100 0300 0800 0000 0100 0000  ................
0000100: 0e00 0000 0100 0000 0f00 0000 0100 0300  ................
0000110: 1100 0000 0100 0100 0100 0000 0100 0000  ................
0000120: 0c00 0000 0100 0200 1000 0000 0200 0100  ................
0000130: 0100 0000 0100 0000 0100 0000 0200 0000  ................
0000140: 0000 0000 0200 0000 0000 0000 7d02 0000  ............}...
0000150: 7002 0000 0100 0100 0100 0000 5c02 0000  p...........\...
0000160: 0400 0000 7010 0300 0000 0e00 0300 0200  ....p...........
0000170: 0000 0000 6102 0000 0300 0000 9000 0102  ....a...........
0000180: 0f00 0000 0300 0100 0200 0000 6902 0000  ............i...
0000190: 0a00 0000 1400 3333 3300 1401 4444 4400  ......333...DDD.
00001a0: 7120 0100 1000 0e00 0200 0000 0000 0000  q ..............
00001b0: 0100 0000 0500 0931 3131 3131 3131 3131  .......111111111
00001c0: 0006 3c69 6e69 743e 000b 426c 6561 6368  ..<init>..Bleach
00001d0: 2e6a 6176 6100 0149 0003 4949 4900 084c  .java..I..III..L
00001e0: 426c 6561 6368 3b00 124c 6a61 7661 2f6c  Bleach;..Ljava/l
00001f0: 616e 672f 4f62 6a65 6374 3b00 124c 6a61  ang/Object;..Lja
0000200: 7661 2f6c 616e 672f 5374 7269 6e67 3b00  va/lang/String;.
0000210: 0354 4147 0001 5600 0256 4c00 135b 4c6a  .TAG..V..VL..[Lj
0000220: 6176 612f 6c61 6e67 2f53 7472 696e 673b  ava/lang/String;
0000230: 0003 6164 6400 0e63 616f 6d69 6e67 5f31  ..add..caoming_1
0000240: 3233 3435 3600 0369 6431 0003 6964 3200  23456..id1..id2.
0000250: 046d 6169 6e00 046e 616d 6500 0100 070e  .main..name.....
0000260: 000d 0200 0007 0e2d 0009 0100 070e 9600  .......-........
0000270: 0417 0044 3333 3344 4444 4417 0d04 0003  ...D333DDDD.....
0000280: 0000 1901 1901 1a01 1900 8180 04d4 0201  ................
0000290: 09ec 0201 0984 0300 0e00 0000 0000 0000  ................
00002a0: 0100 0000 0000 0000 0100 0000 1200 0000  ................
00002b0: 7000 0000 0200 0000 0600 0000 b800 0000  p...............
00002c0: 0300 0000 0300 0000 d000 0000 0400 0000  ................
00002d0: 0400 0000 f400 0000 0500 0000 0400 0000  ................
00002e0: 1401 0000 0600 0000 0100 0000 3401 0000  ............4...
00002f0: 0120 0000 0300 0000 5401 0000 0110 0000  . ......T.......
0000300: 0200 0000 a801 0000 0220 0000 1200 0000  ......... ......
0000310: b601 0000 0320 0000 0300 0000 5c02 0000  ..... ......\...
0000320: 0520 0000 0100 0000 7002 0000 0020 0000  . ......p.... ..
0000330: 0100 0000 7d02 0000 0010 0000 0100 0000  ....}...........
0000340: 9802 0000                                ....
现在依据Header字段来进行分析
magic字段为8个字节,标示一个有效的dex文件它的值固定为6465 780a 3033 3500
转换为字符串为“dex.035.”

checksum字段为4个字节  ff5d 319c

哈希值为20个字节
a78a 60ba f6ea 6645 dfbc 2572 199b c30a 06a2 965b

fileSize 4个字节 为整个文件大小:4403 0000
0x(0000 0344) = 836
mutian@mutian:~/Desktop/test$ ll
-rw-rw-r-- 1 mutian mutian  836 Jan 29 10:58 Bleach.dex

HeaderSize DexHeader结构大小,为固定值7000 0000
0x(0000 0070) = 112

endianTag字段指定了dex运行环境的cpu字节序
预设值ENDIAN_CONSTANT = 0x12345678表示默认采用Little-Endian字节序, 即小端字节序(字节序大端小端问题,不清楚请百度)

linkSize字段与stringIdsOff字段指定链接段的大小与文件偏移,大多数情况下他们值都为0

mapOff字段指定了DexMapList结构的文件偏移:9802 0000
偏移地址map_off_=0x(0000 0298)
[0x0298] = 0e00 0000   即MapList.size_ = 0x(0000 000e) 表示后面有14个MapItem
0000 0000 0100 0000 0000 0000
0100 0000 1200 0000 7000 0000
0200 0000 0600 0000 b800 0000
0300 0000 0300 0000 d000 0000
0400 0000 0400 0000 f400 0000
.............................
分析第一个MapItem
0000 0000 0100 0000 0000 0000
类型为0(kDexTypeHeaderItem),个数为1,偏移为0

分析第二个MapItem
0100 0000 1200 0000 7000 0000
类型为1(kDexTypeStringIdItem),个数为0x(0000 0012)=18,也就是说有18个字符串。这18个字符串的指针是存放在一个字符串指针数组里面的.
字符串指针数组的首地址为0x(0000 0070)

0000070: b601 0000 c101 0000 c901 0000 d601 0000  ................
0000080: d901 0000 de01 0000 e801 0000 fc01 0000  ................
0000090: 1002 0000 1502 0000 1802 0000 1c02 0000  ................
00000a0: 3102 0000 3602 0000 4602 0000 4b02 0000  1...6...F...K...
00000b0: 5002 0000 5602 0000

这18个字符串的存放地址依次为0x(0000 01b6)   0x(0000 01c1)  0x(0000 01c9)  0x(0000 01d6) ......
这里以第一个字符串为例,其存放地址为0x(0000 01b6)
00001b0: 0100 0000 0500 0931 3131 3131 3131 3131  .......111111111
00001c0: 0006 3c69 6e69 743e 000b 426c 6561 6368  ..<init>..Bleach

第一个字节0x(09)表示字符串的长度(字符串结束标识符不计算在内)
所以字符串内容为31 3131 3131 3131 3131,即“111111111”,这个就是我们代码里面定义的那个字符串了

第二个字符串,其存放地址0x(0000 01c1)
字符串长度为0x(06),内容为3c69 6e69 743e,即“<init>”  ,也不知道系统加这个字符串干嘛的

其它字段类推......................
其它MapItem类推......................

最后补充一点:
其实我们根据uint32_t map_off_ 就可以推出所有类型的描述,但是系统却把某些可以依据map_off_计算出来的类型的字段也存储起来了,比如
    uint32_t string_ids_size_;  // number of StringIds
    uint32_t string_ids_off_;  // file offset of StringIds array
我们的dex文件里面
0000030: 0000 0000 9802 0000 1200 0000 7000 0000  ............p...
string_ids_size_ = 0x(0000 0012) = 18
string_ids_off_ = 0x(0000 0070)
这两个结果和我们依据map_off_算出来的结果是一样的。估计这是为了更快速的查找吧,所以才在生成dex文件的时候把这些值存储起来的。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
这个版块也是有大神的
2016-2-23 12:10
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
好帖,支持你,好好学习
2016-3-15 21:57
0
游客
登录 | 注册 方可回帖
返回
//