以上一篇文章生成的dex文件为例,讲解dex文件结构,这个dex文件结构非常简单,只有一个HelloWorld.java
文件,dex文件的基本格式请参考官方文档,对照dex文件的各个组成部分,我们可以将dex的内容进行分解。
1、文件头
header | header_item | the header |
64 65 78 0A 30 33 35 00 52 5B 33 08 D6 16 D3 44 F7 27 50 E9 D6 16 9A 3E 6D 53 EF F9 92 28 70 FA 98 02 00 00 70 00 00 00 78 56 34 12 00 00 00 00 00 00 00 00 04 02 00 00 0C 00 00 00 70 00 00 00 06 00 00 00 A0 00 00 00 02 00 00 00 B8 00 00 00 01 00 00 00 D0 00 00 00 04 00 00 00 D8 00 00 00 01 00 00 00 F8 00 00 00 80 01 00 00 18 01 00 00现在开始分解header部分的内容:
64 65 78 0A 30 33 35 00这部分是dex_magic的值对应的ASCII码值为:dex 035
52 5B 33 08
这部分是checksum,计算checksum的逻辑如下面的代码:
private static void calcChecksum(byte bytes[]) { Adler32 a32 = new Adler32(); a32.update(bytes, 12, bytes.length - 12); int sum = (int) a32.getValue(); checksum[0] = (byte) sum; checksum[1] = (byte) (sum >> 8); checksum[2] = (byte) (sum >> 16); checksum[3] = (byte) (sum >> 24); try { String decoded = new String(checksum, "UTF-8"); System.out.println(decoded); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } bytes[8] = (byte) sum; bytes[9] = (byte) (sum >> 8); bytes[10] = (byte) (sum >> 16); bytes[11] = (byte) (sum >> 24); }
D6 16 D3 44 F7 27 50 E9 D6 16 9A 3E 6D 53 EF F9 92 28 70 FA这部分是sha1的签名,计算signature的逻辑如下:
private static void calcSignature(byte bytes[]) { MessageDigest md; try { md = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException ex) { throw new RuntimeException(ex); } md.update(bytes, 32, bytes.length - 32); try { int amt = md.digest(bytes, 12, 20); if (amt != 20) throw new RuntimeException((new StringBuilder()) .append("unexpected digest write:").append(amt) .append("bytes").toString()); } catch (DigestException ex) { throw new RuntimeException(ex); } }
98 02 00 00这部分是文件大小:即0x0298=664bytes
70 00 00 00这部分是头的大小,112bytes
78 56 34 12这部分是大小端的标志,dex文件缺省是小端的,这个dex文件是小端的,因为对应的值为0x12345678,系统
里的定义如下:
uint ENDIAN_CONSTANT = 0x12345678; uint REVERSE_ENDIAN_CONSTANT = 0x78563412;
00 00 00 00 00 00 00 00这全0的八字节是link_size和link_off字段,主要用在文件的静态链接上,该dex不是静态链接文件,所有为0
04 02 00 00
这部分为map_off字段,值为0x204,即516,这个值是map_list所在位置相对文件起始位置的偏移量。map_list
是对整个dex文件的描述,分不同的类型,包括头、字符串、类型、函数原型、类、代码等等,其实和header的
信息有一些冗余,主要是在dex文件生成后对文件做一些校验工作,dx进程在生成dex文件后,会根据map_list的
内容对dex做一些校验工作,这个在后面介绍dx进程的时候会描述。
0C 00 00 00 70 00 00 00这部分内容为string_ids_size和string_ids_off,各占四字节
和map_list类似,string也有一个string_list,上面的两个值就是用来标识有多少个字符串,以及string_list
的偏移的。
header部分余下的都是各个list的长度和对应的偏移量,如type_ids_size和type_ids_off,指向type_list,
还有proto_list,field_list,method_list,code_item_list, class_def_item_list,data_list。这些list包含了
程序里的字符串信息、函数原型、字段、函数描述、类描述信息,这些也就组成了所有的程序内容。
dex header部分的分析基本完成了,header部分是整个dex文件的描述,也是文件各部分内容的索引,
掌握了header部分就可以继续分析其他部分内容,比如method_list,比如code_item_list。
在分析的过程中,要经常参考android源码,主要是数据结构的定义,因为涉及到数据元素的大小以及
成员,所以还是很费时间和精力的,但是这些都很有意义,让您在理解了一个可执行文件的结构的同时,
也熟悉android的源码。如果您在分析其他部分时,碰到问题可以联系我,在时间允许的情况下我会帮
您一起分析、解答问题。