part-2

ProtoIds

ProtoIds 存放的是原型的声明,包括原型简名、参数和返回值。
ProtoIds 的大小和文件偏移在 DexHeader 和 map_list 中都有指定。

结构

ProtoIds 以4字节对齐,即总大小为 4 * 3 * protoIdsSize .

1
2
3
4
5
6
7
8
/*
* Direct-mapped "proto_id_item".
*/
struct DexProtoId {
u4 shortyIdx; /* 指向 stringIds 的索引,表示简名 */
u4 returnTypeIdx; /* 指向 TypeIds 的索引,表示返回类型 */
u4 parametersOff; /* 指向 type_list 的文件偏移,表示参数列表,为 0 表示没有参数,参数类型中没有空*/
};
1
2
3
4
5
6
7
/*
* Direct-mapped "type_list".
*/
struct DexTypeList {
u4 size; /* DexTypeItem 的个数 */
DexTypeItem list[1]; /* DexTypeItem 数组内容 */
};
1
2
3
4
5
6
/*
* Direct-mapped "type_item".
*/
struct DexTypeItem {
u2 typeIdx; /* 指向 typeIds 的索引*/
};

手工查找

某 dex 文件的 ProtoIds 部分

protoIdsSize : 0x12
protoIdsOff : 0x0244

0x0244 ~ 0x031c

