ELF文件解析-文件头/段内容解析
ELF: 可执行连接格式
可执行连接格式是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface(ABI)而开发和发布的。工具接口标准委员会(TIS)选择了正在发展中的ELF标准作为工作在32位INTEL体系上不同操作系统之间可移植的二进制文件格式。
假定开发者定义了一个二进制接口集合,ELF标准用它来支持流线型的软件发展。应该减少不同执行接口的数量。因此可以减少重新编程重新编译的代码。
ELF Header
#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;
示例:
EI_MAG0 to EI_MAG3(Magic的前四位):
保存着魔术字,用于确定该文件是否为ELF的目标文件。
Name Value Position ==== ===== ======== ELFMAG0 0x7f e_ident[EI_MAG0] ELFMAG1 'E' e_ident[EI_MAG1] ELFMAG2 'L' e_ident[EI_MAG2] ELFMAG3 'F' e_ident[EI_MAG3]
EI_CLASS(Class):
说明文件的类型。
Name Value Meaning ==== ===== ======= ELFCLASSNONE 0 Invalid class ELFCLASS32 1 32-bit objects ELFCLASS64 2 64-bit objects
EI_DATA(Data):
指定了在object文件中特定处理器数据的编码方式。当前定义了以下编码方式。
Name Value Meaning ==== ===== ======= ELFDATANONE 0 Invalid data encoding ELFDATA2LSB 1 See below ELFDATA2MSB 2 See below
e_type(Type):
指出object的类型:
Name Value Meaning ==== ===== ======= ET_NONE 0 No file type ET_REL 1 Relocatable file ET_EXEC 2 Executable file ET_DYN 3 Shared object file ET_CORE 4 Core file ET_LOPROC 0xff00 Processor-specific ET_HIPROC 0xffff Processor-specific
e_machine(Machine):
指出该程序需要的体系结构。
Name Value Meaning ==== ===== ======= EM_NONE 0 No machine EM_M32 1 AT&T WE 32100 EM_SPARC 2 SPARC EM_386 3 Intel 80386 EM_68K 4 Motorola 68000 EM_88K 5 Motorola 88000 EM_860 7 Intel 80860 EM_MIPS 8 MIPS RS3000
e_version(Version):
确定object文件的版本,1表示原来的文件格式,创建新的版本就用大于1的数
Name Value Meaning ==== ===== ======= EV_NONE 0 Invalid version EV_CURRENT 1 Current version
e_entry(Entry point address):
系统第一个传输控制的虚拟地址,在那启动进程。假如文件没有如何关联的入口点,该成员就保持为0。e_flags(flags):
保存着相关文件的特定处理器标志。
e_ehsize(size of section headers):
保存着ELF头大小(以字节计数)
e_phentsize(size of program headers)
保存着在文件的程序头表(program header table)中一个入口的大小(以字节计数)。所有的入口都是同样的大小。
e_phnum(number of program headers):
保存着在程序头表中入口的个数。因此,e_phentsize和e_phnum的乘机就是表的大小(以字节计数).假如没有程序头表(program header table),
e_phnum变量为0。e_shentsize(Size of section headers):
保存着section头的大小(以字节计数)。一个section头是在section头表(section header table)的一个入口;所有的入口都是同样的大小。
e_shnum(number of section headers):
保存着在section header table中的入口数目。因此,e_shentsize和e_shnum的乘积就是section头表的大小(以字节计数)。假如文件没有section头表,e_shnum值为0。
e_shstrndx(Section header string table index )
保存着跟section名字字符表相关入口的section头表(section header table)索引。假如文件中没有section名字字符表,该变量值为SHN_UNDEF。更详细的信息 看下面“Sections”和字符串表(“String Table”) 。
ELF Section
Section Header
typedef struct {
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;
} Elf32_Shdr;
sh_name(Name):
这个section的名字。它的值是section报头字符表section的索引。, 以NULL空字符结束。
sh_type(Type):
Name Value ==== ===== SHT_NULL 0 该section头是无效的;它没有相关的section。 SHT_PROGBITS 1 该section保存被程序定义了的一些信息,它的格式和意义取决于程序本身。 SHT_SYMTAB 2 sections保存着一个符号表(symbol table) SHT_STRTAB 3 该section保存着一个字符串表。 SHT_RELA 4 该section保存着具有明确加数的重定位入口。 SHT_HASH 5 该标号保存着一个标号的哈希(hash)表 SHT_DYNAMIC 6 该section保存着动态连接的信息。 SHT_NOTE 该section保存着其他的一些标志文件的信息 SHT_NOBITS 8 该类型的section在文件中不占空间,但是类似SHT_PROGBITS SHT_REL 9 该section保存着具有明确加数的重定位的入口 SHT_SHLIB 10 该section类型保留但语意没有指明。包含这个类型的section的程序 是不符合ABI的。(应用程序二进制接口) SHT_DYNSYM 11 保存着一个动态连接时所需最小的标号集合来节省空间 SHT_LOPROC 0x70000000 在这范围之间的值为特定处理器语意保留的。 SHT_HIPROC 0x7fffffff 在这范围之间的值为特定处理器语意保留的。 SHT_LOUSER 0x80000000 为应用程序保留的索引范围的最小边界 SHT_HIUSER 0xffffffff 为应用程序保留的索引范围的最大边界
sh_flags(FLG): 支持位的标记,用来描述多个属性。
sh_addr(Addr):
出了一个该section在内存中的位置。若不出现在进程的内存映象空间里该变量为0。
sh_offset(Off):
section的字节偏移量(从文件开始计数)
sh_size(Size):
section的字节大小。除非这个section的类型为SHT_NOBITS,否则该section将在文件中将占sh_size个字节。SHT_NOBITS类型的section可能为非0的大小,但是不占文件空间。
sh_link(Lk):
保存了一个section报头表的索引连接,它的解释依靠该section的类型。以下一个表描述了这些值。
sh_info(Inf):
保存着额外的信息,它的解释依靠该section的类型。以下一个表描述了这些值。
sh_addralign(AL):
一些sections有地址对齐的约束。例如,假如一个section保存着双字,系统就必须确定整个section是否双字对齐。所以sh_addr的值以sh_addralign的值作为模,那么一定为0。当然的,仅仅0和正的2的次方是允许的。值0和1意味着该section没有对齐要求。
sh_entsize(ES):
一些sections保存着一张固定大小入口的表,就象符号表。
Special Sections
Section Headers:
Name Type
NULL
.interp PROGBITS
.note.ABI-tag NOTE
.note.gnu.build-i NOTE
.gnu.hash GNU_HASH
.dynsym DYNSYM
.dynstr STRTAB
.gnu.version VERSYM
.gnu.version_r VERNEED
.rel.dyn REL
.rel.plt REL
.init PROGBITS
.plt PROGBITS
.plt.got PROGBITS
.text PROGBITS
.fini PROGBITS
.rodata PROGBITS
.eh_frame_hdr PROGBITS
.eh_frame PROGBITS
.init_array INIT_ARRAY
.fini_array FINI_ARRAY
.jcr PROGBITS
.dynamic DYNAMIC
.got PROGBITS
.got.plt PROGBITS
.data PROGBITS
.bss NOBITS
.comment PROGBITS
.shstrtab STRTAB
.bss:
保存着未初始化的数据,这些数据存在于程序内存映象中。通过定义,当程序开始运行,系统初始化那些数据为0。该section不占文件空间,正如它的section类型SHT_NOBITS指示的一样。
.comment
保存着版本控制信息。
.data and .data1
保存着初始化了的数据,那些数据存在于程序内存映象中。
.debug
保存着为标号调试的信息。该内容是未指明的。
.dynamic
保存着动态连接的信息。该section的属性将包括SHF_ALLOC位。是否需要SHF_WRITE是跟处理器有关。第二部分有更详细的信息。
.dynstr
保存着动态连接时需要的字符串,一般情况下,名字字符串关联着符号表的入口。第二部分有更详细的信息。
.dynsym
保存着动态符号表,如“Symbol Table”的描述。第二部分有更详细的信息。
.fini
保存着可执行指令,它构成了进程的终止代码。因此,当一个程序正常退出时,系统安排执行这个section的中的代码。
.got
保存着全局的偏移量表。看第一部分的“Special Sections”和第二部分的“Global Offset Table”获得更多的信息。
.hash
保存着一个标号的哈希表。
.init
保存着可执行指令,它构成了进程的初始化代码。因此,当一个程序开始运行时,在main函数被调用之前(c语言称为main),系统安排执行这个section的中的代码。
.interp
保存了程序的解释程序(interpreter)的路径。假如在这个section中有一个可装载的段,那么该section的属性的SHF_ALLOC位将被设置;否则,该位不会被设置。
.line
包含编辑字符的行数信息,它描述源程序与机器代码之间的对于关系。该section内容不明确的。
.note
该section保存一些信息。
.plt
该section保存着过程连接表(Procedure Linkage Table)。
.rel and .rela
这些section保存着重定位的信息
.rodata and .rodata1
保存着只读数据,在进程映象中构造不可写的段。
.shstrtab
该section保存着section名称。
.strtab
保存着字符串,一般地,描述名字的字符串和一个标号的入口相关联。
.symtab
该section保存着一个符号表,正如在这个section里“Symbol Table’’的描述。
.text
保存着程序的“text’’或者说是可执行指令。
以上就是ELF文件中的文件头和段头的内容后续还有很多内容需要了解。未必需要完全记忆,但是理解和了解是必要的。
参考文章
《CTF特训营》
http://blog.chinaunix.net/uid-1835494-id-2831795.html