一篇文章,搞懂 GOT表 & PLT表
一篇文章,搞懂 GOT表 & PLT表
一个栗子
- 以一段很简单的代码为例
#include<stdio.h> int main(void){ printf("1st"); printf("2nd"); return 0; }
- 在 Linux 下,使用 gcc 编译,编译时关闭 ASLR(Address Space Layout Randomization,Linux 中叫做 pie)
gcc ./test.c -o test -no-pie #-no-pie 就是关闭 ASLR 的指令
使用 GDB 进行调试
-
首先,查看一下
main
函数的汇编代码 -
在两次调用
printf
处下断点 -
运行,此时会停在第一个断点处
-
使用
si (step into)
命令查看跳转到aaa@qq.com
(这其实就是位于plt
表中)的过程。可见,此时printf
处共有三行汇编语句。第一行是跳转到了$(rip+0x2fe2)
处,其值为0x404018
至于说为什么此时的
RIP
寄存器为0x401030
,但rip+0x2fe2
却等于0x404018
,那是因为jmp
语句底层实现是先将下一条语句的地址写入rip
中,然后才去判断标志位,判断是否需要用jmp
后的立即数覆写rip
的值 -
查看此时
0x404018
处的值,发现其就等于jmp
语句后面一条语句的地址。同时,编译器提示我们该函数是aaa@qq.com
(这就是got
表中的一个字段) -
也就是说,在第一次调用
printf
时,相当于jmp
语句没有起作用!——跳出去,又跳回来了! -
使用
c (continue)
命令接着运行到下一次调用printf
。同样,还是使用si
命令步入到aaa@qq.com
中 -
查看此时
0x404018
处的值,发现它指向了一个显然不在此进程的内存空间中的值,这,就是printf
在内存中的真实地址。
调用库函数时发生了什么
先概况一下上面调试中的发生的情况:第一次调用
printf
时,“顺序”执行了aaa@qq.com
中的三条语句(实际上是跳出去又跳回来);第二次调用printf
时,直接跳转到了0x00007ffff7e46db0
处,也就是执行了printf
函数
- 调用库函数时,先查
plt
表(如aaa@qq.com
),plt
表中第一条语句索引了got
表中的值。 - 若从未调用过该库函数,则此时
got
表中的字段是指回plt
表中的第二条汇编指令的(如aaa@qq.com
中最开始的值为0x0x401036
),此时顺序执行,而后续指令就是在装载相应的库,然后对got
表进行覆写 - 后续的调用中,
got
表均保存着库函数的真实地址,因此无需再去装载一遍动态链接库等,直接跳转到对应的库函数的真实地址处,执行即可。
最后
- 欢迎关注我的微信公众号Code Segment,及时获取各类通俗易懂的技术文章~
上一篇: Unity创建和读取excel表格
下一篇: ps怎么设计一个金属质感的徽章?