20135218 实践四 ELF文件格式分析
一 :概述
ELF全称Executable and Linkable Format,可执行连接格式,ELF格式的文件用于存储Linux程序。ELF文件(目标文件)格式主要三种:
(1)可重定向文件:文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件。
(目标文件或者静态库文件,即linux通常后缀为.a和.o的文件)
(2)可执行文件:文件保存着一个用来执行的程序。(例如bash,gcc等)
(3)共享目标文件:共享库。文件保存着代码和合适的数据,用来被下连接编辑器和动态链接器链接。(linux下后缀为.so的文件)
一般的 ELF 文件包括三个索引表:ELF header,Program header table,Section header table。
- ELF header:在文件的开始,保存了路线图,描述了该文件的组织情况。
- Program header table:告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。
- Section header table:包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。 用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。
-
e_type表示文件类型,2表示可执行文件。
e_machine:指明可以在哪种机器结构中运行。
e_version:指明版本信息
e_entry:指明系统运行该程序时将控制权转交到的虚拟地址的值,如果没有则为零。
e_phoff: program header table在文件中的字节(Byte)偏移offset,如果没有program header table, 则该值为零。
e_shoff: section header table在文件中的字节偏移,如果没有section header table, 则该值为零
e_flags: 有关处理器的信息
e_ehsize: elf header的大小,单位:字节
e_phentsize: 在program header table中一个entry的大小,前面提到过,program header table & section header table都是数组,所以它们的每一个元素,即每一个entry的大小,都是一样的。
e_phnum: program header table中元素的个数,即entry的个数。
e_shentsize: section header table每一个entry的大小,与e_phentsize类似。
e_shnum: section header table中元素的个数,即entry的个数。可以看出来,这个program header table或者section header table的大小可以用entry的个数乘以每一个entry的大小得到。
e_shstrndx: 指明string name table在section header table中的index。
二:分析ELF文件头。
1. 进入终端输入:cd /usr/include 进入include文件夹后查看elf1.h文件,查看ELF的文件头包含整个文件的控制结构。
2. 写一个简单的代码,进行编译运行,生成elf可执行文件。
使用‘readelf –a elf1’命令,都得到下面的ELF Header头文件的信息,如下图:
3.如下图使用:‘hexdump –x elf1 –n ’命令来查看elf1文件头的16进制表对格式进行分析。
- 第一行,对应e_ident[EI_NIDENT]。实际表示内容为7f454c46010101000000000000000000;前四个字节7f454c46(0x45,0x4c,0x46是'e','l','f'对应的ascii编码)是一个魔数,表示这是一个ELF对象。接下来的一个字节01表示是一个32位对象,接下来的一个字节01表示是小端法表示,再接下来的一个字节01表示文件头版本。剩下的默认都设置为0.
- 第二行,e_type值为0x0002,表示是一个可执行文件。(若为0x0001,表示重定位文件)。
e_machine值(2字节)为0x0003,说明是intel 80386。
e_version值(4字节)为0x00000100,表示是当前版,0是非法版本。e_entry值为0x8048320 ,表示入口点。
- 第三行,e_shoff值为0x114c,表示段表的偏移地址。
e_phoff(4字节)值为0x00000034 ,表示程序头表
e_flags(4字节)0x00000000,表示未知处理器特点标志(#define EF_SH_UNKNOWN 0x0)
e_ehsize(2字节)0x0034,表示ELF文件头大小为0x34H,(64位的是0x40H)
e_phentsize(2字节)0x0020,program header的大小是32比特。
e_phnum(2字节) 0x0009,program headers的数量是9个
e_ ehentsize(2字节) 0x0028,表示段头大小为40字节(由此可知section header table 里面表个header的大小)
e_ shnum(2字节) 0x001e,表示段表入口地址有30个(由此知道段表有30个段)
e_shstrndx (2字节) 0x001b,表示段名串表的在段表中的索引号(由此知.shstrtab段(符号表)的信息在段表的索引号是27)
4.使用readelf -h elf1查看ELF文件头,将上述分析结果进行对比。
三、通过文件头找到section header table,理解其内容。
file elf1显示生成的目标文件elf1的类型
elf1是一个可执行文件。输入:ls –l elf1查看elf1的大小:
输入:hexdump –x elf1来用16进制的数字来显示elf1的内容(第二列是16进制表示的偏移地址)
输入:objdump –x elf1来显示elf1中各个段以及符号表的相关信息:program header:程序头。
输入:readelf –a hello来查看各个段信息:
section header table:
符号表 Symbol table:
四、通过section header table找到各section
在一个ELF文件中有一个section header table,通过它我们可以定位到所有的 section,而 ELF header 中的e_shoff 变量就是保存 section header table 入口对文件头的偏移量。而每个 section 都会对应一个 section header ,所以只要在 section header table 中找到每个 section header,就可以通过 section header 找到你想要的 section。
使用‘vi /usr/include/elf1.h ’命令查看Sections Header的结构体:
由之前文件头的分析可知:e_shoff(4字节)值为0x0000114C,表示段表的偏移地址
e_ ehentsize(2字节) 0x0028,表示段头大小为40字节(由此可知section header table 里面表个header的大小)
e_ shnum(2字节) 0x001e,表示段表入口地址有30个(由此知道段表有30个段)
段表大小 28H*1e=08B0
所以从0x0000114C—0X000019FC,存储的是段表。
- 第一个段,其中内容全部为0,所以不表示任何段。
- 第二个段,为.interp段
- 第三个段,为.note.ABI-tag段。
- 第四个段,为.note.gnu.build-i段。
- 第五个段,为.gnu.hash段。
我们用readelf 命令去查看.text这个 section 中的内容,
输入readelf –x 13 elf1,(.text前面的标号为13)对13索引号的.text的section的内容进行查看:
下面用 hexdump 的方法去读取.text这个 section 中的内容,通过看section header中.text中offset和size分别是0x000320和0x0001b2,通过16进制向10进制转换得到 offset:800和size:418。
输入 hexdump –s 800 –n 418 –C elf1
得到了和上面的readelf得到的相同。
使用下面命令对elf1的文本段(.text)进行反汇编:
objdump –d elf1得到如下图,可以看出,使用反汇编的16进制数据和前面查找到的是相同的。
五、理解常见.text .strtab .symtab .rodata等section
①.text section是可执行指令的集合,.data和.text都是属于PROGBITS类型的section,这是将来要运行的程序与代码。查询段表可知.text section的位偏移为0x8048320,size为0x0001b2。
②.strtab section是属于STRTAB类型的section,可以在文件中看到,它存着字符串,储存着符号的名字。位偏移为0x0000000,size为0x000251。
③.symtab section存放所有section中定义的符号名字,比如“data_items”,“start_loop”。 .symtab section是属于SYMTAB类型的section,它描述了.strtab中的符号在“内存”中对应的“内存地址”。 位偏移为0x00000000,size0x000430为。
④.rodata section,ro代表read only。位偏移为0x08048e8,size为0x000010。