GOT和PLT | GOT and PLT for pwning
重定向
二进制文件有两种类型:静态链接和动态链接。静态链接二进制文件包含运行需要的全部代码,不要加载外部库。动态链接没有包含全部代码,需要加载系统库来运行。
假设动态链接二进制文件加载(调用)系统库使用硬编码地址(绝对地址),那么系统库发生变化,二进制文件内的硬编码地址很可能全部改变。所以提出了一个重定向机制。
.got
、.plt
、.got.plt
、链接器
等是重定向的组成成分。
重定位表(relocations)
.got
全局偏移表。用于记录在 ELF (二进制)文件中所用到的共享库中函数(或字符串)的绝对地址。
在程序刚开始运行时,GOT 表为空的,当函数第一次被调用时会动态解析符号的绝对地址然后转去执行,并将被解析符号的绝对地址记录在 GOT 中;第二次调用同一函数时,由于 GOT 中已经记录了其绝对地址,直接转去执行即可(不用重新解析)。(结合 .got.plt 理解)
.got.plt
got 表中的一部分。用于重定向请求到 .got 上的对应偏移或者返回 .plt 中激活链接器寻找函数在 系统库 中的地址。
开始运行是 .got.plt 不为空。当 got 表中没有函数的记录值时,会把从 .plt 进入 .got.plt 的进程重定向到 .plt 中激活链接器,寻址完成后,.got 记录函数在系统库偏移,.got.plt 会记录函数在 .got 偏移。
.plt
程序链接表。是调用系统库函数最开始的入口。它有两个功能,在 .got.plt 节中拿到地址,并跳转;当 .got.plt 没有所需地址的时,触发「链接器」去找到所需函数地址。
.plt.got
没有太准确的相关资料,在 stackoverflow 上面有一个帖子提及,原文如下:
The difference is that .got.plt is runtime-writable, while .got is not if you enable a defense against GOT overwriting attacks called RELRO (relocations read-only). To enable RELRO, you use the ld option -z relro. RELRO places GOT entries that must be runtime-writable for lazy binding in .got.plt, and all others in the read-only .got section
没太看懂,大概说 .got.plt 在运行时是可读写。但是当开启 RELRO 时,.got 是不可写的。
调用系统库函数
实例
源码如下:
1 | // Build with: gcc -m32 -no-pie -g -o plt plt.c |
1 | pwndbg> info files |
gdb 反编译 main 函数,call 函数下断点,单步调试
1 | disass main |
debug 到 call 函数,用 si 单步入进入 plt 函数里面,否则直接调 puts 代码。可以用x/i $pc
查汇编,或者disass 0x8048300
反编译一样能看到跳转的 .got.plt 地址 0x804a00c 。
1 | si |
查询 .got.plt 的跳转地址,是跳转回 .plt 。因为第一次调用 .got 表没有记录,需要跳转 .plt 激活链接器寻址。
1 | x/wx 0x804a00c |
然后线程会进入系统库函数中(libc),并且 .got 记录 libc 地址,.got.plt 记录在 .got 中偏移。
Pwning Relocations
通常就是控制程序执行流程嘛,但是通常某一部分不会同时开启写和执行权限,也就是 NX 保护嘛。
然后.got.plt
是一个函数指针数组(库),就覆盖其中值控制执行流程。
对应的保护措施就是 RELRO :partial and full RELRO。
Partial RELRO (enabled with -Wl,-z,relro
):
- Maps the
.got
section as read-only (but not.got.plt
) - Rearranges sections to reduce the likelihood of global variables overflowing into control structures.
Full RELRO (enabled with -Wl,-z,relro,-z,now
):
- Does the steps of Partial RELRO, plus:
- Causes the linker to resolve all symbols at link time (before starting execution) and then remove write permissions from
.got
. .got.plt
is merged into.got
with full RELRO, so you won’t see this section name.