ELF详解
Wednesday, October 26, 2022
本文共5855字
12分钟阅读时长
⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/principle/elf%E8%AF%A6%E8%A7%A3/。商业转载请联系作者获得授权,非商业转载请注明出处!
Myths which are believed in tend to become true.
— George Orwell
什么是ELF
ELF(Executable and Linkable Format)是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件的文件格式,目前已经成为Unix和类Unix操作系统的标准二进制格式,目前常见的Linux、Android可执行文件、共享库(.so)、目标文件( .o)以及Core 文件均为此格式
使用readelf
命令读取elf文件
readelf -S 查询节头表
-l 查询程序头表
-s 查询符号表
-e 查询ELF文件头数据
-r 查询重定位入口
-d 查询动态段
ELF文件类型
使用ELF格式的文件可以被标记为以下五种格式:
- 未知类型ET_NONE
- 重定位文件ET_REL,通常是还没有经过链接的一段独立代码,也就是.o文件
- 可执行文件ET_EXEC,这个就不用说了~
- 共享目标文件ET_DYN,就是动态链接库
- 核心文件ET_CORE,程序崩溃或产生SIGSEGV信号的时候会在此文件中记录整个进程的镜像信息,从而被GDB读取复盘
我们可以通过readelf -h
命令来查看原始的ELF文件头:
$ readelf -h babyrop
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
类型: EXEC (可执行文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x4004e0
程序头起点: 64 (bytes into file)
Start of section headers: 6768 (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: 31
Section header string table index: 28
readelf
是怎么找到ELF头在哪里呢?答案就是ELF头从二进制文件的0号地址,也就是文件的开头开始,就好像一篇论文在一开始会对整个论文做概述一样,ELF文件头是对剩余部分的一个映射
ELF文件头告诉了我们:
- 这个文件是啥类型的文件
- 这个文件的结构
- 程序开始执行的入口地址
- 其他ELF头(节头、程序头)的地址(偏移量)
ELF程序头(描述段的头)
在原始的ELF头中我们记录了ELF程序头的地址(e_phoff),这样我们就能得到程序头表
ELF程序头是对二进制文件中段的描述,是程序必需的一部分。
我们知道,一个程序想要运行,首先要把它装载到内存上,而这个ELF程序头就相当于一本程序装载的说明书,告诉你每个段是怎样的。
我们可以通过readelf -l
来查询程序头表:
$ readelf -l babyrop
Elf 文件类型为 EXEC (可执行文件)
Entry point 0x4004e0
There are 9 program headers, starting at offset 64
程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 0x8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000814 0x0000000000000814 R E 0x200000
LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x0000000000000240 0x0000000000000248 RW 0x200000
DYNAMIC 0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
0x00000000000001d0 0x00000000000001d0 RW 0x8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 0x4
GNU_EH_FRAME 0x00000000000006e8 0x00000000004006e8 0x00000000004006e8
0x0000000000000034 0x0000000000000034 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x00000000000001f0 0x00000000000001f0 R 0x1
Section to Segment mapping:
段节...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
ELF程序头有一些常见的类型,下面我们来介绍一下它们
PT_LOAD
这类程序头描述的是可以被装载的段,根据这个头,我们将需要的段装载/映射到内存中
一个动态链接的ELF可执行文件通常包括:
- text段:存放程序代码(通常可读可执行PF_R|PF_X)
- data段:全局变量和动态链接信息(通常可读可写PF_R|PF_W)
我们可以更改程序头中p_flags处增加PF_W标记来修改text段权限,从而扭曲原本的代码
PT_DYNAMIC(动态链接可执行文件特有)
它包括了:
- 动态链接的库列表
- GOT表的地址
- 重定位条目的相关信息
- DT_SYMTAB:动态符号表的地址,对应.dynsym节
- DT_HASH:符号散列表的地址,对应.hash节
- DT_STRTAB:符号字符串表的地址,对应.dynstr节
- …
PT_NOTE
特定供应商/系统相关的附加信息
实际上这一段只保存了操作系统的规范信息,在运行的时候是不需要它的
还有一种note段的攻击,但作者实在是没找到相关资料,如果各位读者有相关资料可以参考,烦请在评论区给出链接,感谢您的付出!
PT_INTERP
对程序解释器位置的描述,如,/lib/linux-ld.so.2
一般指动态连接器的位置,也即程序解释器的位置(.so.2文件不是.so文件)
PT_PHDR
保存了程序头表本身的位置和大小,phdr为program header的缩写
我们可以使用readelf -S
命令查看节头表:
$ readelf -S babyrop
There are 31 section headers, starting at offset 0x1a70:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.bu[...] NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 000002b8
0000000000000090 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400348 00000348
000000000000005f 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000004003a8 000003a8
000000000000000c 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000004003b8 000003b8
0000000000000030 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 00000000004003e8 000003e8
0000000000000018 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400400 00000400
0000000000000060 0000000000000018 AI 5 24 8
[11] .init PROGBITS 0000000000400460 00000460
000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000400480 00000480
0000000000000050 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 00000000004004d0 000004d0
0000000000000008 0000000000000000 AX 0 0 8
[14] .text PROGBITS 00000000004004e0 000004e0
00000000000001b2 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 0000000000400694 00000694
0000000000000009 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 00000000004006a0 000006a0
0000000000000047 0000000000000000 A 0 0 8
[17] .eh_frame_hdr PROGBITS 00000000004006e8 000006e8
0000000000000034 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000400720 00000720
00000000000000f4 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000600e10 00000e10
0000000000000008 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000600e18 00000e18
0000000000000008 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 0000000000600e20 00000e20
0000000000000008 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000600e28 00000e28
00000000000001d0 0000000000000010 WA 6 0 8
[23] .got PROGBITS 0000000000600ff8 00000ff8
0000000000000008 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 00001000
0000000000000038 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000601038 00001038
0000000000000018 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000601050 00001050
0000000000000008 0000000000000000 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 00001050
0000000000000035 0000000000000001 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00001964
000000000000010c 0000000000000000 0 0 1
[29] .symtab SYMTAB 0000000000000000 00001088
0000000000000690 0000000000000018 30 47 8
[30] .strtab STRTAB 0000000000000000 00001718
000000000000024c 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),
D (mbind), l (large), p (processor specific)
这里面我们可以注意一下每个节的旗标(flag),如果被标记为A,那么说明该节会在程序运行时分配并装载进内存。有一些节在运行时不是必需的,就不会被装载。比如.symtab节,有时候为了节省空间会把它删掉
段和节的区别
- 段是程序执行的必要组成部分
- 节是段中代码或数据的划分
段是房子的房间,节是房间中的屏风隔断
而所谓节头表就是对这些节的位置和大小的描述(主要用于链接和调试,没有它程序可以正常运行)
节头
如果程序中缺少了节头,并不意味着节消失了;就好像电话簿上某人的号码消失了,并不代表某人的号码不存在了。我们只是不能再通过节头引用节了而已
因此,我们可以人为地删除节头,程序仍然可以正常运行
但是如果没有节头,gdb和objdump这种工具就没用了
.text节
保存了程序代码指令,一般存在text段中
.rodata节
rodata为read-only data的缩写,意为只读数据,这个节因为数据只读,因此不存在data段中,而是在text段中
.data节
.data节保存了初始化的全局变量等数据,存在data段中
.bss节
.bss节中保存了
- 未初始化的全局变量和静态变量
- 在代码中没有明确初始化的全局变量和静态变量
它存在data段中,一般来说它会被初始化为0,但是在运行期间它的值是可能改变的
它为什么被称为bss
呢?请看下图:
bss原本是指 block storage start 块状存储起点(渣翻请勿在意)(或者block started by symbol),但是为了和.data节区分,可以记作 better save space 更优保存空间
可以参考这篇博文
.plt节
plt表,意为procedure linkage table 过程链接表,包含了动态链接器调用从共享库导入的函数所必需的相关代码,保存在text段中
.got与.got.plt节
got意为Global Offsets Table,保存了全局偏移表,保存在text段中,和.plt节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改。
.got和.plt的关系类似于:
.got中存储的是一块空间的地址,这块空间中一开始保存了一个offset用于与.plt中的地址进行计算得到真实地址,然后就会把这个真实地址存入.got指向的那块空间之中
Got.plt is actually smaller subset of the .got section. Think of pointing to the tail end of an array of slots. Conceptually it sort of looks like this
int[10] got; int* gotplt = &got[5];
而.got.plt节是.got的一个子集,
Got section basically can contain addresses of Global variables and functions. All the global variables are in the first couple of slots and suffix is all pointers to functions. gotplt is the first slot .got that contains only the addresses of function.
.got表中包含了全局变量和函数的地址,但.got.plt中只有函数
Eventually after function addresses are resolved via means of plt. The resolved address goes into .gotplt which btw is inside .got as I mentioned earlier.
最终由于.plt表的存在,运行的时候.got.plt中的地址会被转化,而这个.got.plt仍然在.got中
更多请查看此问题
为什么有了.plt还要有.got呢?
因为单纯由plt表没法找到函数的真实地址,我们需要通过got表才能真正地调用函数。
既然如此,为什么不一开始就保存函数的真实地址呢?
因为动态链接器采用默认的延迟链接方式时,不会在第一次调用时就对地址进行解析,这样可以提高装载时的性能
got表的结构
.ctors和.dtors节
- ctors -> Constructor 构造器,是在main函数之前需要执行的函数
- dtors -> Destructor 析构器,是在main函数之后需要执行的函数
它们保存了构造函数和析构函数的指针
ELF符号
符号是对某些类型的数据或者代码的符号引用,实际上就是一个结构体,可以用于链接、重定位、反汇编和调试
typedef struct elf64_sym {
Elf64_Word st_name;
/*
符号名称,字符串表中的索引
STT_OBJECT 表示符号关联到一个数据对象,如变量、数组或指针;
STT_FUNC 表示符号关联到一个函数;
STT_SECTION 表示符号关联到节
STT_NOTYPE 表示符号类型未指定,用于未定义引用
*/
unsigned char st_info;
/*
类型和绑定属性:
STB_LOCAL 本地符号在目标文件之外时不可见的
STB_GLOBAL 对于所有要合并的目标文件都可见
STB_WEAK 类似于STB_GLOBAL,不过优先级较弱,可能会被为标记为STB_WEAK的同名符号覆盖
*/
unsigned char st_other; // 语义未定义,0
Elf64_Half st_shndx; // 相关节的索引,符号将绑定到该节,此外SHN_ABS指定符号是绝对值,不因重定位而改变,SHN_UNDEF标识未定义符号。
Elf64_Addr st_value; // 符号的值
Elf64_Xword st_size; // 符号的长度,如一个指针的长度或struct对象中包含的字节数。
}Elf64_Sym;
我们可以使用readelf -s
命令查看程序符号表:
$ readelf -s babyrop
Symbol table '.dynsym' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [...]@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [...]@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [...]@GLIBC_2.2.5 (2)
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __[...]@GLIBC_2.7 (3)
Symbol table '.symtab' contains 70 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400238 0 SECTION LOCAL DEFAULT 1 .interp
2: 0000000000400254 0 SECTION LOCAL DEFAULT 2 .note.ABI-tag
3: 0000000000400274 0 SECTION LOCAL DEFAULT 3 .note.gnu.build-id
4: 0000000000400298 0 SECTION LOCAL DEFAULT 4 .gnu.hash
5: 00000000004002b8 0 SECTION LOCAL DEFAULT 5 .dynsym
6: 0000000000400348 0 SECTION LOCAL DEFAULT 6 .dynstr
7: 00000000004003a8 0 SECTION LOCAL DEFAULT 7 .gnu.version
8: 00000000004003b8 0 SECTION LOCAL DEFAULT 8 .gnu.version_r
9: 00000000004003e8 0 SECTION LOCAL DEFAULT 9 .rela.dyn
10: 0000000000400400 0 SECTION LOCAL DEFAULT 10 .rela.plt
11: 0000000000400460 0 SECTION LOCAL DEFAULT 11 .init
12: 0000000000400480 0 SECTION LOCAL DEFAULT 12 .plt
13: 00000000004004d0 0 SECTION LOCAL DEFAULT 13 .plt.got
14: 00000000004004e0 0 SECTION LOCAL DEFAULT 14 .text
15: 0000000000400694 0 SECTION LOCAL DEFAULT 15 .fini
16: 00000000004006a0 0 SECTION LOCAL DEFAULT 16 .rodata
17: 00000000004006e8 0 SECTION LOCAL DEFAULT 17 .eh_frame_hdr
18: 0000000000400720 0 SECTION LOCAL DEFAULT 18 .eh_frame
19: 0000000000600e10 0 SECTION LOCAL DEFAULT 19 .init_array
20: 0000000000600e18 0 SECTION LOCAL DEFAULT 20 .fini_array
21: 0000000000600e20 0 SECTION LOCAL DEFAULT 21 .jcr
22: 0000000000600e28 0 SECTION LOCAL DEFAULT 22 .dynamic
23: 0000000000600ff8 0 SECTION LOCAL DEFAULT 23 .got
24: 0000000000601000 0 SECTION LOCAL DEFAULT 24 .got.plt
25: 0000000000601038 0 SECTION LOCAL DEFAULT 25 .data
26: 0000000000601050 0 SECTION LOCAL DEFAULT 26 .bss
27: 0000000000000000 0 SECTION LOCAL DEFAULT 27 .comment
28: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
29: 0000000000600e20 0 OBJECT LOCAL DEFAULT 21 __JCR_LIST__
30: 0000000000400510 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
31: 0000000000400550 0 FUNC LOCAL DEFAULT 14 register_tm_clones
32: 0000000000400590 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
33: 0000000000601050 1 OBJECT LOCAL DEFAULT 26 completed.7594
34: 0000000000600e18 0 OBJECT LOCAL DEFAULT 20 __do_global_dtor[...]
35: 00000000004005b0 0 FUNC LOCAL DEFAULT 14 frame_dummy
36: 0000000000600e10 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_in[...]
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS babyrop.c
38: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
39: 0000000000400810 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__
40: 0000000000600e20 0 OBJECT LOCAL DEFAULT 21 __JCR_END__
41: 0000000000000000 0 FILE LOCAL DEFAULT ABS
42: 0000000000600e18 0 NOTYPE LOCAL DEFAULT 19 __init_array_end
43: 0000000000600e28 0 OBJECT LOCAL DEFAULT 22 _DYNAMIC
44: 0000000000600e10 0 NOTYPE LOCAL DEFAULT 19 __init_array_start
45: 00000000004006e8 0 NOTYPE LOCAL DEFAULT 17 __GNU_EH_FRAME_HDR
46: 0000000000601000 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_
47: 0000000000400690 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
48: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
49: 0000000000601038 0 NOTYPE WEAK DEFAULT 25 data_start
50: 0000000000601050 0 NOTYPE GLOBAL DEFAULT 25 _edata
51: 0000000000400694 0 FUNC GLOBAL DEFAULT 15 _fini
52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND system@@GLIBC_2.2.5
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
54: 0000000000601048 8 OBJECT GLOBAL DEFAULT 25 binsh
55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...]
56: 0000000000601038 0 NOTYPE GLOBAL DEFAULT 25 __data_start
57: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
58: 0000000000601040 0 OBJECT GLOBAL HIDDEN 25 __dso_handle
59: 00000000004006a0 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
60: 0000000000400620 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
61: 0000000000601058 0 NOTYPE GLOBAL DEFAULT 26 _end
62: 00000000004004e0 42 FUNC GLOBAL DEFAULT 14 _start
63: 0000000000601050 0 NOTYPE GLOBAL DEFAULT 26 __bss_start
64: 00000000004005d6 69 FUNC GLOBAL DEFAULT 14 main
65: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
66: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __isoc99_scanf@@[...]
67: 0000000000601050 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__
68: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
69: 0000000000400460 0 FUNC GLOBAL DEFAULT 11 _init
我们可以看到.symtab节中保存了节、数据对象、包括main在内的函数等,这些对我们的调试、反汇编都是非常重要的。
当我们使用objdump
时,每一段代码前面会有一个标注,如下:
$ objdump -d babyrop
babyrop: 文件格式 elf64-x86-64
Disassembly of section .init:
0000000000400460 <_init>:
400460: 48 83 ec 08 sub $0x8,%rsp
400464: 48 8b 05 8d 0b 20 00 mov 0x200b8d(%rip),%rax # 600ff8 <__gmon_start__>
40046b: 48 85 c0 test %rax,%rax
40046e: 74 05 je 400475 <_init+0x15>
400470: e8 5b 00 00 00 call 4004d0 <__gmon_start__@plt>
400475: 48 83 c4 08 add $0x8,%rsp
400479: c3 ret
...
过程入口是每个函数的起点,因此通过检测过程序言(procedure prologue)可以帮助我们找到一个新韩淑的起始位置
如果段是公司中不同的部门,节就是部门中的各小组,而符号表就是一张登记表,把各小组及其成员都登记在表上,如果没有符号表,老板就不知道谁是谁了
.symtab节
保存了符号信息,用于调试和链接,保存了可执行文件的本地符号(全局变量、本地函数等)
.symtab中保存了所有的符号,有很多都是很重要的,如果把他们去掉,虽然严格意义上不会妨碍程序的运行,但是对人来说就难读了很多
.strtab节
保存了符号字符串表,该内容会被.symtab结构引用
.dynsym节
dynsym,dynamic symbols,保存了从共享库导入的动态符号信息,保存在text段中
它是 .symtab的子集,它只保存动态/全局符号
与symtab不同的是,动态链接的二进制文件运行时不能缺少它;symtab可以删掉但它不能。但是如果是静态链接程序,用strip
命令清理后就不会有符号表(dynsym也变成非必需的了)
.dynstr节
保存了动态符号字符串表,就是动态符号的名字
.rel.*节
rel->reacation,重定位节,保存了重定位相关的信息,这些信息描述了如何在链接或运行过程中对部分内容或进程镜像进行补充或修改
.hash节
保存了一个用于查找符号的哈希表(散列表)
.shstrtab节
保存了节头字符串表,也就是每个节头的名字
ELF重定位 Relocation
重定位就是将符号定义和符号引用进行连接的过程。
在重定位文件中,重定位记录保存了如何对给定的符号对应代码进行补充的相关信息,实际上是一种给二进制文件打补丁的机制。
例如,1.o的代码中引用了2.o中的代码,当链接的时候就会对1.o中的重定位记录进行分析,目标文件中的代码会被重定位到可执行文件的段中一个给定的地址,符号引用被解析成了符号定义。
对于共享库的函数,由于延迟链接,只有当一个函数真正被调用时,才会进行GOT重定位
基于二进制修补的重定位代码注入
既然重定位是将符号引用实现的过程,那么我们可以更改可执行文件的符号表,让其指向我们给定的目标文件让其重定位,这样我们的代码就变成了可执行文件的一部分
更多的资料敬请阅读Linux ELF 手册
扫码阅读此文章
点击按钮复制分享信息
点击订阅