一、ELF文件格式概述
ELF(Executable and Linkable Format)是一种可执行文件和可链接文件格式,被广泛地应用于Unix和类Unix系统中。ELF格式具有以下特点:
1、二进制格式,支持可执行文件、共享目标文件、目标文件等多种类型;
2、通过段(Section)概念对文件进行组织,段包含代码、数据、符号表等信息;
3、支持动态链接,共享目标文件可以在运行时被加载,共享;
4、可通过读取和修改ELF文件中的信息,实现二进制文件的混淆、加密等操作。
二、ELF文件格式组成
一个ELF文件主要包含三个部分:文件头(ELF header)、节区表(Section header table)和节区(Sections)。
1、文件头(ELF Header)
文件头定义了整个ELF文件的组织结构和属性信息,包括标识、目标类型、节区表的位置和大小等。文件头的结构定义如下:
struct Elf32_Ehdr { 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; };
2、节区表(Section Header Table)
节区表是一张记录了整个ELF文件中每个节区的信息的表格。节区表一般位于ELF文件的开头末尾,节区表的信息包括节区的名称、偏移地址、大小等。节区表的结构定义如下:
struct Elf32_Shdr { Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; };
3、节区(Sections)
每个节区对应一个文件中的逻辑块,例如代码段、数据段、符号表段、字符串表段等等。节区的定义如下:
struct elf_section { Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; void *data; };
三、ELF文件格式分析
1、ELF文件标识(ELF Identification)
在ELF文件中,前4个字节称为文件的魔数,标识文件的类型。ELF文件标识的结构定义如下:
#define EI_NIDENT 16 struct Elf32_Ehdr { unsigned char e_ident[EI_NIDENT]; //... };
e_ident数组前四个字节是文件魔数,分别为0x7F、’E’、’L’、’F’。文件是否是64位还是32位,处理器类型等信息,也在e_ident表中。
2、节区表(Section Header Table)
节区表是定义每个节(Section)的一张表,表中包含了每个节的信息,如名称、大小、对齐方式等。节区表的格式由Elf32_Shdr或Elf64_Shdr表示。
ELF文件中,标准的节区表通常包含以下几个节区:
代码段(.text):存放程序的机器代码,只读不可写。
数据段(.data):存放程序中已初始化的全局变量和静态变量,可读可写。
只读数据段(.rodata):存放程序中的只读数据,如字符串常量等,只读不可写。
符号表(.symtab):存放程序中的全局符号信息,符号表中包含符号名、符号类型、符号地址等。
字符串表(.strtab):存放程序中使用的字符串常量的名称。
重定位表(.rel.text):用于对代码段进行重定位操作。
3、程序头表(Program Header(TWo-level))
程序头表由Elf32_Phdr或Elf32_Phdr数组表示,其名称中的“P”代表“Program”,也就是程序头表。在程序执行时,程序头表会被内核解析并用来对整个进程的映像文件进行映像。
struct Elf32_Phdr { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; };
4、重定位(Relocation)
重定位是指将静态编译的程序映射到内存中执行时,因为内存的地址可能与链接时的地址不同,需要对代码中的地址进行调整,以便代码可以在内存中正常运行。这个过程称为重定位(Relocation),在ELF中,重定位相关的信息保存在重定位表(.rel.text)和重定位表(.rel.data)中。
5、链接(Linking)
链接(Linking)是指将多个目标文件和库,最终链接成一个可执行文件或共享目标文件的过程。链接器会将多个目标文件的节合并为一个文件,并进行符号重定位等处理。
四、ELF文件格式示例
1、C语言程序生成ELF文件示例
// demo.c #include void main() { printf("Hello World!\n"); }
使用gcc生成可执行程序demo:
gcc -o demo demo.c
使用objdump查看demo的汇编代码:
objdump -d demo
通过objdump生成的结果可以看到,ELF二进制文件包含了很多信息,包括符号表、重定位表、反汇编的代码等信息。
2、动态链接库(shared object)生成ELF文件示例
多个进程间同时使用同一个共享库,可以减少内存使用,应用程序所用的内存被调用库的代码部分所共享,因为在内存中只有一份副本。所以,动态链接库的好处就在于节约了内存。以下是使用gcc来创建动态链接库:
// demo.c #include void foo() { printf("Hello World"); } void bar() { printf("Hello Bar"); }
编译动态链接库:
gcc -shared -o libdemo.so demo.c
编译完成后会生成一个名为libdemo.so的动态链接库文件,接下来需要将生成的动态库文件放到OS默认的动态库搜索目录中(例如/usr/lib目录)。
程序可以通过使用动态链接库进行编译,而不需要手动将库打包进可执行文件中:
// main.c #include #include int main() { void *handle; void (*foo)(), (*bar)(); handle = dlopen("libdemo.so", RTLD_LAZY); if (handle == NULL) { printf("Failed to open library.\n"); return 1; } foo = dlsym(handle, "foo"); bar = dlsym(handle, "bar"); foo(); bar(); dlclose(handle); return 0; }
编译可执行程序main:
gcc -o main main.c -ldl
动态链接库和可执行程序main都是ELF文件格式。
五、ELF文件格式的应用
在Linux/BSD等类UNIX系统中,ELF文件格式被广泛应用于可执行程序、共享库、目标文件等类型的二进制文件。ELF文件格式将程序代码、数据、符号表、重定位等信息完整地保存在二进制文件中,为程序的开发、调试、优化提供了很好的基础。
同时,ELF文件格式也被用于二进制代码混淆、加密等操作中,通过修改ELF文件中的信息,可以达到有效保护程序机密性的目的。
六、总结
本文对ELF文件格式进行了详细的阐述,介绍了ELF文件格式的组成、特点以及应用。ELF文件格式是Unix和类Unix系统中常用的二进制文件格式,通过ELF格式,程序的开发、调试、优化等方面得到了很好的支持。同时,ELF文件格式也为程序的保护提供了很好的基础。
原创文章,作者:OCIGZ,如若转载,请注明出处:https://www.506064.com/n/362077.html