ELF(Executable and Linkable Format)即可执行的和可链接的格式。
出于方便性和效率考虑,ELF文件提供了两种并行视图,即链接视图,执行视图。
下面我们使用 readelf
来读取ls
的文件信息
以Magic Str开头,描述文件类型,abi, version等信息,并提供到Program Header Table*和 Section Header Table的索引。
[root@localhost tgt]# readelf -h /bin/ls
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: DYN (共享目标文件)
系统架构: AArch64
版本: 0x1
入口点地址: 0x5914
程序头起点: 64 (bytes into file)
Start of section headers: 197648 (bytes into file)
标志: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 28
Section header string table index: 27
存在于可执行文件或共享库中,是一个结构体组成的数组。一般在ELF Header之后。每个数组元素描述一个段(segment)或者其他程序执行时需要的信息;该结构用于执行阶段。
[root@localhost tgt]# readelf -l /bin/ls
Elf 文件类型为 DYN (共享目标文件)
Entry point 0x5914
There are 9 program headers, starting at offset 64
程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R 0x8
INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x000000000000001b 0x000000000000001b R 0x1
[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000020780 0x0000000000020780 R E 0x10000
LOAD 0x000000000002ef30 0x000000000003ef30 0x000000000003ef30
0x0000000000001380 0x0000000000002648 RW 0x10000
DYNAMIC 0x000000000002f948 0x000000000003f948 0x000000000003f948
0x0000000000000220 0x0000000000000220 RW 0x8
NOTE 0x0000000000000254 0x0000000000000254 0x0000000000000254
0x0000000000000044 0x0000000000000044 R 0x4
GNU_EH_FRAME 0x000000000001c914 0x000000000001c914 0x000000000001c914
0x000000000000093c 0x000000000000093c R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x000000000002ef30 0x000000000003ef30 0x000000000003ef30
0x00000000000010d0 0x00000000000010d0 R 0x1
Section to Segment mapping:
段节...
00
01 .interp
02 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .data.rel.ro .dynamic .got .data .bss
04 .dynamic
05 .note.gnu.build-id .note.ABI-tag
06 .eh_frame_hdr
07
08 .init_array .fini_array .data.rel.ro .dynamic .got
索引每个section的位置,该结构用于链接阶段。
[root@localhost tgt]# readelf -S /bin/ls
There are 28 section headers, starting at offset 0x30410:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000238 00000238
000000000000001b 0000000000000000 A 0 0 1
[ 2] .note.gnu.build-i NOTE 0000000000000254 00000254
0000000000000024 0000000000000000 A 0 0 4
[ 3] .note.ABI-tag NOTE 0000000000000278 00000278
0000000000000020 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 00000298
0000000000000040 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000000002d8 000002d8
0000000000000c90 0000000000000018 A 6 3 8
[ 6] .dynstr STRTAB 0000000000000f68 00000f68
00000000000005e4 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 000000000000154c 0000154c
000000000000010c 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000001658 00001658
0000000000000070 0000000000000000 A 6 3 8
[ 9] .rela.dyn RELA 00000000000016c8 000016c8
00000000000016f8 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000002dc0 00002dc0
0000000000000a98 0000000000000018 AI 5 22 8
[11] .init PROGBITS 0000000000003858 00003858
0000000000000014 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000003870 00003870
0000000000000730 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000003fa0 00003fa0
0000000000013fa8 0000000000000000 AX 0 0 8
[14] .fini PROGBITS 0000000000017f48 00017f48
0000000000000010 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 0000000000017f60 00017f60
00000000000049b4 0000000000000000 A 0 0 16
[16] .eh_frame_hdr PROGBITS 000000000001c914 0001c914
000000000000093c 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 000000000001d250 0001d250
0000000000003530 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 000000000003ef30 0002ef30
0000000000000008 0000000000000008 WA 0 0 8
[19] .fini_array FINI_ARRAY 000000000003ef38 0002ef38
0000000000000008 0000000000000008 WA 0 0 8
[20] .data.rel.ro PROGBITS 000000000003ef40 0002ef40
0000000000000a08 0000000000000000 WA 0 0 8
[21] .dynamic DYNAMIC 000000000003f948 0002f948
0000000000000220 0000000000000010 WA 6 0 8
[22] .got PROGBITS 000000000003fb68 0002fb68
0000000000000498 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000040000 00030000
00000000000002b0 0000000000000000 WA 0 0 8
[24] .bss NOBITS 00000000000402b0 000302b0
00000000000012c8 0000000000000000 WA 0 0 8
[25] .gnu.build.attrib NOTE 0000000000041578 000302b0
0000000000000024 0000000000000000 0 0 4
[26] .gnu_debuglink PROGBITS 0000000000000000 000302d4
0000000000000028 0000000000000000 0 0 4
[27] .shstrtab STRTAB 0000000000000000 000302fc
0000000000000113 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
节区名 | 介绍 |
---|---|
[.text] | .text 节是保存了程序代码指令的代码节。一段可执行程序,如果存在Phdr,.text 节就会存在于text 段中。由于.text 节保存了程序代码,因此节的类型为SHT_PROGBITS。 |
[.dynsym] | 保存了引用来自外部文件符号的全局符号,如printf 这样的库函数。.dynsym 是被标记了ALLOC 的,在运行时分配并装载进入内存,因此动态链接可执行文件的执行来说是必需的。该节保存在text 段中,节类型被标记为SHT_DYNSYM。 |
[.rodata] | .rodata 节保存了只读的数据,如一行C 语言代码中的字符串。下面这条命令就是存放在.rodata 节中的: printf(“Hello World!\n”);因为.rodata 节是只读的,所以只能存在于一个可执行文件的只读段中。因此,只能在text 段(不是data 段)中找到.rodata 节。由于.rodata 节是只读的,因此节类型为SHT_PROGBITS。 |
[.plt] | 程序链接表(Procedure Linkage Table,PLT)包含了动态链接器调用从共享库导入的函数所必需的相关代码。由于其存在于text 段中,同样保存了代码,因此节类型为SHT_PROGBITS。 |
[.data] | 不要将.data 节和data 段混淆了,.data 节存在于data 段中,保存了初始化的全局变量等数据。由于其保存了程序的变量数据,因此类型被标记为SHT_PROGBITS。 |
[.bss] | .bss 节保存了未进行初始化的全局数据,是data 段的一部分,占用空间不超过4 字节,仅表示这个节本身的空间。程序加载时数据被初始化为0,在程序执行期间可以进行赋值。由于.bss 节未保存实际的数据,因此节类型为SHT_NOBITS。 |
[.got.plt] | .got 节保存了全局偏移表。.got 节和.plt 节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改。如果攻击者获得了堆或者.bss 漏洞的一个指针大小的写原语,就可以对该节任意进行修改。.got.plt 节跟程序执行有关,因此节类型被标记为SHT_PROGBITS。 |
[.symtab] | .symtab 中保存了可执行文件的本地符号,如全局变量,或者代码中定义的本地函数等。只是用来进行调试和链接的,有时候为了节省空间,会将.symtab 符号表从生产二进制文件中删掉 |
参考资料: