最近在看ART虚拟机相关的内容,稍微有了解的都能知道/data/dalvik-cache下的.dex后缀的文件和odex文件实际上都是修改过的ELF文件,网上有很多说明,但是貌似都没有找到一篇对一个odex或者说oat文件做分析的。所以博主这里就想着尝试对odex文件做一个简单分析。
odex文件总体信息
这里博主拿SystemUI的oat文件(/data/dalvik-cache/arm64/system@product@priv-app@[email protected]@classes.dex
)做例子,下面是通过readelf -e system@product@priv-app@[email protected]@classes.dex
得出的ELF header、Section header、Program header以及Section到Segmemnt的映射关系如下所示
首先我们来看ELF header信息
这里我们先把通过readelf读出来的信息贴出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
ELF Header: Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - GNU ABI Version: 0 Type: DYN (Shared object file) Machine: AArch64 Version: 0x1 Entry point address: 0x0 Start of program headers: 64 (bytes into file) Start of section headers: 3756960 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 12 Section header string table index: 11 |
下面我们将代码中的ELF头定义贴出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
typedef struct elf64_hdr { unsigned char e_ident[EI_NIDENT]; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; Elf64_Half e_phnum; Elf64_Half e_shentsize; Elf64_Half e_shnum; Elf64_Half e_shstrndx; } Elf64_Ehdr; |
最后是通过010Editor使用十六进制显示的内容
通过上面三个信息的对比,很多信息就很明显了有木有
- 首先代码中的
unsigned char e_ident[EI_NIDENT];
的定义对应readelf信息的Magic,前面四个字节是固定的,定义如下:
1 2 3 4 5 |
#define ELFMAG0 0x7f #define ELFMAG1 'E' #define ELFMAG2 'L' #define ELFMAG3 'F' |
随后紧接着的是文件的类型,这里是0x02,便是的是64位文件
1 2 3 4 5 6 7 |
// Object file classes. enum { ELFCLASSNONE = 0, ELFCLASS32 = 1, // 32-bit object file ELFCLASS64 = 2 // 64-bit object file }; |
随后是文件字节排布的类型,即是大端字节序还是小端字节序,这里是0x01,代表是小端字节序
1 2 3 4 5 6 7 |
// Object file byte orderings. enum { ELFDATANONE = 0, // Invalid data encoding. ELFDATA2LSB = 1, // Little-endian object file ELFDATA2MSB = 2 // Big-endian object file }; |
再接着代表文件的版本,固定位1,所以是0x01,到这里我们就将第一个分析e_ident[EI_NIDENT]
的信息分析完了。
- 接着我们分析struct余下的字段:
e_type
代表的是OS ABI的类型,查看010Editor的信息,这里是0x03,对应的是GNU/Linux类型,也和readelf的信息对应上了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// OS ABI identification. enum { ELFOSABI_NONE = 0, // UNIX System V ABI ELFOSABI_HPUX = 1, // HP-UX operating system ELFOSABI_NETBSD = 2, // NetBSD ELFOSABI_LINUX = 3, // GNU/Linux ELFOSABI_HURD = 4, // GNU/Hurd ELFOSABI_SOLARIS = 6, // Solaris ELFOSABI_AIX = 7, // AIX ELFOSABI_IRIX = 8, // IRIX ELFOSABI_FREEBSD = 9, // FreeBSD ELFOSABI_TRU64 = 10, // TRU64 UNIX ELFOSABI_MODESTO = 11, // Novell Modesto ELFOSABI_OPENBSD = 12, // OpenBSD ELFOSABI_OPENVMS = 13, // OpenVMS ELFOSABI_NSK = 14, // Hewlett-Packard Non-Stop Kernel ELFOSABI_AROS = 15, // AROS ELFOSABI_FENIXOS = 16, // FenixOS ELFOSABI_C6000_ELFABI = 64, // Bare-metal TMS320C6000 ELFOSABI_C6000_LINUX = 65, // Linux TMS320C6000 ELFOSABI_ARM = 97, // ARM ELFOSABI_STANDALONE = 255 // Standalone (embedded) application }; |
后面的字段也类似,一次对应即可。这样ELF头信息就能很好的解读出来。接下来要说明的是section的信息了
Section hader解读
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 |
Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .rodata PROGBITS 0000000000001000 00001000 00000000000bd000 0000000000000000 A 0 0 4096 [ 2] .text PROGBITS 00000000000be000 000be000 00000000002aed00 0000000000000000 AX 0 0 4096 [ 3] .data.bimg.rel.ro PROGBITS 000000000036d000 0036d000 0000000000001560 0000000000000000 A 0 0 4096 [ 4] .bss NOBITS 000000000036f000 00000000 0000000000005814 0000000000000000 A 0 0 4096 [ 5] .dex NOBITS 0000000000375000 00000000 000000000000bed0 0000000000000000 A 0 0 4096 [ 6] .dynstr STRTAB 0000000000381000 0036f000 00000000000000c7 0000000000000000 A 0 0 4096 [ 7] .dynsym DYNSYM 00000000003810c8 0036f0c8 0000000000000120 0000000000000018 A 6 1 8 [ 8] .hash HASH 00000000003811e8 0036f1e8 000000000000003c 0000000000000004 A 7 0 4 [ 9] .dynamic DYNAMIC 0000000000382000 00370000 0000000000000070 0000000000000010 A 6 0 4096 [10] .gnu_debugdata PROGBITS 0000000000000000 00371000 0000000000024338 0000000000000000 0 0 4096 [11] .shstrtab STRTAB 0000000000000000 00395338 0000000000000063 0000000000000000 0 0 1 |
readelf section header获取到的信息如上,一共有11个section,其中第一个section是空的,这个是ELF的定义如此,那么我们要怎么在010Editor中找到对应字节的位置呢?首先在ELF header中,有一个关键的信息,就是section header的便宜,相信在上一节内容,你已经知道了,对应readelf的信息就是 Start of section headers: 3756960 (bytes into file)
,没错,这里就是和文件头的偏移量:3756960,即十六进制的3953A0。那么接下来我们在010Editor中跳转到3953A0位置看看
这里蓝色标注的内容就是一个section header描述的信息,section header在代码中的描述如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Section header for ELF64 - same fields as ELF32, different types. struct Elf64_Shdr { Elf64_Word sh_name; Elf64_Word sh_type; Elf64_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf64_Xword sh_size; Elf64_Word sh_link; Elf64_Word sh_info; Elf64_Xword sh_addralign; Elf64_Xword sh_entsize; }; |
第一个sh_name,这个需要特别说明一下,这个sh_name是一个Elf64_Word,实际是uint_32类型的,里面存储的是一个地址,指向
.shstrtab section中某个字串的开始地址。所以我们先看看.shstrtab section中存储的是什么内容吧,通过readelf中Section Header信息可以知道.shstrtab节的偏移地址位0x00395338,是的,这里直接读出来的地址就已经是16进制过的了。然后在010Editor中查看这个地址在存储的内容是什么呢?
通过右边的字符串区可以看到一些可读信息,这里面存储的就是该odex文件中用到的section name。而在具体的section中,对应的sh_name的值就是.shstrtab section中的偏移量。
OK,因为博主也是最近才开始用010Editor查看并对照这代码来分析的,目前上面的信息暂时够用了,后续有要对各个section的内容做分析的时候,博主再补充了,或者各位看官有什么不懂得,可以提出来,后续博主再继续分析哈。
大佬,可以加友链吗?
emmm,这个没弄过唉,不知道咋操作