读书笔记:ELF文件结构

0x81 ELF文件的构成

Linux最常见的文件就是ELF文件了,无论是目标文件、动态链接库文件还是可执行文件,都是ELF文件,它与微软Windows的PE文件类似,都是COFF的一种实现,按照某种规则存储程序代码和数据。ELF文件本身的构成简单又复杂,在之前的文章中已经简单的陈述了ELF文件可能包含的结构,在这里我们略去复杂繁琐的部分,把其中最重要的部分提取出来:ELF文件头、各个段及段表、重定位表、字符串表以及符号表等等。

0x82 ELF文件头

要分析ELF文件,就需要使用我们之前提到的readelf工具,它能帮我们分析出文件中的信息。其中ELF最开始的部分就是ELF文件头,它包括了一部分基本信息如ELF版本号和程序入口地址等等。我们使用readelf -h {filename}命令查看一下ELF文件头信息:

SimpleSection.o的文件头信息

从上面的信息我们可以很容易的看出ELF文件的魔数和其基本信息,这些信息当中的所有数据结构都定义在/usr/include/elf.h文件当中。对于Magic Number,这是一个固定长度为16个字节的部分,对应结构体Elf32_Ehdr中的e_ident成员,其中第1个字节0x7f为ELF文件标记,紧接着三个字节0x454c46对应’E’’L’’F’三个字母,第5个字节代表ELF字长(0x00无效0x01为32位0x02为64位),第6个字节为字节序(0x00无效0x01小端0x02大端),然后一个字节是ELF版本号基本为1,其余9个字节保留补0。

0x83 段表

前面分析目标文件结构的时候,最终的结果就是我们将ELF文件分成了几个段,其中这些段的信息就记录在段表当中,编译器、链接器和装载器都是根据这个段表来定位各个段的信息。使用readelf -S {filename}来查看完整的段表信息:

SimpleSection.o的段表信息

可以看到readelf呈现的段信息是比objdump要多的,那是因为objdump显示的其实只是关键段的信息,而readelf工具则将符号表、字符串表、段头字符串表等信息都显示了出来,而段表的偏移正好是段内容结束后0x338=0x2d8+0x5f+0x1,而13个节为520字节,因此目标文件总长为1344个字节。在elf.h当中,这个偏移量存在于e_shoff当中,而段表的结构则是定义在elf.h中的Elf_Shdr结构体,并且是以一种数组的方式保存每个段信息。

段名实际上只对编译器链接器来说有意义,而对于操作系统,确定了段类型和段标志便确定了该段的属性和权限。段名我们可以在程序当中指定,而段的类型在一般情况下是在编译时决定的,它是一些预定类型中的某一个(比如PROGBITS程序相关段,SYMTAB符号表,STRTAB字符串表等).对于段标志,上图已经列出了比较详细的类型。

0x84 重定位表和字符串表

重定位表,顾名思义就是用于链接时重定位用的,比如.rel.text段,它其实是针对代码段.text的重定位表。所谓重定位,就是对于代码中对于某些函数调用或成员访问使用的是绝对地址,而这个绝对地址其实是相对于程序自身而言,所以链接器在链接时需要将地址重定位,所以这个记录了重定位信息的表就叫做重定位表,而书中的例子因为只有全局变量和静态变量所以没有.rel.data这种重定位数据表。

字符串表就是保存了程序当中的字符串信息的表,包括变量名段名这种字符串,而它的存放方式通常是连续存放每个字符串,每个字符串之间使用’\0’终止符隔开,这样字符串表使用时只需要使用初始偏移量就可以了。

简单的ELF文件分析就到这,之前的文章曾提到过strip工具,它能将程序段中的方法名脱去,而实际上脱去的就是本文中没有讲到的符号表,我想用单独的一篇来记录符号表的相关信息。随着学习的不断深入,每种内容可能都需要大量的篇幅来描述,比如上面的重定位表,我不想像复读机那样背过这些概念,而是想通过不断地实践去理解它们,不要再让自己保持一个一知半解的状态。

愿学习的道路上,你我共勉~