Global Offset Table,全局偏移表。这是链接器在执行链接时实际上要填充的部分, 保存了所有外部符号的地址信息
Procedure Linkage Table,进程链接表
调用动态链接器来解析某个外部函数的地址,并填充到.got.plt
中,然后跳转到该函数
如果已经填充过,直接在.got.plt
中查找并跳转到对应外部函数
.got.plt
是got的一部分,相当于.plt的GOT全局偏移表,其内容有两种情况,如果在之前查找过该符号,内容为外部函数的具体地址;如果没查找过,则内容为跳转回.plt的代码,,并调用动态链接器
在生成可执行程序的时候,只是引用的未定义的符号作了标识,到加载到内存中的时候才进行符号重定位
ld.so
命名规则lib(.*).so
#include <stdio.h>
int main(int argc, char const *argv[])
{
puts("hello world");
return 0;
}
gcc -fPIC -shared hello.c -o libhello.so
链接阶段是将一个或多个中间文件(.o文件)通过链接器将它们链接成一个可执行文件
如果是在中间文件中已经定义的函数,链接阶段可以直接重定位到函数地址
如果是在动态库中定义的函数,链接阶段无法直接重定位到函数地址,只能生成额外的小片段代码(PLT表),然后重定位到该代码片段
运行后加载动态库,把动态库中的相应函数地址填入GOT表
只有动态库函数在被调用时,才会进行地址解析和重定位工作,这时候动态库函数的地址才会被写入到GOT表项中
#include <stdio.h>
int main(int argc, char const *argv[])
{
puts("hello world");
puts("A1_D4i");
return 0;
}
//gcc 1.c -o 1 -no-pie
objdump -h看一波
root@A1_D4i:/mnt/c/Users/wdbbw/Desktop/test# objdump -h 1
1: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 0000001c 0000000000400298 0000000000400298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000060 00000000004002b8 00000000004002b8 000002b8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000003d 0000000000400318 0000000000400318 00000318 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000008 0000000000400356 0000000000400356 00000356 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 0000000000400360 0000000000400360 00000360 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 00000030 0000000000400380 0000000000400380 00000380 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000018 00000000004003b0 00000000004003b0 000003b0 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 00000017 00000000004003c8 00000000004003c8 000003c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000020 00000000004003e0 00000000004003e0 000003e0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 00000192 0000000000400400 0000000000400400 00000400 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 00000009 0000000000400594 0000000000400594 00000594 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 00000017 00000000004005a0 00000000004005a0 000005a0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .eh_frame_hdr 0000003c 00000000004005b8 00000000004005b8 000005b8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 00000100 00000000004005f8 00000000004005f8 000005f8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .init_array 00000008 0000000000600e10 0000000000600e10 00000e10 2**3
CONTENTS, ALLOC, LOAD, DATA
18 .fini_array 00000008 0000000000600e18 0000000000600e18 00000e18 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .dynamic 000001d0 0000000000600e20 0000000000600e20 00000e20 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .got 00000010 0000000000600ff0 0000000000600ff0 00000ff0 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .got.plt 00000020 0000000000601000 0000000000601000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .data 00000010 0000000000601020 0000000000601020 00001020 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .bss 00000008 0000000000601030 0000000000601030 00001030 2**0
ALLOC
24 .comment 0000002b 0000000000000000 0000000000000000 00001030 2**0
CONTENTS, READONLY
>>> hex(elf.plt['puts'])
'0x4003ec'#0x4003f0
>>> hex(elf.got['puts'])
'0x601018'
pwndbg> x/10gi 0x4003ec
0x4003ec: nop DWORD PTR [rax+0x0]
=> 0x4003f0 <puts@plt>: jmp QWORD PTR [rip+0x200c22] # 0x601018
0x4003f6 <puts@plt+6>: push 0x0
0x4003fb <puts@plt+11>: jmp 0x4003e0
0x400400 <_start>: xor ebp,ebp
0x400402 <_start+2>: mov r9,rdx
0x400405 <_start+5>: pop rsi
0x400406 <_start+6>: mov rdx,rsp
0x400409 <_start+9>: and rsp,0xfffffffffffffff0
0x40040d <_start+13>: push rax
在0x4003f0断
跳0x601018
此时
pwndbg> x/10gx 0x601018
0x601018: 0x00000000004003f6 0x0000000000000000
0x601028: 0x0000000000000000 0x0000000000000000
0x601038: 0x0000000000000000 0x0000000000000000
0x601048: 0x0000000000000000 0x0000000000000000
0x601058: 0x0000000000000000 0x0000000000000000
执行jmp qword ptr [rip + 0x200c22] <0x601018>
,跳转到0x4003f6
继续执行,跳转到0x4003e0,然后跳_dl_runtime_resolve_xsavec@got
,执行_dl_runtime_resolve_xsavec
puts地址写入.got.plt,然后跳转
pwndbg> x/10gx 0x601018
0x601018: 0x00007fffff0809c0 0x0000000000000000
0x601028: 0x0000000000000000 0x0000000000000000
0x601038: 0x0000000000000000 0x0000000000000000
0x601048: 0x0000000000000000 0x0000000000000000
0x601058: 0x0000000000000000 0x0000000000000000
之后再调用puts时,直接跳转到puts
relocations read-only
gcc -o test test.c // 默认情况下,是Partial RELRO
gcc -z norelro -o test test.c // 关闭,即No RELRO
gcc -z lazy -o test test.c // 部分开启,即Partial RELRO
gcc -z now -o test test.c // 全部开启,即Full RELRO
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-ND 4.0)进行许可。
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).