Android Dex文件

.dex(Dalvik Executable Format)文件用于保存类定义及其关联的辅助数据

文件格式

Android Dex文件_第1张图片

header文件头

数据结构

  // Raw header_item.
  struct Header {
    uint8_t magic_[8];
    uint32_t checksum_;  // See also location_checksum_
    uint8_t signature_[kSha1DigestSize];
    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
    ......
  }
  • file_size_: 整个文件的大小,单位字节
  • header_size_: 文件头的大小,单位字节
  • string_ids_size_: 字符串列表中字符串的数量
  • string_ids_off_: 字符串列表在文件中的偏移
  • type_ids_size_: 类型列表中元素数量
  • type_ids_off_: 类型列表在文件中的偏移
  • proto_ids_size_: 方法原型列表元素数量
  • proto_ids_off_: 方法原型列表在文件中的偏移
  • field_ids_size_: 字段列表的元素数量
  • field_ids_off_: 字段列表在文件中的偏移
  • method_ids_size_: 方法列表元素的数量
  • method_ids_off_: 方法列表在文件中的偏移
  • class_defs_size_: 类定义列表中元素数量
  • class_defs_off_: 类定义列表在文件中的偏移
  • data_size_: data段的大小
  • data_off_: data段在文件中的偏移

例子

DEX file header:
magic               : 'dex\n035\0'
checksum            : 1181417f
signature           : a022...4f7a
file_size           : 2727984
header_size         : 112
link_size           : 0
link_off            : 0 (0x000000)
string_ids_size     : 19080
string_ids_off      : 112 (0x000070)
type_ids_size       : 2332
type_ids_off        : 76432 (0x012a90)
proto_ids_size       : 3091
proto_ids_off        : 85760 (0x014f00)
field_ids_size      : 8477
field_ids_off       : 122852 (0x01dfe4)
method_ids_size     : 17750
method_ids_off      : 190668 (0x02e8cc)
class_defs_size     : 1681
class_defs_off      : 332668 (0x05137c)
data_size           : 2325048
data_off            : 402936 (0x0625f8) ===> 类定义列表与data段有间隙

string_ids字符串列表

文件使用的所有字符串的标识符,用于内部命名(例如类型描述符)或用作代码引用的常量对象。此列表必须使用 UTF-16 代码点值按字符串内容进行排序,且不得包含任何重复条目。

元素数据结构

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

   private:
    DISALLOW_COPY_AND_ASSIGN(StringId);
  };
  • string_data_off_: 字符串数据在文件中的偏移量,该偏移量应该在data段中,采用string_data_item指定的格式

    名称 格式 说明
    utf16_size uleb128 字符串的长度
    data ubyte[] 一系列MUTF-8代码单元

    对于leb128以及MUTF-8请参考https://source.android.com/devices/tech/dalvik/dex-format?hl=zh-cn

例子

......
00000070  08 5d 18 00 0a 5d 18 00  0d 5d 18 00 22 5d 18 00  |.]...]...].."]..|
00000080  38 5d 18 00 3b 5d 18 00  3f 5d 18 00 44 5d 18 00  |8]..;]..?]..D]..|
......
string_ids => 0x70
第一个StringId(string_id_item)的string_data_off_为0x00185d08
第二个StringId(string_id_item)的string_data_off_为0x00185d0a
第三个StringId(string_id_item)的string_data_off_为0x00185d0d
第四个StringId(string_id_item)的string_data_off_为0x00185d22
......

data段的字符串信息:
00185d00  00 00 00 00 00 00 00 00  00 00 01 0a 00 13 0a 20  |............... |
00185d10  20 43 6c 69 65 6e 74 20  76 65 72 73 69 6f 6e 3a  | Client version:|
00185d20  20 00 14 0a 20 20 53 65  72 76 69 63 65 20 76 65  | ...  Service ve|

第一个字符串二进制: 00 00
第二个字符串二进制: 01 0a 00
第三个字符串二进制: 13 0a 20 20 43 6c 69 65 6e 74 20  76 65 72 73 69 6f 6e 3a 20 00

type_ids类型列表

类型标识符列表,包含文件引用的所有类型(类、数组或原始类型)的标识符,此列表必须按 string_id 索引进行排序,且不得包含任何重复条目。

元素数据结构

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

   private:
    DISALLOW_COPY_AND_ASSIGN(TypeId);
  };
  • descriptor_idx_: 类描述符在string_ids列表中的索引

