序 整个dex文件可以看作一个dex结构体的实例, 结构体使用 c 语言实现
dex结构体的源码位于
art/libdexfile/dex/dex_file.h
(新目录)
dalvik/libdex/DexFile.h
(较旧版本)
了解dex结构体是是否有必要的, 特别在加固与脱壳的领域
整体参考图(与现版本有些许差异)
对应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct DexFile { const DexOptHeader* pOptHeader; const DexHeader* pHeader; const DexStringId* pStringIds; const DexTypeId* pTypeIds; const DexFieldId* pFieldIds; const DexMethodId* pMethodIds; const DexProtoId* pProtoIds; const DexClassDef* pClassDefs; const DexLink* pLinkData; const DexClassLookup* pClassLookup; const void * pRegisterMapPool; const u1* baseAddr; int overhead; };
使用工具010 editor的模板功能, 能够十分有效的帮助我们学习dex结构体
使用以下源码得到的dex文件作为学习案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Hello { private int number; public Hello (int number) { this .number = number; } public int getNumber () { return number; } public void setNumber (int number) { this .number = number; } public static int add (int a, int b) { return a + b; } public static void main (String[] args) { Hello ahello = new Hello (10 ); int result = add(5 , 3 ); System.out.println("The result of addition is: " + result); System.out.println("The number in the object is: " + ahello.getNumber()); } }
通过以下指令得到目标文件
1 2 javac ./Hello.java AndroidSDKVersion/d8.bat --ouput . .\Hello.class
使用010editor打开
leb128 为了节省内存dex文件中会对部分整数使用可变长度编码
名称
说明
sleb128
有符号 LEB128,可变长度(见下文)
uleb128
无符号 LEB128,可变长度(见下文)
uleb128p1
无符号 LEB128 加 1
,可变长度(见下文)
每个 LEB128 编码值均由 1-5 个字节组成,共同表示一个 32 位的值。每个字节均已设置其最高有效位(序列中的最后一个字节除外,其最高有效位已清除)。每个字节的剩余 7 位均为载荷,即第一个字节中有 7 个最低有效位,第二个字节中也是 7 个,依此类推。
对于有符号 LEB128 (sleb128
),序列中最后一个字节的最高有效载荷位会进行符号扩展,以生成最终值。在无符号情况 (uleb128
) 下,任何未明确表示的位都会被解译为 0
。
变体 uleb128p1
用于表示一个有符号值,其表示法是编码为 uleb128
的值加 1。这使得 -1
的编码(或被视为无符号值 0xffffffff
)成为一个单字节(但没有任何其他负数),并且该编码在下面这些情况下非常实用:所表示的数值必须为非负数或 -1
(或 0xffffffff
);不允许任何其他负值(或不太可能需要使用较大的无符号值)。
编码序列
As sleb128
As uleb128
编码为 uleb128p1
00
0
0
-1
01
1
1
0
7f
-1
127
126
80 7f
-128
16256
16255
header用于描述dex文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct DexHeader { u1 magic[8 ]; u4 checksum; u1 signature[20 ]; u4 fileSize; u4 headerSize; 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
magic字段格式一般是
其中035会随着dex文件版本发生变化
例如也有可能是
checksum
用于校验dex完整性
signature
20字节的SHA-1哈希
file_size
整个文件的大小
header_size
头部大小
endian_tag
标识字节序
小端序0x78563412
大端序0x12345678
以上这些在案例中的实例
link
link_size
与 link_off
用于标明链接段
大多数时候皆为空(例如案例中), 不作多关注
map_off DexMapList的偏移
DexMapList描述了整个dex文件的布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 struct DexMapList { u4 size; DexMapItem list [1 ]; }; struct DexMapItem { u2 type; u2 unused; u4 size; u4 offset; }; 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_id string_idx_size
描述DexStringId的个数
string_idx_off
DexStringId的偏移
1 2 3 struct DexStringId { u4 stringDataOff; };
也就是黄色的这部分
其描述的是一个stringData的偏移
例如第一个偏移是0x332
对应内容是
代表该字符串共有6个字符(注意并不是6个字节), 其中不包括结尾的\x00
所有的字符串按顺序排列生成一个索引
type_id type_ids_size
描述DexTypeId的个数
type_ids_off
指向类型描述符id的偏移
1 2 3 4 struct DexTypeId { u4 descriptorIdx; };
内容如下
1 2 3 02 00 00 00 05 00 00 00 08 00 00 00 09 00 00 00 0A 00 00 00 0B 00 00 00 0C 00 00 00 0F 00 00 00 12 00 00 00
数字代表的就是字符串的索引
例如
02 00 00 00
就代表索引2的字符串也就是I
, int型
proto_id proto_ids_size
描述proto_id的个数
proto_ids_off
指向proto_ids的偏移
每一个proto_id的定义如下,用于描述一个方法原型
1 2 3 4 5 struct DexProtoId { u4 shortyIdx; u4 returnTypeIdx; u4 parametersOff; }
第一个字段是一个索引, 对应字符串中一个能描述方法的字符串
例如III
表示int (int, int)
第二个字段就是返回值类型的类型索引
第三个字段是指向一个DexTypeList的偏移
1 2 3 4 5 6 7 8 struct DexTypeList { u4 size; DexTypeItem list [size]; }; struct DexTypeItem { u2 typeIdx; };
例如02 00 00 00 00 00 00 00
代表
共有两个参数, 都位于类型索引表的0
处, 也就是int, int
field_id field_ids_size
描述DexFieldId的个数
field_ids_off
描述DexFieldId所在偏移
1 2 3 4 5 struct DexFieldId { u2 classIdx; u2 typeIdx; u4 nameIdx; };
第一个字段代表所属类
第二个字段代表类型
第三个字段对应字符串表中的索引, 是字段的名称
例如01 00 00 00 17 00 00 00
代表Hello.number字段, 其是一个int
method_id method_ids_size
method_id的个数
method_ids_off
所在偏移
1 2 3 4 5 struct DexMethodId { u2 classIdx; u2 protoIdx; u4 nameIdx; };
第一个字段代表所属类索引
第二个字段代表方法原型声明索引
第三个字段代表方法名字
class_def 整个dex最复杂也是最关键的地方
class_defs_size
描述DexClassDef的个数
class_defs_off
DexClassDef结构所在偏移
1 2 3 4 5 6 7 8 9 10 struct DexClassDef { u4 classIdx; u4 accessFlags; u4 superclassIdx; u4 interfacesOff; u4 sourceFileIdx; u4 annotationsOff; u4 classDataOff; u4 staticValuesOff; };
访问标志枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 enum { ACC_PUBLIC = 0x00000001 , ACC_PRIVATE = 0x00000002 , ACC_PROTECTED = 0x00000004 , ACC_STATIC = 0x00000008 , ACC_FINAL = 0x00000010 , ACC_SYNCHRONIZED = 0x00000020 , ACC_SUPER = 0x00000020 , ACC_VOLATILE = 0x00000040 , ACC_BRIDGE = 0x00000040 , ACC_TRANSIENT = 0x00000080 , ACC_VARARGS = 0x00000080 , ACC_NATIVE = 0x00000100 , ACC_INTERFACE = 0x00000200 , ACC_ABSTRACT = 0x00000400 , ACC_STRICT = 0x00000800 , ACC_SYNTHETIC = 0x00001000 , ACC_ANNOTATION = 0x00002000 , ACC_ENUM = 0x00004000 , ACC_CONSTRUCTOR = 0x00010000 , ACC_DECLARED_SYNCHRONIZED =0x00020000 , ACC_CLASS_MASK =(ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT| ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM), ACC_INNER_CLASS_MASK =(ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC), ACC_FIELD_MASK =(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL| ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM), ACC_METHOD_MASK =(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR| ACC_DECLARED_SYNCHRONIZED), };
annotations 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 struct DexAnnotationSetItem { u4 size; u4 entries[1 ]; }; struct DexFieldAnnotationsItem { u4 fieldIdx; u4 annotationsOff; }; struct DexMethodAnnotationsItem { u4 methodIdx; u4 annotationsOff; }; struct DexParameterAnnotationsItem { u4 methodIdx; u4 annotationsOff; }; struct DexAnnotationSetRefList { u4 size; DexAnnotationSetRefItem list [1 ]; }; struct DexAnnotationSetRefItem { u4 annotationsOff; }; struct DexAnnotationItem { u1 visibility; u1 annotation[1 ]; }; enum { kDexVisibilityBuild = 0x00 , kDexVisibilityRuntime = 0x01 , kDexVisibilitySystem = 0x02 , kDexAnnotationByte = 0x00 , kDexAnnotationShort = 0x02 , kDexAnnotationChar = 0x03 , kDexAnnotationInt = 0x04 , kDexAnnotationLong = 0x06 , kDexAnnotationFloat = 0x10 , kDexAnnotationDouble = 0x11 , kDexAnnotationString = 0x17 , kDexAnnotationType = 0x18 , kDexAnnotationField = 0x19 , kDexAnnotationMethod = 0x1a , kDexAnnotationEnum = 0x1b , kDexAnnotationArray = 0x1c , kDexAnnotationAnnotation = 0x1d , kDexAnnotationNull = 0x1e , kDexAnnotationBoolean = 0x1f , kDexAnnotationValueTypeMask = 0x1f , kDexAnnotationValueArgShift = 5 , };
class_data 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 struct DexClassData { DexClassDataHeader header; DexField* staticFields; DexField* instanceFields; DexMethod* directMethods; DexMethod* virtualMethods; }; struct DexClassDataHeader { u4 staticFieldsSize; u4 instanceFieldsSize; u4 directMethodsSize; u4 virtualMethodsSize; }; struct DexField { u4 fieldIdx; u4 accessFlags; }; struct DexMethod { u4 methodIdx; u4 accessFlags; u4 codeOff; }; struct DexCode { u2 registersSize; u2 insSize; u2 outsSize; u2 triesSize; u4 debugInfoOff; u4 insnsSize; u2 insns[1 ]; }; struct DexTry { u4 startAddr; u2 insnCount; u2 handlerOff; };
staticValues 1 2 3 struct DexEncodedArray { u1 array [1 ]; };
1 2 3 4 5 6 7 8 9 10 11 typedef struct { ubyte value_type; ubyte value[value_type_size(value_type)]; } EncodedValue; typedef struct { uleb128 size; EncodedValue values[size]; } EncodedArray;
data data_size
数据段大小
data_off
数据段偏移
参考 Dalvik 可执行文件格式 | Android Open Source Project
dex_file.h - Android Code Search