1
2
3
4
5
6
7
8
9
00000240 .. .. .. .. 06 00 00 00 00 00 00 00 40 0a 00 00 |)...........@...|
00000250 07 00 00 00 01 00 00 00 00 00 00 00 08 00 00 00 |................|
00000260 01 00 00 00 48 0a 00 00 09 00 00 00 08 00 00 00 |....H...........|
00000270 00 00 00 00 0a 00 00 00 0c 00 00 00 f8 09 00 00 |................|
00000280 09 00 00 00 0f 00 00 00 00 00 00 00 0a 00 00 00 |................|
00000290 14 00 00 00 14 0a 00 00 .. .. .. .. .. .. .. ..
........ .. .. .. .. .. .. .. .. 28 00 00 00 17 00 00 00 |........(.......|
00000300 2c 0a 00 00 27 00 00 00 17 00 00 00 1c 0a 00 00 |,...'...........|
00000310 .. .. .. .. .. .. .. .. 14 0a 00 00

以第一个 DexProtoId 为例,
shortyIdx=0x06 => stringIdOff=0x88 => stringDataOff=0x0544 => IL
returnTypeIdx=0x00 => stringId=0x05 => StringIdOff=0x84 => stringDataOff=0x0541 => I
parametersOff=0x0a40 => 01 00 00 00 18 00 => typeIdOff=0x240 => stringIdOff=0x29 => stringDataOff=0x079c => [B
即此函数声明为 int (Byte[])

以此类推:

序数 原型 返回值 参数 方法
0x00 IL I [B int (Byte[])
0x01 J J long ()
0x02 JL J Ljava/io/File; long (File)
0x03 L Ljava/io/File; File ()
0x04 LL Ljava/io/InputStream; Ljava/util/zip/ZipEntry; InputStream (ZipENtry)
0x05 L Ljava/lang/String; String ()
0x06 LL Ljava/util/zip/ZipEntry; Ljava/lang/String; ZipEntry (String)
0x10 ZLL Z Landroid/app/Application; Ljava/lang/String; bool (Application,String)
0x11 ZL Z Ljava/lang/Object; bool (Object)
0x12 ZL Z Ljava/lang/String; bool (String)

写程序解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def parseProtos(f):
'''
此函数基于 parseStrings 和 parseTypes 函数的结果
'''
cur_pos = 0
for i in range(DexStruct.DexHeader['protoIdsSize']):
f.seek(DexStruct.DexHeader['protoIdsOff'] + cur_pos)
proto_shortyId = struct.unpack('I',f.read(4))[0]
proto_returnTypeId = struct.unpack('I',f.read(4))[0]
proto_paramtersOff = struct.unpack('I',f.read(4))[0]
parameters = []
if proto_paramtersOff > 0: # 有参数再读取
f.seek(proto_paramtersOff)
param_size = struct.unpack('I',f.read(4))[0]
for j in range(param_size):
param_typeId = struct.unpack('H',f.read(2))[0]
parameters.append(DexStruct.DexTypes[param_typeId]['content'])
tmpDexProto = {
'shortyDescription' : DexStruct.DexStrings[proto_shortyId]['content'],
'returnType' : DexStruct.DexTypes[proto_returnTypeId]['content'],
'parameters' : parameters,
};
DexStruct.DexProtos.append(tmpDexProto)
cur_pos += 12

FieldIds

fields 表示的是类中的字段,包括本字段所属的类、字段类型和字段名。
FieldIds 的大小和文件偏移在 DexHeader 和 map_list 中都有指定。

结构

以 4 字节对齐。

1
2
3
4
5
6
7
8
/*
* Direct-mapped "field_id_item".
*/
struct DexFieldId {
u2 classIdx; /* 指向 typeIds 的索引,表示定义这个字段的类 */
u2 typeIdx; /* 指向 typeIds 的索引,表示字段类型 */
u4 nameIdx; /* 指向 stringIds 的索引,表示字段名*/
};

手动查找

某 dex 文件的 FieldIds 部分

fieldIdsSize : 0x01
fieldIdsOff : 0x031c

1
2
00000310 .. .. .. .. .. .. .. .. .. .. .. .. 04 00 0f 00 |'...............|
00000320 04 00 00 00 .. .. .. .. .. .. .. .. .. .. .. .. |................|

由 DexFieldId 可得 :
classIdx=0x0004 => typeId=0x0d => stringDataOff=0x00a4 => Landroid/os/Build;
typeIdx=0x000f => typeId=0x18 => stringDataOff=0x00d0 => Ljava/lang/String;
nameIdx=0x00000004 => stringDataOff=0x0538 => CPU_ABI

写程序解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def parseFields(f):
'''
此函数基于 parseStrings 和 parseTypes 函数的结果
'''
f.seek(DexStruct.DexHeader['fieldIdsOff'])
for i in range(DexStruct.DexHeader['fieldIdsSize']):
field_class = DexStruct.DexTypes[struct.unpack('H',f.read(2))[0]]['content']
field_type = DexStruct.DexTypes[struct.unpack('H',f.read(2))[0]]['content']
field_name = DexStruct.DexStrings[struct.unpack('I',f.read(4))[0]]['content']
tmpDexFieldItem = {
'class' : field_class,
'Type' : field_type,
'name' : field_name,
}
DexStruct.DexFields.append(tmpDexFieldItem)

Methods

MethodId 表示的是方法,包括本方法所属的类、方法原型 (proto) 和方法名。
MethodId 的大小和文件偏移在 DexHeader 和 map_list 中都有指定。

结构

以 4 字节对齐。

1
2
3
4
5
6
7
8
/*
* Direct-mapped "method_id_item".
*/
struct DexMethodId {
u2 classIdx; /* 指向 typeIds,表示定义这个方法的类 */
u2 protoIdx; /* 指向 protoIds,表示方法的原型*/
u4 nameIdx; /* 指向 stringIds,表示方法名 */
};

手动查找

某 dex 文件的 methodIds 部分

methodIdsSize : 0x2b
methodIdsOff : 0x0324

1
2
3
4
00000320 .. .. .. .. 02 00 07 00 03 00 00 00 02 00 08 00 |................|
00000330 30 00 00 00 .. ...
........
00000470 42 00 00 00 15 00 04 00 43 00 00 00

以第一个 02 00 07 00 03 00 00 00 为例:
classIdx=0x02 => Landroid/app/Application;
protoIdx=0x07 => void ()
nameIdx=0x03 => <init>
即该函数为 void android.app.Application.<init>()

以此类推:

序数 所属的类 函数原型 函数名 全称
0x00 Landroid/app/Application; void () void android.app.Application.()
0x01 Landroid/app/Application; void (android.content.Context) attachBaseContext void android.app.Application.attachBaseContext(android.content.Context)
0x2b Ljava/util/zip/ZipFile; InputStream (ZipENtry) getInputStream java.io.InputStream java.util.zip.ZipFile.getInputStream(java.util.zip.ZipEntry)

写程序解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def parseMethods(f):
'''
此函数基于 parseStrings、parseProtos 和 parseTypes 函数的结果
'''
f.seek(DexStruct.DexHeader['methodIdsOff'])
for i in range(DexStruct.DexHeader['methodIdsSize']):
method_class = DexStruct.DexTypes[struct.unpack('H',f.read(2))[0]]['content']
method_proto = DexStruct.DexProtos[struct.unpack('H',f.read(2))[0]]
method_name = DexStruct.DexStrings[struct.unpack('I',f.read(4))[0]]['content']
tmpDexMethodItem = {
'class' : method_class,
'proto' : method_proto,
'name' : method_name,
}
DexStruct.DexMethods.append(tmpDexMethodItem)

Reference

Dalvik Executable format

《Android 软件安全与逆向分析》