深入跟踪plt表和got表

深入跟踪plt表和got表

0x01 概述

在漏洞利用的时候,需要搞清楚plt表和got表,这里通过实验一步步跟踪来帮助理解。

下面贴一张图来帮助理解:

函数第一次被调用过程

plt_got

第一步由函数调用跳入到PLT表中,然后第二步PLT表跳到GOT表中,可以看到第三步由GOT表回跳到PLT表中,这时候进行压栈,把代表函数的ID压栈,接着第四步跳转到公共的PLT表项中,第5步进入到GOT表中,然后_dl_runtime_resolve对动态函数进行地址解析和重定位,第七步把动态函数真实的地址写入到GOT表项中,然后执行函数并返回。

解释下dynamic段,link_map和_dl_runtime_resolve

dynamic段:提供动态链接的信息,例如动态链接中各个表的位置

link_map:已加载库的链表,由动态库函数的地址构成的链表

_dl_runtime_resolve:在第一次运行时进行地址解析和重定位工作

函数之后被调用过程

plt_got2

可以看到,第一步还是由函数调用跳入到PLT表,但是第二步跳入到GOT表中时,由于这个时候该表项已经是动态函数的真实地址了,所以可以直接执行然后返回。

对于动态函数的调用,第一次要经过地址解析和回写到GOT表项中,第二次直接调用即可。(以上资料来源于网络)

0x02 实验环境

ubuntu 16.04

0x03 实验

这里找了一个程序,直接gdb进行调试,先看下main函数反汇编:

main

可以看到有vul函数,再查看下vul函数:

vul

可以看到有个gets函数,它后面有个@plt,我们就在它这里下断后运行:

gets

然后单步步入这个call:

gets@plt

可以看到它跳转到了plt表位置,继续单步:

push0

可以看到它跳转到了push 0 的位置也就是plt+6的位置,继续单步:

plt+11

可以看到它跳转到了plt+11 的位置,继续一直单步:

got

看到跳转到了got表里,然后继续单步:

got_gets

可以看到程序跳转到了真正的gets函数位置(0xf7e43ae0),这里直接finish这个gets函数:

finish

现在我们看下执行完一次之后的这个gets@plt:

gets@plt2

看下它要jmp的那个地址的内容:

x/wx

可以发现其内容为0xf7e43ae0,即真正的gets函数的地址,说明在第一次执行完之后它会把gets函数地址直接放在要跳转的第一个jmp中了。当第二次执行的时候就会直接跳到gets函数,也就是延迟绑定机制。