学习PLT表GOT表

对PLT表和GOT表的浅显理解

之前做PWN题的时候了解过一点plt表和got表——plt表可跳转到got表,got表里存放着函数的真实地址

今天巧合在网上翻到一篇用gdb调试plt表和got表关系的博客,这才对它有了进一步的了解。

GOT(全局偏移表)和 PLT(过程链接表),是两个表之间的交互才使得函数实现延迟绑定,通过这种方法将过程地址的绑定推迟到第一次调用该函数。

GOT表构成

为了实现延迟绑定,GOT的头三条表目是特殊的:

GOT[0]包含.dynamic段的地址,.dynamic段包含了动态链接器用来绑定过程地址的信息,比如符号的位置和重定位信息;

GOT[1]包含动态链接器的标识;

GOT[2]包含动态链接器的延迟绑定代码的入口点。

GOT的其他表目为本模块要引用的一个全局变量或函数的地址。

PLT表构成

PLT是一个以16字节(32位平台中)表目的数组形式出现的代码序列。就像GOT表,PLT表并不是每个表项都用于存放“函数地址“,其中PLT[0]是一个特殊的表目,它跳转到动态链接器中执行,换句话说,PLT[0]是一个函数,这个函数的作用是通过GOT[1]和GOT[2]来正确绑定一个函数的正式地址到GOT表中来。

实现过程

每个定义在共享库中并被本模块调用的函数在PLT中都有一个表目,从PLT[1]开始.模块对函数的调用会转到相应PLT表目中执行,这些表目由三条指令构成。

第一条指令是跳转到相应的GOT存储的地址值中.

第二条指令把函数相应的ID压入栈中,

第三条指令跳转到PLT[0]中调用动态链接器解析函数地址,并把函数真正地址存入相应的GOT表目中。

被调用函数GOT相应表目中存储的最初地址为相应PLT表目中第二条指令的地址值,函数第一次被调用后.GOT表目中的值就为函数的真正地址。

因此,第一次调用函数时开销比较大.但是其后的每次调用都只会花费一条指令和一个间接的存储器引用。


所以第一次函数调用过程如下:

1.调用函数找到plt表

2.jmp 相应的got表

3.push got表的下标//相应的标识

4.jmp plt[0]

5.plt[0]的指令转向got[2],进入动态连接器入口

//由于GOT[2]中存储的是动态链接器的入口地址,所以通过GOT[1]中的数据作为参数,跳转到GOT[2]所对应的函数入口地址,这个动态链接器会将一个函数的真正地址绑定到相应的GOT[x]中

6.将真正的函数地址覆盖到got表中

其中2、3、4步(jmp、push、jmp)是第一次调用某函数时进行的三步操作,是实现延时绑定机制的关键

可以理解为这种状态:

img


最后借用一下大佬的动图:

在这里插入图片描述