ELF文件结构介绍

1.ELF文件介绍

首先需要知道的是:对象文件(Object files)有三个种类

可重定位的对象文件(Relocatable file)

这是由编译前汇编生成的 .o 文件。后面的连接器(link editor)拿一个或一些Relocatable object files 作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件(Shared object file)。我们可以使用 ar 工具将众多的 .o Relocatable object files 归档(archive)成 .a 静态库文件。如何产生 Relocatable file,你应该很熟悉了,请参见我们相关的基本概念文章和JulWiki。另外,可以预先告诉大家的是我们的内核可加载模块 .ko 文件也是 Relocatable object file。

可执行的对象文件(Executable file)

文本编辑器vi、调式用的工具gdb、播放mp3歌曲的软件mplayer等等都是Executableobject file。

在我们的 Linux 系统里面,存在两种可执行的东西。除了这里说的Executable object file,另外一种就是可执行的脚本(如shell脚本)。

注意这些脚本不是 Executableobject file,它们只是文本文件,但是执行这些脚本所用的解释器就是 Executable object file,比如bash shell 程序

可被共享的对象文件(Shared object file)

这些就是所谓的动态库文件,也即 .so 文件。

动态库在发挥作用的过程 中,必须经过两个步骤:

  1. 链接编辑器(link editor)拿它和其他Relocatable object file以及其他shared object file作为输入,经链接处理后,生存另外的shared object file或者executable file。
  2. 在运行时,动态链接器(dynamic linker)拿它和一个Executable file以及另外一些Shared object file来一起处理,在Linux系统里面创建一个进程映像。

这里我们主要是以Shared Object File(.so)为重点分析对象,因为我们在逆向APK中会遇到的绝大部分都是此类文件。

ELF文件格式

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

image-20230525233044161

链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。上图左侧的视角是从链接来看的,右侧的视角是执行来看的。总个文件可以分为四个部分:

  • ELF header: 描述整个文件的组织。

  • Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的

  • sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section

  • Section Header Table: 包含了文件各个segction的属性信息,我们都将结合例子来解释。

image-20230525155813703

程序头部表(Program Header Table),如果存在的话,告诉系统如何创建进程映像。

节区头部表(Section Header Table)包含了描述文件节区的信息,比如大小、偏移等。

可以通过执行命令”readelf -S android_server”来查看该可执行文件中有哪些section。

通过执行命令readelf –segments android_server,可以查看该文件的执行视图。

3.ELF Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
ELF32_Half e_type;
ELF32_Half e_machine;
ELF32_Word e_version;
ELF32__Addr e_entry;
ELF32_Off e_phoff;
ELF32_Off e_shoff;
ELF32_Word e_flags;
ELF32_Half e_ehsize;
ELF32_Half e_phentsize;
ELF32_Half e_phnum;
ELF32_Half e_shentsize;
ELF32_Half e_shnum;
ELF32_Half e_shstrndx;
}Elf32_Ehdr;

e_ident : ELF的一些标识信息,前四位为.ELF,其他的信息比如大小端等

e_machine : 文件的目标体系架构,比如ARM

e_version : 0为非法版本,1为当前版本

e_entry : 程序入口的虚拟地址

e_phoff : 程序头部表偏移地址

e_shoff : 节区头部表偏移地址

e_flags :保存与文件相关的,特定于处理器的标志

e_ehsize :ELF头的大小

e_phentsize : 每个程序头部表的大小

e_phnum :程序头部表的数量

e_shentsize:每个节区头部表的大小

e_shnum : 节区头部表的数量

e_shstrndx:节区字符串表位置

接着运行readelf -h android_server命令,可以看到ELF Header结构的内容。

image-20230525160416354