0x01 介绍
ELF(Executable and Linkable Format)是一种常见的可执行文件和共享库格式,其结构如下:
- ELF header:包含了 ELF 文件的基本属性信息。
- Program header table:描述了程序在内存中的加载情况,包括可执行代码段、数据段等。
- Section header table:描述了 ELF 文件中所有节区的信息,如代码节区、数据节区等。
- 节区数据:包含了 ELF 文件中的实际数据,如代码、数据等。
ELF 文件的结构可以根据不同的操作系统和体系结构而有所差异。
来点通俗的解释:
- 头部(Header):ELF文件的头部包含了整个文件的基本信息,如文件类型、机器架构、入口点地址等。可以将头部看作是文件的元数据,就像书的封面上包含了书的基本信息。
- 节区表(Section Table):节区表是一个数据结构,它描述了ELF文件中各个节区的位置和属性。类似于书的目录,它告诉你在书中的哪个位置可以找到某个章节或内容。
- 代码节区(Code Section):代码节区包含了程序的实际指令,即可执行的机器码。这部分就像是一本书中的具体章节内容,包含了实际的文字和故事情节。
- 数据节区(Data Section):数据节区存储了程序中使用的全局变量、静态变量和常量等数据。可以把它看作是书中的附录或者注释部分,包含了与故事情节相关的补充信息。
- 符号表(Symbol Table):符号表包含了程序中定义的函数和变量的信息,比如名称、地址等。可以将符号表类比为书的索引,你可以通过索引查找到书中特定关键字或人物的出现位置。
- 动态链接表(Dynamic Linking Table):动态链接表包含了程序在运行时所需的动态链接信息,比如依赖的共享库和符号解析的过程。它就像是一本参考书,提供了额外的信息和解释。
0x02 ELF header
在 /usr/include/elf.h
或ELF规范里可以查到ELF头部的定义:
typedef struct
{
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; /* ELF头部的大小 */
Elf64_Half e_phentsize; /* 程序头表的条目大小 */
Elf64_Half e_phnum; /* 程序头表的条目数 */
Elf64_Half e_shentsize; /* 节头表的条目大小 */
Elf64_Half e_shnum; /* 节头表的条目数 */
Elf64_Half e_shstrndx; /* 节头表中与节名称字符串相关联的条目的索引 */
} Elf64_Ehdr;
ELF header包含以下部分:
标识信息 (e_ident)
包括ELF文件类型、机器架构、版本等信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixcuzz1v-1687964972116)(https://cdn.nlark.com/yuque/0/2023/png/12758072/1680406734442-e07376a0-a049-480e-aa09-7f03f2a8a374.png)]
e_ident数组始终以4个字节的幻数为开头,以此来标识ELF文件,幻数由十六进制0x7f和ELF的ASCII码组成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-noUMO1MZ-1687964972117)(https://cdn.nlark.com/yuque/0/2023/png/12758072/1680406667485-1257259c-efac-43b3-947b-002fcc1f6318.png)]
幻数后面的字节提供了有关ELF二进制文件类型规范的详细信息,后面那些字节的索引分别称为EI_CLASS、EI_DATA、EI_VERSION、EI_OSABI、EI_ABIVERSION以及EI_PAD等
- EI_CLASS标识该二进制文件用于32位还是64位体系结构,前者设置为常量ELFCLASS32(1),后者设置为常量ELFCLASS64(2)
- EI_DATA标识架构的字节序,也就是多字节值是大端序还是小端序,ELFDATA2LSB(1)表示小端序,ELFDATA2MSB(2)表示大端序
- EI_VERSION标识ELF规范版本,现在只有一个值EV_CURRENT也就是1
- EI_OSABI和EI_ABIVERSION标识关于应用程序二进制接口和操作系统的信息
- EI_PAD包含多个字节,保留为了以后使用
readelf可以查看ELF头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UqHMtPt9-1687964972118)(https://cdn.nlark.com/yuque/0/2023/png/12758072/1680407629546-32bd0e3e-d83f-46d0-bd72-d2b7dbaafea0.png)]
e_type、e_machine和e_version
文件类型 (e_type)
描述ELF文件的类型,如可执行文件、共享库、目标文件等。
机器架构 (e_machine)
描述ELF文件所使用的机器架构,例如x86、ARM等。
版本信息 (e_version)
描述ELF头部和节头表的版本号。
入口地址 (e_entry)
描述可执行文件入口点的虚拟内存地址。
e_phoff和e_shoff
程序头表偏移量 (e_phoff)
描述程序头表在文件中的偏移量。
节头表偏移量 (e_shoff)
描述节头表在文件中的偏移量。
标志 (e_flags)
描述ELF文件的各种属性,如是否为可重定位文件。
ELF头部大小 (e_ehsize)
描述ELF头部的大小。
e_*entsize和e_*num
程序头表项大小 (e_phentsize)
描述每个程序头表项的大小。
程序头表项数量 (e_phnum)
描述程序头表中有多少个表项。
节头表项大小 (e_shentsize)
描述每个节头表项的大小。
节头表项数量 (e_shnum)
描述节头表中有多少个表项。
字符串表索引 (e_shstrndx)
描述字符串表在节头表中的索引。
0x03 Program header table 程序头表
ELF(Executable and Linkable Format)文件的Program header table(程序头表)是一部分用于描述可执行文件和可链接文件的结构信息的表格。它包含了与程序运行相关的段(segment)信息。每个段描述了文件中的一个连续内存区域,这些区域在程序运行时被加载到内存中。
Program header table中的每一项都是一个程序头(Program Header),包含了以下字段:
p_type
:段的类型,指示段的作用和属性。常见的类型包括可加载段(PT_LOAD)、动态链接信息(PT_DYNAMIC)、符号表(PT_SYMTAB)等。p_flags
:段的标志位,描述段的属性,如可读(PF_R)、可写(PF_W)、可执行(PF_X)等。p_offset
:段在文件中的偏移量,表示该段的数据在文件中的位置。p_vaddr
:段的虚拟地址,表示该段在内存中的加载地址。p_paddr
:段的物理地址,一般在可执行文件中没有特定用途。p_filesz
:段在文件中的大小,表示段的数据在文件中的长度。p_memsz
:段在内存中的大小,表示段在内存中占用的长度。对于需要额外的内存空间的段,文件大小和内存大小可能不同。p_align
:段在文件和内存中的对齐方式,表示段的起始位置在文件和内存中的偏移量需要满足的对齐要求。
Program header table
的项数由e_phnum
字段确定,该字段记录了Program header table中的项数。
总而言之,Program header table用于描述ELF文件中各个段的属性和位置信息,以便在程序加载和执行时进行正确的内存映射。
0x04 Section Header Table(节头表)
ELF(Executable and Linkable Format)文件的Section Header Table(节头表)是一个重要的数据结构,用于描述和组织文件中的各个节(Section)。它包含了一系列节头(Section Header),每个节头对应一个节,用于存储关于该节的信息。Section Header Table位于ELF文件的头部信息中,可以通过解析ELF文件的头部来访问。
每个Section Header包含以下字段:
-
Name(名称):存储该节的名称,是一个对字符串表(String Table)的索引,用于查找具体的节名称。
-
Type(类型):指定该节的类型,如代码段(.text)、数据段(.data)、符号表(.symtab)等。不同类型的节承载着不同的功能和数据。
-
Flags(标志):描述该节的属性和特征,如是否可读、可写、可执行等。
-
Address(地址):指定该节在内存中的起始地址或者文件中的偏移地址。
-
Offset(偏移量):指定该节在ELF文件中的偏移量,表示该节在文件中的位置。
-
Size(大小):指定该节的大小,以字节为单位。
-
Link(关联):指定其他相关节的索引,具体含义根据不同的节类型而定。
-
Info(信息):包含与该节相关的其他信息,如符号表索引、重定位信息等。
-
Address Alignment(地址对齐):指定该节在内存中的对齐方式。
-
Entry Size(入口大小):对于某些特定类型的节,指定每个入口的大小。
Section Header Table通过这些节头信息,提供了对ELF文件中各个节的描述和组织。它是ELF文件格式的重要组成部分,用于实现文件的装载、链接和执行等操作。
0x05 节区数据
ELF (Executable and Linkable Format)文件的节区(Section)是用来组织和存储程序的各种数据和代码的。一个ELF文件通常由多个节区组成,每个节区负责存储不同类型的数据。以下是一些常见的节区类型和它们可能包含的内容:
- 代码节区(Text Section):存储可执行代码。
- 数据节区(Data Section):存储初始化的全局和静态变量。
- BSS节区(BSS Section):存储未初始化的全局和静态变量。
- 符号表节区(Symbol Table Section):包含符号表,其中记录了程序中使用的函数、变量和其他符号的信息。
- 字符串表节区(String Table Section):包含字符串表,用于存储其他节区中使用的字符串。
- 重定位节区(Relocation Section):包含重定位信息,用于在链接过程中修正地址。
- 动态链接节区(Dynamic Linking Section):存储动态链接器使用的信息,如共享库的依赖关系和重定位信息。
- 调试节区(Debug Section):包含调试信息,如源代码行号、局部变量等。
- 初始化节区(Initialization Section):存储程序的初始化代码,用于在程序加载时执行一些初始化操作。
- 可执行节区(Executable Section):存储可执行程序的二进制代码。
- 其他自定义节区:开发者可以定义自己的节区类型,用于存储特定的数据。