編輯:關於Android編程
Java源文件通過Java編譯器生成CLASS文件,再通過dx工具轉換為classes.dex文件。
DEX文件從整體上來看是一個索引的結構,類名、方法名、字段名等信息都存儲在常量池中,這樣能夠充分減少存儲空間,相較於Java字節碼文件更適合手機設備。
DEX文件的相關結構聲明定義在/dalvik/libdex/DexFile.h文件中,下面我們先來看一下DEX文件中使用到的數據結構。
DEX文件的基本結構如下圖所示:
header是DEX文件頭,包含magic字段、alder32校驗值、SHA-1哈希值、string_ids的個數以及偏移地址等。DEX文件的頭結構很固定,占用0x70個字節,具體定義代碼如下所示(摘自DexFile.h):
/*
* Direct-mapped "header_item" struct.
*/
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;
};
magic[8]:共8個字節。目前為固定值dex\n035。
checksum:文件校驗碼,使用alder32算法校驗文件除去magic、checksum外余下的所有文件區域,用於檢查文件錯誤。
signature:使用 SHA-1算法hash除去magic,checksum和signature外余下的所有文件區域 ,用於唯一識別本文件 。
fileSize:DEX文件的長度。
headerSize:header大小,一般固定為0x70字節。
endianTag:指定了DEX運行環境的cpu字節序,預設值ENDIAN_CONSTANT等於0x12345678,表示默認采用Little-Endian字節序。
linkSize和linkOff:指定鏈接段的大小與文件偏移,大多數情況下它們的值都為0。link_size:LinkSection大小,如果為0則表示該DEX文件不是靜態鏈接。link_off用來表示LinkSection距離DEX頭的偏移地址,如果LinkSize為0,此值也會為0。
mapOff:DexMapList結構的文件偏移。
stringIdsSize和stringIdsOff:DexStringId結構的數據段大小與文件偏移。
typeIdsSize和typeIdsOff:DexTypeId結構的數據段大小與文件偏移。
protoIdsSize和protoIdsSize:DexProtoId結構的數據段大小與文件偏移。
fieldIdsSize和fieldIdsSize:DexFieldId結構的數據段大小與文件偏移。
methodIdsSize和methodIdsSize:DexMethodId結構的數據段大小與文件偏移。
classDefsSize和classDefsOff:DexClassDef結構的數據段大小與文件偏移。
dataSize和dataOff:數據段的大小與文件偏移。
下面我們來看某apk中classes.dex的解析結果,確實與上面的結構一致:
Dalvik虛擬機解析DEX文件的內容,最終將其映射成DexMapList數據結構,它實際上包含所有其他區段的結構大綱。DexHeader中的mapOff字段指明了DexMapList結構在DEX文件中的偏移。具體定義代碼如下所示:
struct DexMapList {
u4 size; /* DexMapItem的個數 */
DexMapItem list[1]; /* DexMapItem的結構 */
};
struct DexMapItem {
u2 type; /* kDexType開頭的類型 */
u2 unused; /* 未使用,用於字節對齊 */
u4 size; /* type指定類型的個數,它們在dex文件中連續存放 */
u4 offset; /* 指定類型數據的文件偏移 */
};
/* 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,
};
下面我們來看一下010Editor對某classes.dex文件的解析出的DexMapList結構。上面DexMapList結構中的size字段表示list數組的成員個數,即DexMapItem結構的數量:圖中是11h,表示共有17個DexMapItem結構,與圖中的list數組大小相符。
然後我們再來看下DexMapItem的結構。例如對於下圖中的DexMapItem的第一項來說,type等於0說明其是kDexTypeHeaderItem類型的結構;unused一般都為0;size為1代表該結構僅有一個,即只有一個Dex文件頭;offset為0代表Dex文件頭從0h開始。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="image" src="/uploadfile/Collfiles/20160627/20160627092154831.png" title="\" />
最後我們將所有DexMapItem結構整理成下表:
可以看出,其中區段的offset與header中的off是完全相等的。
struct DexStringId {
u4 stringDataOff; /* 字符串數據偏移 */
}
DexStringId結構只有一個stringDataOff字段,直接指向字符串數據。這個區段中包含了DEX文件中用到的所有字符串。
struct DexTypeId {
u4 descriptorIdx; /* 指向 DexStringId列表的索引 */
};
descriptorIdx為指向DexStringId列表的索引,它對應的字符串代表了具體類的類型(DEX文件中用到的所有基本數據類型和類的名稱)。如下圖中的第一項值為0xAEB,表示其是DexStringId中第0xAEB(2795)項;而第8項值為0x1969,表示其是DexStringId中第0x1969(6505)項。經過我們的驗證,以上分析是正確的。
struct DexProtoId {
u4 shortyIdx; /* 指向DexStringId列表的索引 */
u4 returnTypeIdx; /* 指向DexTypeId列表的索引 */
u4 parametersOff; /* 指向DexTypeList的偏移 */
}
struct DexTypeList {
u4 size; /* 接下來DexTypeItem的個數 */
DexTypeItem list[1]; /* DexTypeItem結構 */
};
struct DexTypeItem {
u2 typeIdx; /* 指向DexTypeId列表的索引 */
};
下面結合實例進行分析:
DexProtoId
shortyIdx:方法聲明字符串,具體而言是由方法的返回類型與參數列表組成的一個字符串,並且返回類型位於參數列表的前面。如“III”“V”“VI”“VL”等。在下圖的三個方法聲明中分別為B、BL、DL。
returnTypeIdx:方法返回類型,指向DexTypeId列表。下圖的分別為byte、byte、double。
parametersOff:指向一個DexTypeList結構體,存放了方法的參數類型。下圖分別為0、0x4BA78C、0x4BA7BC。值為0表示參數為void。
DexTypeList
size:DexTypeItem的個數,即參數的數量。下圖分別為?、1、1,表示後兩個方法都只有一個參數。 list:指向size個DexTypeItem項,每一項代表方法的一個參數。DexTypeItem
typeIdx:指向DexTypeId列表,最終指向參數類型的字符串。如第三圖61h(97)項在DexTypeId列表中正好指向”Landroid/content/Context;”類型字符串。DexFieldId結構中的數據全部是索引值,指明了字段所在的類、字段的類型以及字段名。
struct DexFieldId {
u2 classIdx; /* 類的類型,指向DexTypeId列表的索引 */
u2 typeIdx; /* 字段類型,指向DexTypeId列表的索引 */
u4 nameIdx; /* 字段名,指向DexStringId列表的索引 */
};
如下圖,可以看到字段所屬類名為MTT.ThirdAppInfoNew,字段類型為int,字段名為iCoreType。
DexMethodId結構中的數據全部是索引值,指明了方法所在的類、方法的聲明以及方法名。
struct DexMethodId {
u2 classIdx; /* 類的類型,指向DexTypeId列表的索引 */
u2 protoIdx; /* 聲明類型,指向DexProtoId列表的索引 */
u4 nameIdx; /* 方法名,指向DexStringId列表的索引 */
};
如下圖,可以看到方法所屬類為MTT.ThirdAppInfoNew,方法聲明為V,方法名為。
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結構的偏移 */
};
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 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]; /* 指令集 */
DexClassDefDexClassData
header:一個DexClassDataHeader結構,指定字段與方法的個數。如下圖中的staticFieldsSize為0,表示沒有靜態字段;instanceFieldsSize為0xC,表示有12個實例字段;directMethodsSize為0x1,表示有一個直接方法;virtualMethodsSize為0x0,表示沒有虛方法。
staticFields*:指向一個DexField結構,表示靜態字段的類型與訪問標志。由於本例沒有靜態字段,因此該結構無效。
directMethods*:指向一個DexField結構,表示實例字段的類型與訪問標志。如下圖本例中有一個實例字段。directMethods*:指向一個DexMethod結構,表示直接方法的原型、名稱、訪問標志、代碼數據塊。。如下圖本例中有12個實例字段。
virtualMethods*:指向一個DexMethod結構,表示虛方法的原型、名稱、訪問標志、代碼數據塊。由於本例沒有靜態字段,因此該結構無效。
DexField
fieldIdx:指向DexFieldId的索引,表示字段的所屬類、字段類型和字段名。 accessFlags:訪問標志。DexMethod
methodIdx:指向DexMethodId的索引,表示方法的所在類、方法的聲明和方法名。 accessFlags:訪問標志。 codeOff:指向DexCode結構的偏移,圖中為0x138A34。DexCode
registersSize:該方法使用的寄存器個數。下圖中為3。 insSize:該方法的參數個數,對應smali語法中的”.register”指令。下圖中為1。 outsSize:該方法調用其他方法時,對應smali語法中的”.paramter”指令。例如現在有一個方法,使用了5個寄存器,其中有2個為參數,而該方法調用了另一個方法,後者使用了20個寄存器,那麼Dalvik虛擬機在分配時,會在分配自身方法寄存器空間時加上那20個寄存器空間。下圖中為1。 triesSize:方法中Try/Catch的個數。 debugInfoOff:如果dex文件保留了調試信息,debugInfoOff字段會指向它。 insnsSize:指令個數,以2字節為單位。 insns[1]:真正的指令部分。在看本文之前,如果你對於Android的廣播機制不是很了解,建議先行閱讀我轉載的一篇博文:圖解 Android 廣播機制。 由於本案例比較簡單,故直接在此貼出代碼,不做
一、廣播發送者&廣播接收者介紹1.廣播接收者廣播接收者簡單地說就是接收廣播意圖的Java類,此Java類繼承BroadcastReceiver類,重寫:public vo
開門見山:這裡給出rk 在cameraHAL層的camera數據結構:typedef struct FramInfo{ int phy_addr; int v
微信對話列表滑動刪除效果很不錯的,借鑒了github上SwipeListView(項目地址:https://github.com/likebamboo/SwipeList