ELF文件格式(一)

1,734 阅读6分钟
原文链接: mp.weixin.qq.com

奇技指南

ELF文件格式是Native Hook以及逆向等技术的重要基础,学习ELF文件格式后,你会更加了解linker的工作方式,能从一个so文件中看到各种信息,甚至直接看懂程序逻辑。本文章尝试深入分析ELF格式,本篇是第一部分。

本文转载自奇卓社。

什么是ELF文件

根据官方定义:

Executable and linking format(ELF)文件是x86 Linux系统下的一种常用目标文件(object file)格式,有三种主要类型:

(1)适于连接的可重定位文件(relocatable file),可与其它目标文件一起创建可执行文件和共享目标文件。

(2)适于执行的可执行文件(executable file),用于提供程序的进程映像,加载的内存执行。

(3)共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。

这段定义,大家是不是看不懂呢?看不懂就对了,因为这种话,只有会的人能看懂,不会的看不懂,通俗来说,就是废话。如果你现在就能看懂,那你赶紧出门吧,这篇文章已经满足不了你了!

解释一下,EFL就是Linux系统的一种文件格式,与Windows系统的PE格式相似。PE格式,就是windows中.exe文件使用的格式,这下大概能理解了吧!在Windows系统中,一个可执行文件,文件名以.exe结尾,双击就可以运行了(忽略批处理文件.bat)。

那么在Linux系统中是如何处理的呢?

与Windows不同,Linux不会以文件的扩展名来判断是否可执行。在Linux中,符合ELF格式标准(可执行文件)的文件,都可以运行。但,Linux文件系统包含一个可执行权限,因此执行一个文件要包含两个条件:符合ELF格式、有可执行权限

再具体解释一下定义,ELF就是一种文件格式,在Linux下,有3种文件都符合这种格式:

(1)可重定位文件:可以理解为.o文件,一个c/c++源文件,经过编译后,就会生成一个对应的.o文件。

(2)可执行文件:相当于windows中的.exe文件。一个或多个.o文件,经过链接后,生成的文件,可直接运行。

(3)共享目标文件:相当于windows中的.dll文件。与(2)一样,一个或多个.o文件,经过链接,可以生成共享目标文件(具体要看make文件中指定的规则)。Android NDK最后生成的so文件,就是这种格式。

我们讨论的是Android上的开发技术,因此,ELF文件格式,指的就是Android的so文件格式。

ELF格式

根据官方定义:

ELF文件格式提供了两种视图,分别是链接视图和执行视图

这又是啥意思?

接着上面的说,在Linux系统中,ELF格式有多种文件,这些文件用处还不一样,既然有多种文件是符合一个相同的文件格式标准的,那么系统是如何区分这些不同的文件呢?

其实,Linux区分不出,对系统来说,这些都是ELF文件。定义两种视图的意义,关键在于这些文件的使用者,每个使用者只认一种视图,才实现多种文件的功能。这么说还是解释不清楚,举个例子吧:

假设有一辆高铁列车,线路限制只在北京和上海之间运行。列车不掉头,有两个车头。这辆列车有两个使用者,分别是北京司机和上海司机。北京司机用南向车头把车开到上海,上海司机用北向车头把车开到北京。对应我们的案例,ELF格式,就是定义了一组车厢加两个车头的标准格式,此外又定义了两个视图,一个是南向视图,包括一组车厢和南向车头,一个是北向视图,包括一组车厢和北向车头。那么铁路局(对应Linux系统)是不区分这两个视图的,它只把车交给对应的司机,只有司机(使用者)才会看对应的视图,对北京司机来说,他只看南向的车头,不会看北向的车头。

回到现实,对于交给linker链接的ELF文件,都会当作链接视图处理。同理,执行视图也是一个道理,我们不在这讨论执行视图。

ELF具体格式如下

ELF文件由以下几个部分组成:

(1) ELF header

(2) section header table/program header table

(3) sections/segments

上面的图体现了ELF格式的主要内容,主体内容其实比较简单,除了两个header外,存放内容的就是setction/segment。对于链接视图,需要先定位到文件尾部的section header table,然后索引到各个section。对于执行视图,定位文件上部的program header table,然后再索引到各个segment。

ELF header:Window系统根据文件的扩展名来判断文件类型,Linux不看扩展名,主要根据各种文件格式的Header来判断文件类型。ELF header,在文件的开头,同其他文件格式(例如:ZIP、PNG等)一样,是描述ELF文件的头部信息,二进制格式,固定长度0x34字节。ELF header的前4个字节,固定为0x7F 45 4C 46(Magic),系统只看这前4个字节,即可判断这是个ELF格式的文件。header部分,主要描述文件本身的信息,包括大小、类型、table header位置等,详细内容我们放在后续的篇章中描述。

section/segment:真正存储内容的区域。对应链接视图为setcion,执行视图的为segment,我们只讨论setcion格式。一个ELF文件中包含多个setcion。一般来说,C/C++源码,经过编译后,会生成对应字符串、只读数据、可执行指令等。so文件用section来存储这些数据,一个section用来存储一个类型的数据,例如用一个名为.text的section存储可执行程序,用.strtab的section存储字符串,将所有这些section组合到一起,就包含了全部内容。

setction header table:上面描述的一堆section,是按二进制紧密的排列到文件中的,没法定位查找。因此需要一个table来记录一下每个section的信息,包括在文件中的起始位置、大小、类型等。linker查看这个table,就能知道有哪些section,每个section在文件的什么地方。

Android的NDK中,附带了查看ELF文件的工具,以android-ndk-r13b为例,在android-ndk-r13b\toolchains\arm-linux-androideabi-4.6\prebuilt\windows-x86_64\bin目录下:

arm-linux-androideabi-readelf 用来查看ELF文件的头信息

arm-linux-androideabi-objdump 可以反编译其中的指令section,可使用这个工具查看so文件中定义的函数,以及汇编代码

详细格式说明会在后续文章中描述!

如有问题,欢迎大家在留言区指出,我会及时修改,以免误导其他人!

界世的你当不

只作你的肩膀

 360官方技术公众号 

技术干货|一手资讯|精彩活动

空·