例子

type_ids位置:
00012a90  a5 04 00 00 9d 05 00 00  0b 07 00 00 6c 08 00 00  |............l...|
00012aa0  72 09 00 00 21 0a 00 00  06 0b 00 00 07 0b 00 00  |r...!...........|

第一个类描述符在string_ids列表中的索引: 0x000004a5 (位置: 0x70 + 0x4a5 * 4 = 0x1304)

对应的string_ids列表内容:
00001300  65 63 19 00 7a 63 19 00  7d 63 19 00 81 63 19 00  |ec..zc..}c...c..|

(0x0019637a)对应的data段的字符串信息:
00196370  65 44 65 6c 65 67 61 74  65 00 01 42 00 02 42 3a  |eDelegate..B..B:|
对应的类型为B(基本类型byte)

proto_ids方法原型列表

方法原型标识符列表,包含文件引用的所有原型的标识符

元素数据结构

  // 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);
  };
  • shorty_idx_: 方法原型的简短描述符字符串在string_ids列表中的索引

  • return_type_idx_: 方法原型的返回类型在type_ids列表中的索引

  • parameters_off_: 方法原型的参数类型列表在文件中的偏移量,如果没有参数,该值为0;该偏移量应该位于data段中,数据采用type_list指定的格式

    名称 格式 说明
    size uint 列表的大小
    list type_item[size] 列表元素
    • type_item格式
    名称 格式 说明
    type_idx ushort 在type_ids列表中的索引

field_ids字段列表

元素数据结构

  // 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);
  };
  • class_idx_: 定义该字段的类在type_ids中的索引
  • type_idx_: 字段的类型在type_ids中的索引
  • name_idx_: 字段的名称在string_ids中的索引

method_ids方法列表

元素数据结构

  // 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);
  };
  • class_idx_: 定义该方法的类在type_ids中的索引
  • proto_idx_: 方法的原型在proto_ids中的索引,uint16_t表示(0~65535),最多有65536个方法原型
  • name_idx_: 方法的名字在string_ids中的索引

class_defs类定义列表

