標籤:
- 整個檔案的布局
- 檔案頭
- 索引區
- string_ids
- type_ids
- proto_ids
- field_ids
- method_ids
- 資料區
- 附錄
整個檔案的布局
整個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
Dalvik虛擬機器【2】——Dex檔案格式