1. 程式人生 > >Android Dex檔案格式解析

Android Dex檔案格式解析

Dex檔案是Android虛擬機器下的可執行檔案,包含了應用程式所用到所有操作指令和執行時資料。在程式編譯過程中,java原始檔先被編譯成class檔案,然後通過dx工具將多個class檔案整合為一個dex檔案。這樣的檔案結構使得各個類能夠共享資料,充分減少了儲存空間,提升了執行效率。

Java原始檔生成Dex檔案的對映關係。

dex檔案的結構如下圖:

我們可以在android原始碼中的/dalvik/libdex/DexFile.h找到關於dexfile的定義。
Andre

這裡定義的DexFile是dex檔案被對映到記憶體中的結構,出了基本的dex檔案結構外,還包含了DexOptHead和尾部附加的資料,這些資料是Android系統為了結合當前平臺特性對dex檔案的結構進行了優化和擴充,是執行效率更高。

/*
 * Structure representing a DEX file.
 *
 * Code should regard DexFile as opaque, using the API calls provided here
 * to access specific structures.
 */
typedef struct DexFile {
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;

    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;

    /* mapped in "auxillary" section */
    const DexClassLookup* pClassLookup;

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    void*               auxData;
} DexFile;
名稱 格式 描述
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[] 型別資料索引,記錄了各個型別的字串索引。
type_ids ubyte[] 資料區,儲存著各個類的資料。
link_data ubyte[] 靜態連線數據。

header是dex檔案的檔案頭,簡單的記錄了dex檔案的一些基本資訊和大致的資料分佈。header的大小固定為0x70,其中每一項資訊所佔用的大小也是固定的。同樣在/dalvik/libdex/DexFile.h可以看到header_item的定義:
/*

 * Direct-mapped "header_item" struct.
 */
typedef struct DexHeader {
    u1  magic[8];           /* includes version number */
    u4  checksum;           /* adler32 checksum */
    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
    u4  fileSize;           /* length of entire file */
    u4  headerSize;         /* offset to start of next section */
    u4  endianTag;          /*位元組序標號*/
    u4  linkSize;
    u4  linkOff;
    u4  mapOff;
    u4  stringIdsSize;
    u4  stringIdsOff;
    u4  typeIdsSize;
    u4  typeIdsOff;
    u4  protoIdsSize;
    u4  protoIdsOff;
    u4  fieldIdsSize;
    u4  fieldIdsOff;
    u4  methodIdsSize;
    u4  methodIdsOff;
    u4  classDefsSize;
    u4  classDefsOff;
    u4  dataSize;
    u4  dataOff;
} DexHeader;

下表是每一項的基本資訊:

名稱 格式 描述
magic ubyte[4] dex\n
version Ubyte[4] 035\0 或 036\0
checksum uint 使用zlib 的adler32 所計算的32-bitsCheckSum . 計算的範圍為DEX 檔案的長度(Header->fileSize) 減去8bytes Magic Code 與4bytes CheckSum 的範圍. 用來確保DEX 檔案內容沒有損毀.
signature ubyte[20] SHA-1signature (hash) 用來識別原本的DEX 檔案( 被優化以前的DEX).SHA-1 計算的範圍為DEX 檔案的長度(Header->fileSize) 減去8bytes Magic Code,4 bytes CheckSum 與20bytesSHA-1 的範圍. 用來識別最原本的DEX 檔案的唯一性.( 所以被優化過後, 這個SHA-1 盡能用來識別原本的DEX 檔案, 而無法 通過ODEX 檔案計算回最原本的DEX 檔案SHA-1 值了).
file_size uint 檔案的總大小。
header_size uint= 0x70 用來記錄目前DEXHeader 的大小( 現有版本為0x70bytes), 可用來做為後續Header 如果有改版時, 最基本的頭欄位向前, 向後相容的依據.
endian_tag uint= ENDIAN_CONSTANT 預設值為Little-Endian, 在這欄位會顯示32bits 值”0x12345678”.( 應該在Big-Endian 處理器上, 會轉為“ 0x78563412”)
link_size uint LinkSection 的大小, 如果為0 表示該DEX 檔案不是靜態連結.
link_off uint 用來表示LinkSection 距離Dex 頭的Offset. 如果LinkSize 為0, 此值也會為0. 資料格式可以參考struct DexLink.
map_off uint 用來表示MapItem 距離Dex 頭的Offset. 如果為0, 表示這DEX 檔案沒有MapItem. 資料格式可以參考struct map_list.
string_ids_size uint 字串地址列表中的元素個數。
string_ids_off uint 字串地址列表的偏移。
type_ids_size uint 用來表示TypeIDs List 中的元素個數.
type_ids_off uint TypeIDs List 的檔案偏移。如果type_ids_size 為0 ,則這個值亦為0。
proto_ids_size uint 用來表示PrototypeIDs List 中的元素個數。
proto_ids_off uint Prototype IDs 的檔案偏移。如果proto_ids_size 為0 這個值亦為0.
field_ids_size uint Field IDs List的檔案偏移。如果field_ids_size 為0 這個值亦為0。
field_ids_off uint 字串地址列表的偏移。
method_ids_size uint 用來表示MethodIDs List 中的元素個數。
method_ids_off uint 用來表示Method IDs List 距離Dex 頭的Offset. 如果method_ids_size 為0 這個值亦為0. 資料格式可以參考struct DexMethodId.
class_defs_size uint 用來表示ClassDefinitions List 的總數.
class_defs_off uint 用來表示ClassDefinitionList 距離Dex 頭的Offset. 如果class_defs_size 為0 這個值亦為0. 資料格式可以參考struct DexClassDef.
data_size uint 用來表示DataSection 的大小. 並需為sizeof(uint) 的偶數倍.( 所以就是0,8,16…etc)
data_off uint 用來表示DataSection 距離Dex 頭的Offset.


