part-1

前言

解析 dex 文件结构 - DexHeader 中我们知道 Table 部分就是索引结构区,他们是是作为指向 Data 区数据的结构索引而存在的。
数据区是 dex 会用到的全部数据。


StringIds 和 StringData

StringIds 存放的是字符串的 id,也就是数据区中各个 StringData 的文件偏移。
StringIds 的大小和文件偏移在 DexHeader 和 map_list 中都有指定。

结构

StringIds 以4字节对齐,即总大小为 4 * stringIdsSize .

1
2
3
4
5
6
/*
* Direct-mapped "string_id_item".
*/
struct DexStringId {
u4 stringDataOff; /* file offset to string_data_item */
};

按照 StringIds 指示的偏移来到数据区对应的位置,StringData 采用的格式为:

字符串长度 : uleb128 格式,不包括字符串尾部的 00 。
字符串内容:MUTF-8(经过修改的UTF-8),以 00 结尾 .

手工查找

某 dex 文件的 StringIds 部分

stringIdsSize:0x5c
stringIdsOff:0x0070

0x0070 ~ 0x01df

1
2
3
4
5
6
7
8
9
00000070 bc 04 00 00 ef 04 00 00 26 05 00 00 30 05 00 00 |........&...0...|
00000080 38 05 00 00 41 05 00 00 44 05 00 00 48 05 00 00 |8...A...D...H...|
00000090 4b 05 00 00 4f 05 00 00 52 05 00 00 56 05 00 00 |K...O...R...V...|
000000a0 71 05 00 00 8c 05 00 00 a0 05 00 00 bf 05 00 00 |q...............|
000000b0 dd 05 00 00 f9 05 00 00 09 06 00 00 24 06 00 00 |............$...|
000000c0 40 06 00 00 57 06 00 00 6e 06 00 00 85 06 00 00 |@...W...n.......|
........ .. .. .. ..
000001c0 c4 09 00 00 c9 09 00 00 d1 09 00 00 dd 09 00 00 |................|
000001d0 e4 09 00 00 eb 09 00 00 f0 09 00 00 f4 09 00 00 |................|

以第一个字符串为例,文件偏移为 bc 04 00 00

1
2
3
4
000004b0 64 0a 00 00 de 0f 00 00 00 00 00 00 31 2f 64 61 |d...........1/da|
000004c0 74 61 2f 64 61 74 61 2f 63 6f 6d 2e 7a 79 68 2e |ta/data/com.zyh.|
000004d0 6c 69 67 68 74 69 6e 67 62 61 63 6b 75 70 2f 2e |lightingbackup/.|
000004e0 6c 69 62 2f 6c 69 62 65 78 65 63 2e 73 6f 00 35 |lib/libexec.so.5|

根据 LEB128 的解码格式可知:
字符串的长度为 0x31(有效位:011 0001) = 49 个字节。(“/data/data/com.zyh.lightingbackup/.lib/libexec.so”)

以此类推:

序数 文件偏移 字符串长度 字符串
0x00 0x04bc 0x31 /data/data/com.zyh.lightingbackup/.lib/libexec.so
0x01 0x04ef 0x35 /data/data/com.zyh.lightingbackup/.lib/libexecmain.so
0x04 0x0538 0x07 CPU_ABI
0x05 0x0541 0x01 I
0x06 0x0544 0x02 IL
0x07 0x0548 0x01 J
0x08 0x054b 0x02 JL
0x09 0x054f 0x01 L
0x0a 0x0552 0x02 LL
0x0b 0x0556 0x19 Landroid/app/Application;
0x0c 0x0571 0x19 Landroid/content/Context;
0x5b 0x09f0 0x02 ze
0x5c 0x09f4 0x02 zf

写程序解析字符串

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
def readuleb128(f):
num = struct.unpack('B',f.read(1))[0] # 读第一字节
if num > 0x7f :
cur = struct.unpack('B',f.read(1))[0] #读第二字节
num = (num & 0x7f) | ((cur & 0x7f) << 7)
if cur > 0x7f:
cur = struct.unpack('B',f.read(1))[0] #读第三字节
num = num | ((cur & 0x7f) << 14)
if cur > 0x7f:
cur = struct.unpack('B',f.read(1))[0] #读第四字节
num = num | ((cur & 0x7f) << 21)
if cur > 0x7f:
cur = struct.unpack('B',f.read(1))[0] #读第五字节
num = num | ((cur & 0x7f) << 28)
return num
def parseStrings(f):
f.seek(DexStruct.DexHeader['stringIdsOff'])
stringIds_data = f.read(4*DexStruct.DexHeader['stringIdsSize'])
cur_pos = 0
for i in range(DexStruct.DexHeader['stringIdsSize']):
str_off = struct.unpack('I',stringIds_data[cur_pos:cur_pos+4])[0]
f.seek(str_off)
str_len = readuleb128(f)
str_content = f.read(str_len)
cur_pos += 4
tmpDexStringItem = {
'offset' : str_off,
'len' : str_len,
'content' : str_content,
}
DexStruct.DexStrings.append(tmpDexStringItem)

