高端知识点,ELF结构详解
扫描二维码
随时随地手机看文章
ELF(可执行和链接格式)是 Linux 系统中用于可执行文件和共享库的文件格式。它定义了如何将一个文件加载到内存中并执行。ELF 文件主要包含以下几部分:
- ELF 头:包含文件的基本信息,如文件类型、入口点地址等。
- 程序头表(Program Header Table)(PHT):描述如何将文件的各个部分加载到内存中。
- 节(Sections):包含代码、数据等具体内容。
- 节头表(Section Header Table)(SHT):描述各个部分的详细信息。
ELF视图
看这个图:
在讨论ELF文件时,通常会提到两种视图:链接视图(Linking View)和执行视图(Execution View)。
链接视图
链接视图是从链接器的角度来看待ELF文件的结构。
链接视图关注的是文件中的节(Section),ELF未被加载到内存执行前,以section 为单位组织数据,这些 section 包含代码、数据、符号表、重定位信息等。每个 section 都有特定的用途,例如.text节用于存放代码,.data节用于存放初始化的数据,.bss节用于存放未初始化的数据。
主要的section有:
- .rodata:只读数据
- .text:可执行代码
- .plt:程序链接表,用于过程调用和跳转
- 各种重定位表(.rel.plt, .rel.dyn等):指导动态链接
- .dynsym:动态符号表
- .dynstr:动态字符串表
- .data:已初始化数据
- .bss:未初始化数据
- .init_array/.fini_array:程序初始化和终止时执行的代码
- .got:全局偏移表
- .symtab:符号表
执行视图
ELF 被加载到内存后,也就是执行后的视图。执行视图是从操作系统加载器的角度来看待ELF文件的结构,执行视图关注的是文件中的段(Segment),这些段定义了程序在内存中的布局和权限。执行视图主要用于程序的加载和执行阶段。
段是内存映射的单位,定义了程序的代码和数据在内存中的布局。常见的段包括可加载段(LOAD)、动态段(DYNAMIC)、解释器段(INTERP)等。操作系统根据段的信息将程序的代码和数据映射到进程的虚拟内存空间。每个段有特定的权限标志(例如,可读、可写、可执行),用于保护内存访问。
- 链接视图关注section,用于编译和链接阶段。
- 执行视图关注segment,用于程序的加载和执行阶段,可以这样理解,一个segment,至少包含一个section。
也可以这样理解:链接视图是链接器处理文件时的视角,而执行视图是操作系统加载和运行程序时的视角。
本项目使用的 PLT HOOK 并不是修改磁盘上的 ELF 文件,而是在运行时修改内存中的数据,因此我们主要关心的是执行视图,即 ELF 被加载到内存后,ELF 中的数据是如何组织和存放的。
linker 依据 ELF 文件执行视图中的信息,用 mmap 将 ELF 加载到内存中,执行 relocation(重定位)把外部引用的绝对地址填入 GOT 表和 DATA 中,然后设置内存页的权限,最后调用 init_array 中的各个初始化函数。
PLT hook 执行的时机是在 linker 完全加载完 ELF 之后,我们需要解析内存中的 ELF 数据,然后修改 relocation 的结果。
ELF 结构
看这个Header(https://github.com/bminor/glibc/blob/master/elf/elf.h#L97):
typedef struct { unsignedchar e_ident[EI_NIDENT]; /* Magic number and other info */ Elf64_Half e_type; /* Object file type */ Elf64_Half e_machine; /* Architecture */ Elf64_Word e_version; /* Object file version */ Elf64_Addr e_entry; /* Entry point virtual address */ Elf64_Off e_phoff; /* Program header table file offset */ Elf64_Off e_shoff; /* Section header table file offset */ Elf64_Word e_flags; /* Processor-specific flags */ Elf64_Half e_ehsize; /* ELF header size in bytes */ Elf64_Half e_phentsize; /* Program header table entry size */ Elf64_Half e_phnum; /* Program header table entry count */ Elf64_Half e_shentsize; /* Section header table entry size */ Elf64_Half e_shnum; /* Section header table entry count */ Elf64_Half e_shstrndx; /* Section header string table index */ } Elf64_Ehdr;
ELF Header是整个文件的起始部分,它包含以下关键字段:
- e_ident: 标识信息,包括魔数(0x7F 'E' 'L' 'F')等。
- e_type: 文件类型(例如,ET_EXEC表示可执行文件,ET_DYN表示共享库)。
- e_machine: 目标机器架构(例如,EM_X86_64表示x86_64架构)。
- e_version: ELF版本。
- e_entry: 程序入口点地址。
- e_phoff: Program Header Table的偏移。
- e_shoff: Section Header Table的偏移。
- e_flags: 与架构相关的标志。
- e_ehsize: ELF Header的大小。
- e_phentsize: Program Header Table中每个条目的大小。(执行视图)
- e_phnum: Program Header Table中的条目数。
- e_shentsize: Section Header Table中每个条目的大小。(链接视图)
- e_shnum: Section Header Table中的条目数。
- e_shstrndx: 包含节名称字符串表的节索引。
Program Header Table
看代码(https://github.com/bminor/glibc/blob/master/elf/elf.h#L707):
typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ } Elf64_Phdr;
Program Header Table用于描述段的信息,每个条目包含:
- p_type: 段类型(例如,PT_LOAD表示可加载段),具体详见(https://github.com/bminor/glibc/blob/master/elf/elf.h#L717):
#define PT_NULL 0 /* Program header table entry unused */ #define PT_LOAD 1 /* Loadable program segment */ #define PT_DYNAMIC 2 /* Dynamic linking information */ #define PT_INTERP 3 /* Program interpreter */ #define PT_NOTE 4 /* Auxiliary information */ #define PT_SHLIB 5 /* Reserved */ #define PT_PHDR 6 /* Entry for header table itself */ #define PT_TLS 7 /* Thread-local storage segment */ #define PT_NUM 8 /* Number of defined types */ #define PT_LOOS 0x60000000 /* Start of OS-specific */ #define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ #define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ #define PT_GNU_PROPERTY 0x6474e553 /* GNU property */ #define PT_GNU_SFRAME 0x6474e554 /* SFrame segment. */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ #define PT_HISUNW 0x6fffffff #define PT_HIOS 0x6fffffff /* End of OS-specific */ #define PT_LOPROC 0x70000000 /* Start of processor-specific */ #define PT_HIPROC 0x7fffffff /* End of processor-specific */
- p_offset: 段在文件中的偏移。
- p_vaddr: 段在内存中的虚拟地址。
- p_paddr: 段在内存中的物理地址(通常未使用)。
- p_filesz: 段在文件中的大小。
- p_memsz: 段在内存中的大小。
- p_flags: 段的权限标志(例如,可读、可写、可执行)。
- p_align: 段的对齐要求。
Section Header Table
看代码(https://github.com/bminor/glibc/blob/master/elf/elf.h#L409):
typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Xword sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr;
Section Header Table用于描述节的信息,每个条目包含:
- sh_name: section 名称字符串表中的索引。
- sh_type: section 类型(例如,SHT_PROGBITS表示程序代码或数据),具体详见(https://github.com/bminor/glibc/blob/master/elf/elf.h#L430):
/* Legal values for sh_type (section type). */ #define SHT_NULL 0 /* Section header table entry unused */ #define SHT_PROGBITS 1 /* Program data */ #define SHT_SYMTAB 2 /* Symbol table */ #define SHT_STRTAB 3 /* String table */ #define SHT_RELA 4 /* Relocation entries with addends */ #define SHT_HASH 5 /* Symbol hash table */ #define SHT_DYNAMIC 6 /* Dynamic linking information */ #define SHT_NOTE 7 /* Notes */ #define SHT_NOBITS 8 /* Program space with no data (bss) */ #define SHT_REL 9 /* Relocation entries, no addends */ #define SHT_SHLIB 10 /* Reserved */ #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ #define SHT_INIT_ARRAY 14 /* Array of constructors */ #define SHT_FINI_ARRAY 15 /* Array of destructors */ #define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ #define SHT_GROUP 17 /* Section group */ #define SHT_SYMTAB_SHNDX 18 /* Extended section indices */ #define SHT_RELR 19 /* RELR relative relocations */ #define SHT_NUM 20 /* Number of defined types. */ #define SHT_LOOS 0x60000000 /* Start OS-specific. */ #define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ #define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ #define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ #define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ #define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ #define SHT_SUNW_move 0x6ffffffa #define SHT_SUNW_COMDAT 0x6ffffffb #define SHT_SUNW_syminfo 0x6ffffffc #define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ #define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ #define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ #define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ #define SHT_HIOS 0x6fffffff /* End OS-specific type */ #define SHT_LOPROC 0x70000000 /* Start of processor-specific */ #define SHT_HIPROC 0x7fffffff /* End of processor-specific */ #define SHT_LOUSER 0x80000000 /* Start of application-specific */ #define SHT_HIUSER 0x8fffffff /* End of application-specific */
- sh_flags: section 标志(例如,可写、可分配、可执行)。
- sh_addr: section 在内存中的地址。
- sh_offset: section 节在文件中的偏移。
- sh_size: section 的大小。
- sh_link: 与section 相关的链接信息。
- sh_info: 附加信息。
- sh_addralign: section 的对齐要求。
- sh_entsize: 如果section 包含固定大小的条目,则为每个条目的大小。
常见的Section
- .text: 包含程序的可执行代码。
- .data: 包含初始化的全局和静态变量。
- .bss: 包含未初始化的全局和静态变量,实际在文件中不占空间。
- .rodata: 包含只读数据,例如字符串常量。
- .symtab: 包含符号表,用于链接。
- .strtab: 包含字符串表,通常用于符号表中的符号名称。
- .rel.text: 包含与.text节相关的重定位信息。
- .debug: 包含调试信息。
Segment
Segment是程序运行时的内存映射单位,常见的段包括:
- LOAD段: 包含可加载的代码和数据,映射到进程的虚拟内存空间。
- DYNAMIC段: 包含动态链接信息。
- INTERP段: 指定程序运行时使用的解释器(例如,动态链接器)。
- NOTE段: 包含附加信息,如版本信息。
ELF文件结构通过Header、Segment和Section的组合,提供了一个灵活的机制来描述程序的代码和数据布局。
本项目的PLTHOOK主要关注的就是DYNAMIC段。





