​ 2023.7.8更新:很早之前已经写了这篇博客,但是只学了一点皮毛..只在表面,后面学到了ret2dlresolve漏洞发现需要理解这部分东西,又回炉重造,添加了需要学习的更细致的知识

符号解析

基础知识

参考ctfwiki(wiki中是倒推的,感觉理解起来不如正推)

​ 在 ELF 文件中,对于每一个需要重定位的 ELF 节都有对应的重定位表,比如说 .text 节如果需要重定位,那么其对应的重定位表为 .rel.text。 所以.rel.plt就是plt节需要重定位所产生的节了.

​ .rel.plt中就会包含一个指向这个符号的重定位表项. r_offset给出了要修改的位置,r_info给出了要修改的符号的符号表索引,所以r_info索引到了 .dynsym

​ .dynsym中,st_name保存着动态符号在dynstr中的偏移

​ .dynstr又是怎么来的呢? 答:当一个程序导入某个函数时,.dynstr就会包含对应函数名称的字符串(最后是根据这个字符串名字来进行解析的!!!)

image-20230708162249130

以 ret2dl中的例子来说,可以用readelf查看这些节(但在ida中不会显示的这么全,会放到LOAD段里)

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
38
39
40
41
root@vultr:~/ret2dl# readelf -S main_partial_relro_32
There are 31 section headers, starting at offset 0x3848:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 080481b4 0001b4 000013 00 A 0 0 1
[ 2] .note.gnu.build-i NOTE 080481c8 0001c8 000024 00 A 0 0 4
[ 3] .note.gnu.propert NOTE 080481ec 0001ec 00001c 00 A 0 0 4
[ 4] .note.ABI-tag NOTE 08048208 000208 000020 00 A 0 0 4
[ 5] .gnu.hash GNU_HASH 08048228 000228 000020 04 A 6 0 4
[ 6] .dynsym DYNSYM 08048248 000248 0000a0 10 A 7 1 4
[ 7] .dynstr STRTAB 080482e8 0002e8 00006b 00 A 0 0 1
[ 8] .gnu.version VERSYM 08048354 000354 000014 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 08048368 000368 000020 00 A 7 1 4
[10] .rel.dyn REL 08048388 000388 000018 08 A 6 0 4
[11] .rel.plt REL 080483a0 0003a0 000028 08 AI 6 24 4
[12] .init PROGBITS 08049000 001000 000024 00 AX 0 0 4
[13] .plt PROGBITS 08049030 001030 000060 04 AX 0 0 16
[14] .plt.sec PROGBITS 08049090 001090 000050 10 AX 0 0 16
[15] .text PROGBITS 080490e0 0010e0 000289 00 AX 0 0 16
[16] .fini PROGBITS 0804936c 00136c 000018 00 AX 0 0 4
[17] .rodata PROGBITS 0804a000 002000 000008 00 A 0 0 4
[18] .eh_frame_hdr PROGBITS 0804a008 002008 000054 00 A 0 0 4
[19] .eh_frame PROGBITS 0804a05c 00205c 000150 00 A 0 0 4
[20] .init_array INIT_ARRAY 0804bf04 002f04 000004 04 WA 0 0 4
[21] .fini_array FINI_ARRAY 0804bf08 002f08 000004 04 WA 0 0 4
[22] .dynamic DYNAMIC 0804bf0c 002f0c 0000e8 08 WA 7 0 4
[23] .got PROGBITS 0804bff4 002ff4 00000c 04 WA 0 0 4
[24] .got.plt PROGBITS 0804c000 003000 000020 04 WA 0 0 4
[25] .data PROGBITS 0804c020 003020 000008 00 WA 0 0 4
[26] .bss NOBITS 0804c028 003028 000004 00 WA 0 0 1
[27] .comment PROGBITS 00000000 003028 00002b 01 MS 0 0 1
[28] .symtab SYMTAB 00000000 003054 000480 10 29 45 4
[29] .strtab STRTAB 00000000 0034d4 000254 00 0 0 1
[30] .shstrtab STRTAB 00000000 003728 00011d 00 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)

.rel.plt

​ .rel.dyn是动态链接的二进制文件中需要重定位的变量的信息,.rel.plt是需要重定位的函数的信息

​ 它的结构如下(以32位为例),两种类型区别见wiki

1
2
3
4
5
6
7
8
9
10
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;

typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;

image-20230708162230315

​ 最重要的就是前两个字段,r_offset,给出了需要重定位的位置,对于可执行文件而言,取值是需要重定位的虚拟地址,一般而言也就是GOT表的地址

image-20230708163140389

​ GOT表中开始存放的值是这个

image-20230708163342737