元素数据结构

  // 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
    ......
  }
  • class_idx_: 类在type_ids中的索引

  • access_flags_: 类的访问标志

  • superclass_idx_: 超类在type_ids中的索引

  • interfaces_off_: 接口列表在文件中的偏移量,如果没有接口,该值为0,否则该偏移量应该位于data段,采用type_list制定的格式

  • source_file_idx_: 文件(包含该类)名称在string_ids列表中的索引;或者该值为NO_INDEX,表示缺少信息

  • annotations_off_: 类的注解在文件中的偏移量,如果没有注解,该值为0,否则该偏移量应该位于data段,采用annotations_directory_item指定的格式

  • 名称 格式 说明
    static_fields_size uleb128 静态字段的数量
    instance_fields_size uleb128 实例字段的数量
    direct_methods_size uleb128 直接方法的数量
    virtual_methods_size uleb128 虚方法的数量
    static_fields encoded_field[static_fields_size] 静态字段,按在field_idx中的索引升序排序
    instance_fields encoded_field[instance_fields_size] 实例字段,按在field_idx中的索引升序排序
    direct_methods encoded_method[direct_methods_size] 直接方法,按在method_ids中的索引升序排序
    virtual_methods encoded_method[virtual_methods_size] 虚方法,按在method_ids中的索引升序排序

    class_data_off_: 类数据在文件中的偏移量,如果没有没数据(标记接口),该值为0;否则该值应该位于data段,采用class_data_item指定格式

    名称 格式 说明
    static_fields_size uleb128 静态字段的数量
    instance_fields_size uleb128 实例字段的数量
    direct_methods_size uleb128 直接方法的数量
    virtual_methods_size uleb128 虚方法的数量
    static_fields encoded_field[static_fields_size] 静态字段,按在field_idx中的索引升序排序
    instance_fields encoded_field[instance_fields_size] 实例字段,按在field_idx中的索引升序排序
    direct_methods encoded_method[direct_methods_size] 直接方法,按在method_ids中的索引升序排序
    virtual_methods encoded_method[virtual_methods_size] 虚方法,按在method_ids中的索引升序排序
    • encoded_field格式
    名称 格式 说明
    field_idx_diff uleb128 字段在field_ids列表中的索引;它的值为与列表中前一个元素的索引之差,第一个元素的值则直接表示
    access_flags uleb128 字段的访问标志
    • encoded_method 格式
    名称 格式 说明
    method_idx_diff uleb128 方法在method_ids列表中的索引;它的值为与列表中前一个元素的索引之差,第一个元素的值则直接表示
    access_flags uleb128 方法的访问标志
    code_off uleb128 方法的代码数据在文件中的偏移量,如果方法是abstract或者native,该值为0;否则该偏移量应该在data段中,采用code_item指定格式
    • code_item
    // Raw code_item.
    struct CodeItem {
      uint16_t registers_size_;            // the number of registers used by this code
                                         //   (locals + parameters)
      uint16_t ins_size_;                  // the number of words of incoming arguments to the method
                                         //   that this code is for
      uint16_t outs_size_;                 // the number of words of outgoing argument space required
                                         //   by this code for method invocation
      uint16_t tries_size_;                // the number of try_items for this instance. If non-zero,
                                         //   then these appear as the tries array just after the
                                         //   insns in this instance.
      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];                  // actual array of bytecode.
    
      private:
      DISALLOW_COPY_AND_ASSIGN(CodeItem);
    };
    
    • registers_size_: 代码使用的寄存器数量(包括本地变量以及参数)
    • ins_size_: 方法入参字数
    • outs_size_: 方法出参字数
    • tries_size_: 该实例的try_items数目,如果非0,try_items出现在该实例的insns之后
    • debug_info_off_: 调试信息在文件中的偏移,如果非0,该偏移量应该在data段,采用debug_info_item指定格式
    • insns_size_in_code_units_: 指令列表的大小,以2个字节为单位
    • insns_: 字节码实际数组
    • try_item格式
    // Raw try_item.
    struct TryItem {
      uint32_t start_addr_; // try代码块的起始地址
      uint16_t insn_count_; // try代码块覆盖的16位代码单元的数量
      uint16_t handler_off_; // 从关联的encoded_catch_hander_list开头部分到此条目的encoded_catch_handler的偏移量(以字节为单位)
    
      private:
      DISALLOW_COPY_AND_ASSIGN(TryItem);
    };
    
    • encoded_catch_handler_list格式(在try_item列表之后)
    名称 格式 说明
    size uleb128 列表的大小
    list encoded_catch_handler[handlers_size] 异常处理程序列表
    • encoded_catch_handler格式
    名称 格式 说明
    size sleb128 捕获异常类型的数量,size为0表示捕获类型为“全部捕获”,而没有明确类型的捕获。size为2表示有两个明确类型的捕获,但没有“全部捕获”类型的捕获。size为-1表示有一个明确类型的捕获和一个“全部捕获”类型的捕获。
    handlers encoded_type_addr_pair[abs(size)] 明确类型异常处理列表
    catch_all_addr uleb128 “全部捕获”处理程序的字节码地址。只有当 size 为非正数时,此元素才会存在。
    • encoded_type_addr_pair格式
    名称 格式 说明
    type_idx uleb128 要捕获的异常类型在type_ids列表中的索引
    addr uleb128 对应的异常处理程序的字节码的地址
  • static_values_off_: 静态字段初始值列表在文件中的偏移量,如果没有该列表(或者静态字段使用0或者null进行初始化),该值为0;否则该值应该位于data段,采用encoded_array_item指定的格式

