《程序员的自我修养》读书笔记---ELF文件结构

<–more–>

文件头

readelf -h 查看ELF文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
wings@sw:~/桌面/0ctf/Print$ readelf -h ./EasiestPrintf
ELF 头:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
类别: ELF32
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: EXEC (可执行文件)
系统架构: Intel 80386
版本: 0x1
入口点地址: 0x8048610
程序头起点: 52 (bytes into file)
Start of section headers: 6588 (bytes into file)
标志: 0x0
本头的大小: 52 (字节)
程序头大小: 32 (字节)
Number of program headers: 9
节头大小: 40 (字节)
节头数量: 29
字符串表索引节头: 26

可能是由于是中文版的,直接给我翻译了。
ELF 文件头中,有ELF 魔数(Magic)、文件机器字节长度,数据存储方式,版本,运行平台,ABI版本,ELF重定位类型,等等等,中文很明显都能看出来了。

ELF 文件头 相关常数定义在“/usr/include/elf.h”,ELF 文件有32版本 和64位版本,文件头内容一样,有些成员不一样。两者文件分别叫”Elf_32Ehdr”和“Elf64_Ehdr”.

书上,以 32位的版本的文件头结构”Elf32_Ehdr”作为例子来描述。定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define EI_NIDENT
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;

发现,文件头结构与之前readefl -h 输出的文件头的结构可以说是一一对应的。

书中尤其提到了的是,Elf32_Ehdr中的e_ident的这个成员对应了结果的“class”,“Data”,“Version”、“OS/ABI”和”API Version”5个参数。

在文章中的文件头结构成员含义中,作者特别单独介绍了魔数,
ELF 魔数
Magic 的16个字节,对应的Elf_Ehdr的这个成员。这16个字节被ELF标准规定用来标识ELF文件的平台属性。

Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

最开始的4个字节,0x7f 0x45 0x4c 0x46 第一个字节对应ASCII字符里的DEL控制符,后3个对应ELF 3个字母的ASCII码。所以这四个字节又称为ELF文件的魔数。


很多类型的文件,其起始的几个字节的内容是固定的(或是有意填充,或是本就如此)。根据这几个字节的内容就可以确定文件类型,因此这几个字节的内容被称为魔数 (magic number)。此外在一些程序代码中,程序员常常将在代码中出现但没有解释的数字常量或字符串称为魔数 (magic number)或魔字符串。


紧接着第一个字节用来标识ELF 文件类的,0x01表示32位,0x02表示64位的,第6个字节是字节序。规定ELF文件是大端还是小端


大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。


第7个字节规定ELF文件的主版本号,一般是1(ELF 标准目前更新到1.2),后面的9个字节一般为0.

段表

段表(Section Header Table),保存ELF文件段的基本属性。描述ELF文件各个段的信息:段名、段的长度、在文件中的偏移、读写权限以及段的其他属性。ELF文件的段结构由段表决定,段表由ELF文件头的“e_shoff” 成员决定。


我们可以用objdump -h来查看 ELF 包含的段,但是这个命令知识显示关键段


书中,告诉我们readefl工具能完整的显示段表结构“”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
wings@sw:~/桌面/0ctf/Print$ readelf -S EasiestPrintf
共有 29 个节头,从偏移量 0x19bc 开始:
节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 0000ac 04 A 5 0 4
[ 5] .dynsym DYNSYM 08048258 000258 000140 10 A 6 1 4
[ 6] .dynstr STRTAB 08048398 000398 0000ca 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048462 000462 000028 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0804848c 00048c 000040 00 A 6 1 4
[ 9] .rel.dyn REL 080484cc 0004cc 000090 08 A 5 0 4
[10] .init PROGBITS 0804855c 00055c 000023 00 AX 0 0 4
[11] .plt PROGBITS 08048580 000580 000010 04 AX 0 0 16
[12] .plt.got PROGBITS 08048590 000590 000078 00 AX 0 0 8
[13] .text PROGBITS 08048610 000610 000392 00 AX 0 0 16
[14] .fini PROGBITS 080489a4 0009a4 000014 00 AX 0 0 4
[15] .rodata PROGBITS 080489b8 0009b8 000044 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 080489fc 0009fc 00003c 00 A 0 0 4
[17] .eh_frame PROGBITS 08048a38 000a38 000108 00 A 0 0 4
[18] .init_array INIT_ARRAY 08049ecc 000ecc 000004 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 08049ed0 000ed0 000004 00 WA 0 0 4
[20] .jcr PROGBITS 08049ed4 000ed4 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049ed8 000ed8 0000e0 08 WA 6 0 4
[22] .got PROGBITS 08049fb8 000fb8 000048 04 WA 0 0 4
[23] .data PROGBITS 0804a000 001000 000008 00 WA 0 0 4
[24] .bss NOBITS 0804a020 001008 00002c 00 WA 0 0 32
[25] .comment PROGBITS 00000000 001008 000034 01 MS 0 0 1
[26] .shstrtab STRTAB 00000000 0018bc 0000fd 00 0 0 1
[27] .symtab SYMTAB 00000000 00103c 000540 10 28 47 4
[28] .strtab STRTAB 00000000 00157c 000340 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

我的这个文件有29个段,Type描述了段的类型。书中告诉我们,段表是以“Elf32_Shdr”结构体为元素的数组。

##### 段的类型(shtype)
段的类型常量,以`SHT
`开头。

段的标志位

段的标志位表示段在进程虚拟地址空间中的属性,比如是否可写,是否可执行等。相关常量以SHF_开头

段的类型与链接相关,如重定位表,符号表,这两个成员就有意义。

重定位表