使用GDB调试汇编语言,查看plt过程

在linux x86平台下,程序链接的plt got里面的值是如何变化的,下面来进行演示:
首先写好一个c文件:

#include <stdio.h>

int main(void)
{
        printf("hello world\n");
        return 0;
}

非常简单的一个hello world程序,编译程序之后对程序进行反汇编objdump -d main
本文使用gdb的一些操作
ni 跳过当前汇编指令
si 进入汇编指令
x 地址 查看内存地址的值
disassembe 查看汇编代码
好了,在进行调试之前,先看一下文件中plt段需要重定向的符号:readelf -r main

重定位节 '.rel.dyn' 位于偏移量 0x290 含有 1 个条目:
 偏移量     信息    类型              符号值      符号名称
08049ffc  00000206 R_386_GLOB_DAT    00000000   __gmon_start__

重定位节 '.rel.plt' 位于偏移量 0x298 含有 2 个条目:
 偏移量     信息    类型              符号值      符号名称
0804a00c  00000107 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
0804a010  00000307 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0

这里只需要关注plt段的重定向,在这里只有两个符号需要重定向,重点是printf的重定向过程。
printf的地址是放在got.plt这个段中的,这个段有三个预先按顺序定义好的值:
1、dynamic段的地址
2、module ID
3、_dl_runtime_resolve()函数地址
那么第四肯定就是printf的地址了
来进行一下调试,首先跳到main函数,有这么一段:

0x08048419 <+14>:    sub    $0x4,%esp
   0x0804841c <+17>:    sub    $0xc,%esp
   0x0804841f <+20>:    push   $0x80484c0
=> 0x08048424 <+25>:    call   0x80482e0 <puts@plt>
   0x08048429 <+30>:    add    $0x10,%esp
   0x0804842c <+33>:    mov    $0x0,%eax

很明显在箭头指向的位置,就是跳到plt中,跳进去之后:

=> 0x080482e0 <+0>:     jmp    *0x804a00c
   0x080482e6 <+6>:     push   $0x0
   0x080482eb <+11>:    jmp    0x80482d0

这时准备跳到printf函数的入口,但是这时肯定不是正确的地址,因为动态链接器使用了延迟绑定的技术,只有在第一次用的时候才会进行真正的重定向,这时候查看一下0x804a00c这个内存的值,发现为0x080482e6 ,也就是下一条汇编指令的位置,继续跟踪跳转到0x80482d0

End of assembler dump.
(gdb) si
0x080482d0 in ?? ()
(gdb) si
0x080482d6 in ?? ()
(gdb) si
_dl_runtime_resolve () at ../sysdeps/i386/dl-trampoline.S:35
35      ../sysdeps/i386/dl-trampoline.S: 没有那个文件或目录.

这里不知道为什么会有“?”,暂且忽略,经过了几次跳转之后来到了_dl_runtime_resolve 函数,这个函数再进行动态绑定,这个函数里面的汇编代码:

=> 0xb7ff0000 <+0>:     push   %eax
   0xb7ff0001 <+1>:     push   %ecx
   0xb7ff0002 <+2>:     push   %edx
   0xb7ff0003 <+3>:     mov    0x10(%esp),%edx
   0xb7ff0007 <+7>:     mov    0xc(%esp),%eax
   0xb7ff000b <+11>:    call   0xb7fe97e0 <_dl_fixup>
   0xb7ff0010 <+16>:    pop    %edx
   0xb7ff0011 <+17>:    mov    (%esp),%ecx
   0xb7ff0014 <+20>:    mov    %eax,(%esp)
   0xb7ff0017 <+23>:    mov    0x4(%esp),%eax
   0xb7ff001b <+27>:    ret    $0xc

里面有一条跳转指令,明显fix_up暗示,执行完这条指令之后,printf完成了重定向,执行完之后我们看一下0x804a00c这个内存地址的值,发生了改变:

(gdb) x 0x804a00c
0x804a00c:      0xb7e65ca0

这才是printf函数真正的入口。
这样重定向的过程算是完成了。

既然,printf的地址在.got.plt中是第四个,那么我们来推断一下.got.plt的地址:0x804a00c-3*4=0x804a000,我们知道.got.plt第一项放的是.dynamic段的地址,调试运行查看一下:

(gdb) x 0x804a000
0x804a000:      0x08049f14

同时使用readelf查看.dynamic在main中的位置:

[21] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [22] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  [23] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4

也是8049f14,这样就证实了我们的推断。

猜你喜欢

转载自blog.csdn.net/whoami_I/article/details/82670846
今日推荐