CTF Pwn 初探:PLT 表和 GOT 表
在 pwn中,理解 ELF 文件的 PLT
(Procedure Linkage Table,过程链接表)和 GOT
(Global Offset Table,全局偏移表)是非常重要的。这些表格用于解决函数调用时的延迟绑定(Lazy Binding),并且与动态链接库加载和 ASLR(Address Space Layout Randomization)防御机制有密切的关系。
ELF 文件的延迟绑定机制
ELF 文件中,函数的调用通过 PLT
表和 GOT
表进行管理。延迟绑定意味着程序在第一次调用一个函数时,不直接解析其地址,而是通过一套机制将解析过程推迟到运行时。具体来说,ELF 文件利用 PLT
表与 GOT
表实现了这一机制。
- PLT 表:每次调用动态链接库中的函数时,程序并不会直接跳转到目标函数的地址,而是先跳转到
PLT
表中相应的入口。PLT
表中的代码会跳转到GOT
表对应项,若这是该函数第一次调用,则会通过动态链接器解析该函数的实际地址。 - GOT 表:
GOT
表中的每一项用于存储函数的实际地址。在函数第一次调用之前,该项保存的是指向PLT
表的某个偏移量;在函数解析之后,GOT
表中该函数的地址会被替换为实际的目标函数地址,以便后续直接调用。
延迟绑定流程
当一个函数第一次被调用时:
- PLT 表跳转:程序首先跳转到
PLT
表中的函数入口,这个入口中的指令会进一步跳转到GOT
表中相应的项。 - GOT 表未解析状态:如果这是第一次调用该函数,
GOT
表中的内容仍然是PLT
表中的某个偏移值(即func@plt+6
),程序会再次跳转到PLT
中预定义的处理函数地址。 - 动态解析:通过动态链接器(
_dl_runtime_resolve
),程序会解析出该函数的实际地址,并将其存储在GOT
表中,后续的函数调用将直接使用该地址。
实例解析
以下是一个包含 PLT
表和 GOT
表的例子。我们可以通过调试工具 pwndbg
来查看程序的 PLT
和 GOT
表的内容。
1 | pwndbg> plt |
通过 x/4i
查看 printf@plt
的汇编指令:
1 | pwndbg> x/4i 0x690 |
当程序调用 printf@plt
时,首先会跳转到 GOT
表中的 printf@got.plt
项。我们可以进一步查看 GOT
表的内容:
1 | pwndbg> x/a 0x200fc0 |
这里可以看到,got.plt
中 printf
的初始值是 printf@plt+6
,意味着函数的第一次调用会回到 PLT
表。此时,PLT
表会通过 push
和 jmp
指令处理动态解析过程。
1 | pwndbg> x/10i 0x690 |
进一步分析
在第一次调用函数时,PLT
会执行一个类似如下的流程:
1 | func@plt: |
_dl_runtime_resolve(link_map_obj, reloc_index)
负责解析函数的真实地址,并将其写入 GOT
表。解析完成后,程序跳转到函数的实际地址。该过程仅在第一次调用时执行,后续调用将直接使用 GOT
表中的已解析地址。
结语
理解 PLT
和 GOT
表的工作机制是理解现代二进制漏洞利用的重要一步。通过掌握延迟绑定流程、动态解析机制以及如何绕过保护机制(如 PIE、NX 和 ASLR),我们可以深入探索漏洞利用的更多技巧。
由于我还是pwn的初学者,因此以上内容可能存在错误,如有发现,欢迎指正,将不胜感激。