欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一篇文章,搞懂 GOT表 & PLT表

程序员文章站 2024-01-20 13:05:40
...

一篇文章,搞懂 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 函数的汇编代码
    一篇文章,搞懂 GOT表 & PLT表

  • 在两次调用 printf 处下断点
    一篇文章,搞懂 GOT表 & PLT表

  • 运行,此时会停在第一个断点处
    一篇文章,搞懂 GOT表 & PLT表

  • 使用 si (step into) 命令查看跳转到 aaa@qq.com (这其实就是位于 plt 表中)的过程。可见,此时 printf 处共有三行汇编语句。第一行是跳转到了 $(rip+0x2fe2) 处,其值为 0x404018
    一篇文章,搞懂 GOT表 & PLT表

    至于说为什么此时的 RIP 寄存器为 0x401030,但 rip+0x2fe2 却等于 0x404018,那是因为 jmp 语句底层实现是先将下一条语句的地址写入 rip 中,然后才去判断标志位,判断是否需要用 jmp 后的立即数覆写 rip 的值

  • 查看此时 0x404018 处的值,发现其就等于 jmp 语句后面一条语句的地址。同时,编译器提示我们该函数是 aaa@qq.com(这就是 got 表中的一个字段)
    一篇文章,搞懂 GOT表 & PLT表

  • 也就是说,在第一次调用 printf 时,相当于 jmp 语句没有起作用!——跳出去,又跳回来了!

  • 使用 c (continue) 命令接着运行到下一次调用 printf。同样,还是使用 si 命令步入到 aaa@qq.com
    一篇文章,搞懂 GOT表 & PLT表

  • 查看此时 0x404018 处的值,发现它指向了一个显然不在此进程的内存空间中的值,这,就是 printf 在内存中的真实地址。
    一篇文章,搞懂 GOT表 & PLT表

调用库函数时发生了什么

先概况一下上面调试中的发生的情况:第一次调用 printf 时,“顺序”执行了 aaa@qq.com 中的三条语句(实际上是跳出去又跳回来);第二次调用 printf 时,直接跳转到了 0x00007ffff7e46db0 处,也就是执行了 printf 函数

  1. 调用库函数时,先查 plt 表(如 aaa@qq.com),plt 表中第一条语句索引了 got 表中的值。
  2. 若从未调用过该库函数,则此时 got 表中的字段是指回 plt 表中的第二条汇编指令的(如 aaa@qq.com 中最开始的值为 0x0x401036),此时顺序执行,而后续指令就是在装载相应的库,然后对 got 表进行覆写
  3. 后续的调用中,got 表均保存着库函数的真实地址,因此无需再去装载一遍动态链接库等,直接跳转到对应的库函数的真实地址处,执行即可。

最后

  • 欢迎关注我的微信公众号Code Segment,及时获取各类通俗易懂的技术文章~
相关标签: Linux CTF