1. 程式人生 > >使用GDB除錯組合語言,檢視plt過程

使用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,這樣就證實了我們的推斷。