例子

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        final TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                tv.setText(stringFromJNI());
                
            }
        });
        testClassLoader();

    }

    private void testClassLoader() {
        try {
            ClassLoader cl = getClassLoader();

            if (cl == null)
                Log.i("wlb", "classload null");

            while (cl != null) {
                Log.i("wlb", cl.toString());
                cl = cl.getParent();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
}

MainActivity为例说明

MainActivity的class_def_item

class_defs_off      : 332668 (0x05137c)
MainActivity在class_ids中的索引:1665
MainActivity的class_def_item偏移:0x05137c + 1665 * 32 = 0x05E39C
==============================================================================
0005e390  50 10 29 00 78 65 08 00  00 00 00 00 36 08 00 00  |P.).xe......6...|
0005e3a0  01 00 00 00 2b 06 00 00  00 00 00 00 9f 15 00 00  |....+...........|
0005e3b0  00 00 00 00 92 65 08 00  00 00 00 00 1e 02 00 00  |.....e..........|
==============================================================================
类在type_ids中的索引:0x0836 (0x1be74d 'Lcom/tracemaker/nativedynamiclinkdemo/MainActivity;')
类的访问标志: 0x00000001
超类在type_ids中的索引: 0x062b
文件(包含该类)名称在string_ids列表中的索引: 0x0000159f (0x001c25fe 'MainActivity.java')
类数据在文件中的偏移量: 0x00086592

MainActivity的class_data

Android Dex文件_第2张图片
  • 静态字段以及实例字段数量均为0
  • 直接方法数为3 (, , testClassLoader)
  • 虚方法数为2 (onCreate, stringFromJNI)

MainActivitytestClassLoader方法

testClassLoader对应的encoded_method十六进制数据06 02 8c 9a 55

  • 方法在method_ids列表中的索引为: 0x439e (0x4397 + 0x1 + 0x6)
  • 方法的访问标志: 0x0002
  • 方法的代码数据在文件中的偏移: uleb128(8c 9a 55) = 0x154D0C

testClassLoader方法的代码数据

Android Dex文件_第3张图片
  • 代码使用的寄存器数量: 0x0005
  • 入参数量: 0x0001
  • 出参数量: 0x0001
  • 方法的try_items项数: 0x0001
  • 方法的指令数: 0x00000025(37个16位代码单元,74个字节)
  • 方法的字节码
154d0c:                                        |[154d0c] com.tracemaker.nativedynamiclinkdemo.MainActivity.testClassLoader:()V
154d1c: 6e10 9a43 0400                         |0000: invoke-virtual {v4}, Lcom/tracemaker/nativedynamiclinkdemo/MainActivity;.getClassLoader:()Ljava/lang/ClassLoader; // method@439a
154d22: 0c00                                   |0003: move-result-object v0
154d24: 3900 0b00                              |0004: if-nez v0, 000f // +000b
154d28: 1b02 244a 0000                         |0006: const-string/jumbo v2, "wlb" // string@00004a24
154d2e: 1b03 e522 0000                         |0009: const-string/jumbo v3, "classload null" // string@000022e5
154d34: 7120 113e 3200                         |000c: invoke-static {v2, v3}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;)I // method@3e11
154d3a: 3800 1500                              |000f: if-eqz v0, 0024 // +0015
154d3e: 1b02 244a 0000                         |0011: const-string/jumbo v2, "wlb" // string@00004a24
154d44: 6e10 4044 0000                         |0014: invoke-virtual {v0}, Ljava/lang/Object;.toString:()Ljava/lang/String; // method@4440
154d4a: 0c03                                   |0017: move-result-object v3
154d4c: 7120 113e 3200                         |0018: invoke-static {v2, v3}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;)I // method@3e11
154d52: 6e10 0444 0000                         |001b: invoke-virtual {v0}, Ljava/lang/ClassLoader;.getParent:()Ljava/lang/ClassLoader; // method@4404
154d58: 0c00                                   |001e: move-result-object v0
154d5a: 28f0                                   |001f: goto 000f // -0010
154d5c: 0d01                                   |0020: move-exception v1
154d5e: 6e10 0844 0100                         |0021: invoke-virtual {v1}, Ljava/lang/Exception;.printStackTrace:()V // method@4408
154d64: 0e00                                   |0024: return-void

testClassLoader方法try_item

  • try代码块的起始地址: 0x0000(也就是0x154d1c)
  • try代码块覆盖的16位代码单元的数量: 0x001e
  • 关联的encoded_catch_hander_list开头部分到此条目的encoded_catch_handler的偏移量: 0x0001(1字节)
  • encoded_catch_handler_list列表大小:0x01
  • 捕获异常类型的数量:0x01
  • 异常类型在type_ids列表中的索引: uleb128(e8 10)
异常类型字符串:
001bf410  15 4c 6a 61 76 61 2f 6c  61 6e 67 2f 45 78 63 65  |.Ljava/lang/Exce|
001bf420  70 74 69 6f 6e 3b 00 11  4c 6a 61 76 61 2f 6c 61  |ption;..Ljava/la|
  • 异常处理程序的字节码的地址: 0x20(实际0x154d5c)

附录OAT文件格式

Android Dex文件_第4张图片

参考

  1. https://source.android.com/devices/tech/dalvik/dex-format?hl=zh-cn
  2. Hiding Behind ART

你可能感兴趣的:(Android Dex文件)