TypeIds

TypeIds 存放的是 stringIds 内的索引,也就是说 StringData 里面的一些字符串是类型描述符。
TypeIds 的大小和文件偏移在 DexHeader 和 map_list 中都有指定。

这里的类型描述符可以代表 dex 文件中的任何类型,包括基本类型,类类型,数组类型和空类型。

结构

TypeIds 以4字节对齐,即总大小为 4 * typeIdsSize .

1
2
3
4
5
6
/*
* Direct-mapped "type_id_item".
*/
struct DexTypeId {
u4 descriptorIdx; /* index into stringIds list for type descriptor */
};

通过 descriptorIdx 可以得到相应类型描述符的字符串的 StringId,从而找到相应的字符串。

手动查找

某 dex 文件的 TypeIds 部分

TypeIdsSize:0x19
TypeIdsOff:0x01e0

0x01e0 ~ 0x0244

1
2
3
4
5
6
7
000001e0 05 00 00 00 07 00 00 00 0b 00 00 00 0c 00 00 00 |................|
000001f0 0d 00 00 00 0e 00 00 00 0f 00 00 00 10 00 00 00 |................|
00000200 11 00 00 00 12 00 00 00 13 00 00 00 14 00 00 00 |................|
00000210 15 00 00 00 16 00 00 00 17 00 00 00 18 00 00 00 |................|
00000220 19 00 00 00 1a 00 00 00 1b 00 00 00 1c 00 00 00 |................|
00000230 1d 00 00 00 1e 00 00 00 21 00 00 00 26 00 00 00 |........!...&...|
00000240 29 00 00 00

以第一个索引值 0x05 为例,在 StringIds 中找到第 0x05 个 StringId,也就是 0x0070(stringIdsOff) + 0x05 * 4 = 0x0084,
该地址值为 0x0541,0x0541 处的字符串为 “I”,int 类型。类型对照参见下表 - Dalvik 字节码类型。

语法 含义
V void; 只用于返回值
Z boolean
B byte
S short
C char
I int
J long
F float
D double
Lfully/qualified/Name; 类名:fully.qualified.Name
[descriptor 数组描述符,可以表示多维数组如 [[[descriptor,但最多只能有255个维度

以此类推:

序数 descriptorIdx StringId Type 真正类型
0x00 0x05 0x0084 I int
0x01 0x07 0x008c J long
0x02 0x0b 0x009c Landroid/app/Application; android.app.Application
0x03 0x0c 0x00a0 Landroid/content/Context; android.content.Context
0x04 0x0d 0x00a4 Landroid/os/Build; android.os.Build
0x05 0x0e 0x00a8 Lcom/shell/NativeApplication; com.shell.NativeApplication
0x06 0x0f 0x00ac Lcom/shell/SuperApplication; com.shell.SuperApplication
0x07 0x10 0x00b0 Ldalvik/annotation/Throws; dalvik.annotation.Throws
0x08 0x11 0x00b4 Ljava/io/File; java.io.File
0x09 0x12 0x00b8 Ljava/io/FileInputStream; java.io.FileInputStream
0x0a 0x13 0x00bc Ljava/io/FileOutputStream; java.io.FileOutputStream
0x0b 0x14 0x00c0 Ljava/io/IOException; java.io.IOException
0x0c 0x15 0x00c4 Ljava/io/InputStream; java.io.InputStream
0x0d 0x16 0x00c8 Ljava/lang/Exception; java.lang.Exception
0x0e 0x17 0x00cc Ljava/lang/Object; java.lang.Object
0x0f 0x18 0x00d0 Ljava/lang/String; java.lang.String
0x10 0x19 0x00d4 Ljava/lang/System; java.lang.System
0x11 0x1a 0x00d8 Ljava/util/zip/CRC32; java.util.zip.CRC32
0x12 0x1b 0x00dc Ljava/util/zip/CheckedInputStream; java.util.zip.CheckedInputStream
0x13 0x1c 0x00e0 Ljava/util/zip/Checksum; java.util.zip.Checksum
0x14 0x1d 0x00e4 Ljava/util/zip/ZipEntry; java.util.zip.ZipEntry
0x15 0x1e 0x00e8 Ljava/util/zip/ZipFile; java.util.zip.ZipFile
0x16 0x21 0x00f4 V void
0x17 0x26 0x0108 Z boolean
0x18 0x29 0x0114 [B] byte[]

写程序解析 TypeIds

1
2
3
4
5
6
7
8
def parseTypes(f):
'''
此函数基于 parseStrings 函数的结果,调用前需先调用 parseStrings
'''
f.seek(DexStruct.DexHeader['typeIdsOff'])
for i in range(DexStruct.DexHeader['typeIdsSize']):
type_desc_id = struct.unpack('I',f.read(4))[0]
DexStruct.DexTypes.append(DexStruct.DexStrings[type_desc_id])

Reference

Dalvik Executable format

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