高端知識點,ELF結(jié)構(gòu)詳解
掃描二維碼
隨時隨地手機(jī)看文章
ELF(可執(zhí)行和鏈接格式)是 Linux 系統(tǒng)中用于可執(zhí)行文件和共享庫的文件格式。它定義了如何將一個文件加載到內(nèi)存中并執(zhí)行。ELF 文件主要包含以下幾部分:
- ELF 頭:包含文件的基本信息,如文件類型、入口點地址等。
- 程序頭表(Program Header Table)(PHT):描述如何將文件的各個部分加載到內(nèi)存中。
- 節(jié)(Sections):包含代碼、數(shù)據(jù)等具體內(nèi)容。
- 節(jié)頭表(Section Header Table)(SHT):描述各個部分的詳細(xì)信息。
ELF視圖
看這個圖:
在討論ELF文件時,通常會提到兩種視圖:鏈接視圖(Linking View)和執(zhí)行視圖(Execution View)。
鏈接視圖
鏈接視圖是從鏈接器的角度來看待ELF文件的結(jié)構(gòu)。
鏈接視圖關(guān)注的是文件中的節(jié)(Section),ELF未被加載到內(nèi)存執(zhí)行前,以section 為單位組織數(shù)據(jù),這些 section 包含代碼、數(shù)據(jù)、符號表、重定位信息等。每個 section 都有特定的用途,例如.text節(jié)用于存放代碼,.data節(jié)用于存放初始化的數(shù)據(jù),.bss節(jié)用于存放未初始化的數(shù)據(jù)。
主要的section有:
- .rodata:只讀數(shù)據(jù)
- .text:可執(zhí)行代碼
- .plt:程序鏈接表,用于過程調(diào)用和跳轉(zhuǎn)
- 各種重定位表(.rel.plt, .rel.dyn等):指導(dǎo)動態(tài)鏈接
- .dynsym:動態(tài)符號表
- .dynstr:動態(tài)字符串表
- .data:已初始化數(shù)據(jù)
- .bss:未初始化數(shù)據(jù)
- .init_array/.fini_array:程序初始化和終止時執(zhí)行的代碼
- .got:全局偏移表
- .symtab:符號表
執(zhí)行視圖
ELF 被加載到內(nèi)存后,也就是執(zhí)行后的視圖。執(zhí)行視圖是從操作系統(tǒng)加載器的角度來看待ELF文件的結(jié)構(gòu),執(zhí)行視圖關(guān)注的是文件中的段(Segment),這些段定義了程序在內(nèi)存中的布局和權(quán)限。執(zhí)行視圖主要用于程序的加載和執(zhí)行階段。
段是內(nèi)存映射的單位,定義了程序的代碼和數(shù)據(jù)在內(nèi)存中的布局。常見的段包括可加載段(LOAD)、動態(tài)段(DYNAMIC)、解釋器段(INTERP)等。操作系統(tǒng)根據(jù)段的信息將程序的代碼和數(shù)據(jù)映射到進(jìn)程的虛擬內(nèi)存空間。每個段有特定的權(quán)限標(biāo)志(例如,可讀、可寫、可執(zhí)行),用于保護(hù)內(nèi)存訪問。
- 鏈接視圖關(guān)注section,用于編譯和鏈接階段。
- 執(zhí)行視圖關(guān)注segment,用于程序的加載和執(zhí)行階段,可以這樣理解,一個segment,至少包含一個section。
也可以這樣理解:鏈接視圖是鏈接器處理文件時的視角,而執(zhí)行視圖是操作系統(tǒng)加載和運(yùn)行程序時的視角。
本項目使用的 PLT HOOK 并不是修改磁盤上的 ELF 文件,而是在運(yùn)行時修改內(nèi)存中的數(shù)據(jù),因此我們主要關(guān)心的是執(zhí)行視圖,即 ELF 被加載到內(nèi)存后,ELF 中的數(shù)據(jù)是如何組織和存放的。
linker 依據(jù) ELF 文件執(zhí)行視圖中的信息,用 mmap 將 ELF 加載到內(nèi)存中,執(zhí)行 relocation(重定位)把外部引用的絕對地址填入 GOT 表和 DATA 中,然后設(shè)置內(nèi)存頁的權(quán)限,最后調(diào)用 init_array 中的各個初始化函數(shù)。
PLT hook 執(zhí)行的時機(jī)是在 linker 完全加載完 ELF 之后,我們需要解析內(nèi)存中的 ELF 數(shù)據(jù),然后修改 relocation 的結(jié)果。
ELF 結(jié)構(gòu)
看這個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是整個文件的起始部分,它包含以下關(guān)鍵字段:
- e_ident: 標(biāo)識信息,包括魔數(shù)(0x7F 'E' 'L' 'F')等。
- e_type: 文件類型(例如,ET_EXEC表示可執(zhí)行文件,ET_DYN表示共享庫)。
- e_machine: 目標(biāo)機(jī)器架構(gòu)(例如,EM_X86_64表示x86_64架構(gòu))。
- e_version: ELF版本。
- e_entry: 程序入口點地址。
- e_phoff: Program Header Table的偏移。
- e_shoff: Section Header Table的偏移。
- e_flags: 與架構(gòu)相關(guān)的標(biāo)志。
- e_ehsize: ELF Header的大小。
- e_phentsize: Program Header Table中每個條目的大小。(執(zhí)行視圖)
- e_phnum: Program Header Table中的條目數(shù)。
- e_shentsize: Section Header Table中每個條目的大小。(鏈接視圖)
- e_shnum: Section Header Table中的條目數(shù)。
- e_shstrndx: 包含節(jié)名稱字符串表的節(jié)索引。
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: 段在內(nèi)存中的虛擬地址。
- p_paddr: 段在內(nèi)存中的物理地址(通常未使用)。
- p_filesz: 段在文件中的大小。
- p_memsz: 段在內(nèi)存中的大小。
- p_flags: 段的權(quán)限標(biāo)志(例如,可讀、可寫、可執(zhí)行)。
- 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用于描述節(jié)的信息,每個條目包含:
- sh_name: section 名稱字符串表中的索引。
- sh_type: section 類型(例如,SHT_PROGBITS表示程序代碼或數(shù)據(jù)),具體詳見(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 標(biāo)志(例如,可寫、可分配、可執(zhí)行)。
- sh_addr: section 在內(nèi)存中的地址。
- sh_offset: section 節(jié)在文件中的偏移。
- sh_size: section 的大小。
- sh_link: 與section 相關(guān)的鏈接信息。
- sh_info: 附加信息。
- sh_addralign: section 的對齊要求。
- sh_entsize: 如果section 包含固定大小的條目,則為每個條目的大小。
常見的Section
- .text: 包含程序的可執(zhí)行代碼。
- .data: 包含初始化的全局和靜態(tài)變量。
- .bss: 包含未初始化的全局和靜態(tài)變量,實際在文件中不占空間。
- .rodata: 包含只讀數(shù)據(jù),例如字符串常量。
- .symtab: 包含符號表,用于鏈接。
- .strtab: 包含字符串表,通常用于符號表中的符號名稱。
- .rel.text: 包含與.text節(jié)相關(guān)的重定位信息。
- .debug: 包含調(diào)試信息。
Segment
Segment是程序運(yùn)行時的內(nèi)存映射單位,常見的段包括:
- LOAD段: 包含可加載的代碼和數(shù)據(jù),映射到進(jìn)程的虛擬內(nèi)存空間。
- DYNAMIC段: 包含動態(tài)鏈接信息。
- INTERP段: 指定程序運(yùn)行時使用的解釋器(例如,動態(tài)鏈接器)。
- NOTE段: 包含附加信息,如版本信息。
ELF文件結(jié)構(gòu)通過Header、Segment和Section的組合,提供了一個靈活的機(jī)制來描述程序的代碼和數(shù)據(jù)布局。
本項目的PLTHOOK主要關(guān)注的就是DYNAMIC段。