​ 这玩意也就是plt表,要进行真正解析的地方

image-20230708163428111

​ r_info给出需要重定位的符号的符号表索引,以及相应的重定位类型. 换句话说,第一个参数是告诉你要把哪里的值进行修改,这个参数是告诉你,要修改哪个符号.

  • 高三个字节对应的值表示这个动态符号在.dynsym符号表中的位置
  • 最低字节表示的是重定位类型

.dynsym

​ 结构如下

1
2
3
4
5
6
7
8
9
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility under glibc>=2.2 */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;

​ 这个在ida中也是没有直接给出的,可以用readelf查看然后寻找.

​ 其中比较重要的字段

  • st_name 保存着动态符号在.dynstr表(动态字符串表)中的偏移
  • st_value 如果这个符号被导出,这个符号保存着对应的虚拟地址

image-20230708164800762

1
Elf32_Sym <offset aRead - offset unk_80482E8, 0, 0, 12h, 0, 0> ; "read"

​ 以read为例,aRead是这个符号的虚拟地址, 减去符号表开始的虚拟地址,就得到了偏移, read的这个值为0x27

0x804830F - 0x80482E8 = 0x27,也就是read这个字符串开始的地方,(以及他们每个字符串结束后都有个0,位置结束符号)

​ dynstr表如下

image-20230708165101316

.dynmaic

image-20230708172738811

解析过程

got plt .got.plt 延迟绑定

got表 got.plt表

​ Globle offset table全局偏移量表,位于数据段,是一个每个条目是8字节地址的数组,用来存储外部函数在内存的确切地址,GOT表存储在数据段,(在IDA中是也就是.data段)可以在程序运行中被修改。

​ .got 存放全局变量引用

​ .got.plt 存放需要延迟绑定的函数

​ got表的初始状态指向一段plt,首次调用时会由plt表中指令进行解析,得到真正的函数地址(即内存中的地址)并填入相应的got表项

image-20230228164021947

image-20230303132148041

plt表

image-20230303132136453

https://blog.csdn.net/qq_52126646/article/details/119494939

延迟绑定

​ 以上一篇博客的题目为例

​ call一个函数的时候,先到plt 0x400650 <system@plt>

1
2
pwndbg> p system
$2 = {<text variable, no debug info>} 0x400650 <system@plt>

​ 然后plt里面第一条 jmp cs:off_600D38 这里会跳到 system@got的地址,(此时还没有初始化)

​ display /3i $rip 设置单步执行后自动显示的内容,这里显示后续三条指令

1
2
3
4
5
pwndbg> display /3i $rip
2: x/3i $rip
=> 0x400650 <system@plt>: jmp QWORD PTR [rip+0x2006e2] # 0x600d38
0x400656 <system@plt+6>: push 0x2
0x40065b <system@plt+11>: jmp 0x40062

​ 之后,0x600d38里面的地址是system@plt刚才jmp的下一条,push 0x2,然后再jmp 0x40062,也就是PLT[0],跳转到动态链接器进行地址解析.找到真正的地址,填入got地址,也就是0x600d38,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> x/20wx 0x600d38
0x600d38: 0x00400656 0x00000000 0xf7a46e40 0x00007fff
0x600d48: 0xf7a46f10 0x00007fff 0xf7af2020 0x00007fff


.plt:0000000000400650 ; int system(const char *command)
.plt:0000000000400650 _system proc near ; CODE XREF: main+DE↓p
.plt:0000000000400650 jmp cs:off_600D38
.plt:0000000000400650 _system endp
.plt:0000000000400650
.plt:0000000000400656 ; ---------------------------------------------------------------------------
.plt:0000000000400656 push 2
.plt:000000000040065B jmp sub_400620

​ 下次在执行的时候,直接就plt->got的第一个jmp -> 实际地址,也就是说0x600d38里存储的是system的真实内存地址了

1
2
3
4
5
6
7
8
9
10
pwndbg> x/20wx 0x600d38
0x600d38: 0xf7a31420 0x00007fff 0xf7a46e40 0x00007fff
0x600d48: 0xf7a46f10 0x00007fff 0xf7af2020 0x00007fff


pwndbg> x/20wx 0x00007ffff7a31420
0x7ffff7a31420 <__libc_system>: 0x74ff8548 0xfa66e90b 0x0f66ffff 0x0000441f
0x7ffff7a31430 <__libc_system+16>: 0x593d8d48 0x48001649 0xe808ec83 0xfffffa50
0x7ffff7a31440 <__libc_system+32>: 0x940fc085 0xc48348c0 0xc0b60f08 0x001f0fc3
0x7ffff7a31450 <__GI___realpath>: 0xe5894855 0x56415741 0x54415541 0xec814853