在header中有一個map_off,這是一個偏移地址,指向dex data區的maplist。

/*
 * Direct-mapped "map_item".
 */
typedef struct DexMapItem {
    u2  type;              /* type code (see kDexType* above) */
    u2  unused;
    u4  size;              /* count of items of the indicated type */
    u4  offset;            /* file offset to the start of data */
} DexMapItem;

/*
 * Direct-mapped "map_list".
 */
typedef struct DexMapList {
    u4  size;               /* #of entries in list */
    DexMapItem list[1];     /* entries */
} DexMapList;

map_list 裡先用一個 uint 描述後面有 size 個 map_item , 後續就是對應的 size 個 map_item 描述 。map_item 結構有 4 個元素 : type 表示該 map_item 的型別 ,本節能用到的描述如下 ,詳細Dalvik
Executable Format 裡 Type Code 的定義 ;size 表示再細分此 item , 該型別的個數 ;offset 是第一個元素的針對檔案初始位置的偏移量 ; unuse 是用對齊位元組的 ,無實際用處 。在DexMapItem結構中,type是一個列舉常量。

/* 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,
 };

string_ids

這一塊區域中儲存的是dex檔案中字串的資源的索引資訊。即目標字串在dex檔案資料區的物理偏移地址。在DexFile.h中可以找到它的定義:

struct DexStringId {
    u4 stringDataOff; /* 在dex檔案中實際偏移量 */
};

該資料結構只有一個stringDataOff成員,當虛擬機器需要讀取該字串是,只需要將dex檔案在記憶體中的起始地址加上stringDataOff所表示的偏移值,即可得到該字串在記憶體中的實際實體地址。

在Dex檔案中,每個字串都對應了一個DexStringId資料結構,該資料結構的大小為4B,是一個確定的量。並且虛擬機器可以通過標頭檔案中的String_ids_size知道當前Dex檔案的字串總數,也就是string_ids區域中DexStringId的總數,因此虛擬機器通過簡單的乘法即可實現對改索引資源進行正確的訪問。

在data區存放的string並不是ascii編碼,而是MUTF-8編碼。這是一種修改過的UTF-8編碼,與傳統的UTF-8編碼有以下幾點區別:

  • MUTF-8使用1-3位元組編碼
  • 大於16位的Unicode編碼U+10000~U+10ffff使用3位元組來編碼
  • U+0000採用2位元組來編碼
  • 採用空字元作為字串的結尾
    MUTF-8字串的頭部存放的是由uleb128編碼的字串個數(字串個數不包括結尾的空字元)。

type_ids

這一塊區域中儲存的是型別資源的索引資訊。定義如下:

struct DexTypeId {
    u4 descriptorIdx; /* 指向字串索引表 */
};

在dex中,型別是是以字串的形式儲存在資料區的,因此DexTypeId資料結構中的descriptorIdx儲存的是目標型別在字串索引表裡的序列號。虛擬機器通過這個序列號從字串索引表中找出對應型別的字串。

proto_ids

這一區域中儲存的內容是方法原型資源的索引資訊,資料結構DexProtoId負責規格化這些資訊。

struct DexProtoId {
    u4 shortyIdx; /* 方法宣告字串,指向字串索引表 */
    u4 returnTypeIdx; /* 方法返回型別,指向字串索引表 */
    u4 parametersOff; /* 指向一個DexTypeList的資料結構,DexTypeList表示引數列表*/

};

這個資料結構的前兩個成員同樣指向字串索引表,而parametersOff指向的是一個DexTypeList的資料結構,這個結構體的定義如下:

/*
* Direct-mapped "type_list".
*/
typedef struct DexTypeList {
     u4  size;               /* 表示DexTypeItem資料結構的個數 */
     DexTypeItem list[1];    /* DexTypeItem列表*/
} DexTypeList;

DexTypeList表示的是一個方法的引數列表,size是引數個數,list這個陣列是一個DexTypeItem的陣列,這個DexTypeItem就是表示引數型別的資料結構。

/*
 * Direct-mapped "type_item".
*/
typedef struct DexTypeItem {
    u2  typeIdx;            /* 表示引數型別,指向type_ids索引表 */
} DexTypeItem;

typeIdx指向型別資源索引表,虛擬機器通過該變亮就能獲得相應的引數型別。

field_ids

這一塊區域儲存的是程式碼中的欄位的索引資訊。用資料結構DexFieldId對索引資訊規格化。

struct DexFieldId {
    u2 classIdx; /* 表示所屬類的型別 */
    u2 typeIdx; /*  表示欄位的型別 */
    u4 nameIdx; /*  欄位的名稱 */
};

在DexFieldId中,classIdx和typeIdx都是指向型別索引表type_ids中的一個DexTypeId表項。而nameIdx是指向字串索引表string_ids中的一個表項。

method_ids

method_ids資源區儲存的是Dex檔案中類方法資料的索引資訊。採用的資料結構為

struct DexMethodId {
    u2 classIdx; /* 表示所屬類的型別 */
    u2 protoIdx; /* 表示方法原型的型別 */
    u4 nameIdx; /* 方法名稱 */
};

classIdx指向型別索引表type_ids中的一個DexTypeId,變數protoIdx記錄的該方法的原型,指向proto_ids表。nameIdx指向字串表string_ids,表示方法名稱。

class_def

在class_def資源區中,使用資料結構DexClassDef來對資源資訊規格化。

struct DexClassDef {
    u4 classIdx; /* 表示類的型別,指向型別索引表 */
    u4 accessFlags; /* 訪問識別符號 */
    u4 superclassIdx; /* 表示超類的型別,指向型別索引表 */
    u4 interfacesOff; /* 介面資訊,指向一個DexTypeList資料結構*/
    u4 sourceFileIdx; /* 表示原始檔名,指向字串索引表 */
    u4 annotationsOff; /* 註解目錄結構 */
    u4 classDataOff; /* 類資料,指向一個DexClassData資料結構*/
    u4 staticValuesOff; /* 靜態值得偏移量 */
    };

訪問識別符號,表示了該類的屬性,同樣也適用於欄位和方法,具體如下表:

AccessFlag位元位 類(Class) 方法(Method) 域(Field)
0x00001 Public Public Public

相關推薦

Android Dex檔案格式解析(第二篇)

1 .DEX檔案中使用的資料型別 u1,u2,u4,u8表示佔某固定位元組的無符號數 sleb128表示有符號的LEB128型別資料,uleb128表示無符號的LEB128,uleb128p1表示無符號的LEB128+1 , 關於LEB128:

Android Dex檔案格式解析

Dex檔案是Android虛擬機器下的可執行檔案,包含了應用程式所用到所有操作指令和執行時資料。在程式編譯過程中,java原始檔先被編譯成class檔案,然後通過dx工具將多個class檔案整合為一個dex檔案。這樣的檔案結構使得各個類能夠共享資料,充分減少了儲存空間,提

Android安全/安全技術--21--基礎檔案格式解析

4-1、so檔案格式解析 1、ELF檔案格式 Android中的so檔案就是ELF檔案,瞭解so檔案首先需要了解ELF檔案的格式,使用工具為readelf,常用命令如下: 1、檢視so檔案的頭部資訊 readelf -h xxx.so //

android .dex檔案探究

在我們寫Java程式碼的時候,生成的檔案是.java檔案,但是JVM並不識別這個,所以會先轉成class檔案,而在Android端,Android上的Davlik虛擬機器能執行.dex。所以dex檔案中包含了所有的app程式碼,可利用反編譯工具獲取java程式碼。 即dex檔案就是A

pcd,obj,mtl檔案格式解析

pcd,obj,mtl檔案格式解析 pcd檔案解析 PCD檔案格式並非白費力氣地做重複工作,現有的檔案結構因本身組成的原因不支援由PCL庫引進n維點型別機制處理過程中的某些擴充套件,而PCD檔案格式能夠很好地補足這一點。PCD不是第一個支援3D點雲資料的檔案型別,尤其是計算機圖形

[譯]Dex檔案格式

Dex檔案格式 原文連結:https://blog.bugsnag.com/dex-and-d8/ 你是否好奇Android應用是如何編譯和到爆陳apk的呢?本文將以微型Dex檔案的實際例子深入Dalvik 可執行格式。 Dex檔案是什麼? Dex檔案包含最終由Androi

Windows快捷方式檔案格式解析

轉載自:https://blog.csdn.net/cosmoslife/article/details/51898534 大家知道通過IShellLink介面可以得到快捷方式的各種屬性。具體怎麼做,網上有很多文章,這裡就不介紹了。現在主要是分析一下快捷方式檔案的格式,並且自己寫一個解析程式。

淺談 Android Dex 檔案

概述 為什麼要了解 Dex 檔案 瞭解了 Dex 檔案以後,對日常開發中遇到一些問題能有更深的理解。如:APK 的瘦身、熱修復、外掛化、應用加固、Android 逆向工程、64 K 方法數限制。 什麼是 Dex 檔案 在明白什麼是 Dex 檔案之前,要先了解一下 JVM,Dalvik 和 ART。JV

mysql .par檔案格式解析

mysql 5.6版本分割槽表有一個檔案:表名.par, 該檔案在5.7.6版本後被移除。 在一個現場環境中,客戶端執行check table後報錯如下,原始碼跟蹤下來之後是缺失par檔案。   mysql解析par檔案的呼叫堆疊如下: (gdb) bt #0 &

封裝格式---FLV---檔案格式解析

轉自:https://wuyuans.com/2012/08/flv-format  flv檔案主要由兩部分組成:header和body。   1.header header部分記錄了flv的型別、版本等資訊,是flv的開頭,一般都差不多,佔9bytes。具體格式如下:

/proc/$pid/maps檔案格式解析

/proc/pid/maps檔案格式解析 以下內容摘錄在man手冊,可以通過執行命令(man 5 proc)獲得。 /proc/[pid]/maps A file containing the currently mapped memory regions a

Android dex檔案通用自動脫殼器

標 題: 【原創】Android dex檔案通用自動脫殼器作 者: zyqqyz時 間: 2015-09-01,13:03:30鏈 接: http://bbs.pediy.com/showthread.php?t=203776之前做了一個Android dex的通用脫殼器

Java Class檔案格式解析及例項

JAVA無關性概述 Java語言從剛誕生開始曾提出一個非常著名的宣言:“一次編寫,到處執行(Write Once, Run Anywhere)”。Sun公司和其他虛擬機器公司釋出了許多可以執行在不同作業系統上的虛擬機器,這些虛擬機器都可以載入和執行同一種平臺無關的位元組碼,

Mp3原理及檔案格式解析

1.引言 文 件壓縮技術的日新月異使得MP3成為時下最燙手的音樂格式,優質的音樂隨著0與1的排列迅速散佈到世界各地,撼動人心。何謂MP3?MP3的全稱是 MPEG Audio Layer 3,它是一種高效的計算機音訊編碼方案,它以較大的壓縮比將音訊檔案轉換成較小的副檔名為.MP3的檔案,基本保持原檔案的音質

Dex 檔案格式詳解

/* * 解析 Dex 檔案 */ #include "dex.h" #include <sstream> #include <string> #include <iostream> void DexFile::parseAllClass() { for (u4

ts檔案格式解析

TS格式解析 by ahuner 1.TS格式介紹    TS:全稱為MPEG2-TS。TS即"Transport Stream"的縮寫。它是分包傳送的,每一個包長為188位元組(還有192和204個位元組的包)。包的結構為,包頭為4個位元組(第一個

Hex檔案格式解析

  第一行,是Extended Linear Address Record,裡面的資料,也就是基地址是0x0004,第二行是Data Record,裡面的地址值是0x0000。那麼資料18F09FE518F09FE518F09FE518F09FE5要寫入FLASH中的地址為 (0x0004 <<

gml檔案格式解析程式詳解之原始檔

// Functions to read a network stored in a GML file into a NETWORK struct // // Mark Newman 11 AUG 06 // // To use this software, #inclu

C++PE檔案格式解析類(輕鬆製作自己的PE檔案解析器)

PE是Portable Executable File Format(可移植的執行體)簡寫,它是目前Windows平臺上的主流可執行檔案格式。 PE檔案中包含的內容很多,具體我就不在這解釋了,有興趣的可以參看之後列出的參考資料及其他相關內容。 最近我也在學習PE檔案格式,參

TS 檔案格式解析

https://blog.csdn.net/cabbage2008/article/details/49281729TS 流都是固定等長的188位元組包如下圖所示 用UltraEdit開啟的一個TS流,我們發現每隔188個位元組就有一個47(可以看做是包頭)TS的包頭佔用四個