GOT表和PLT表知識詳解(轉載)

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/qq_18661257/article/details/54694748
GOTGOT表和PLTPLT表在程序中的做用很是巨大,接下來的講解但願你們能夠仔細看看

咱們用一個很是簡單的例子來說解,代碼以下:
圖1



而後咱們編譯

咱們直接gdb./a.outgdb./a.out來進行反編譯處理,而後經過disasmaindisasmain查看mainmain函數中的反編譯代碼以下:

圖3



咱們能夠觀察到gets@pltgets@plt和puts@pltputs@plt這兩個函數,爲何後面加了個@plt@plt,由於這個爲PLTPLT表中的數據的地址。那爲何反編譯中的代碼地址爲PLTPLT表中的地址呢。

緣由

爲了更好的用戶體驗和內存CPUCPU的利用率,程序編譯時會採用兩種表進行輔助,一個爲PLTPLT表,一個爲GOTGOT表,PLTPLT表能夠稱爲內部函數表,GOTGOT表爲全局函數表(也能夠說是動態函數表這是我的自稱),這兩個表是相對應的,什麼叫作相對應呢,PLTPLT表中的數據就是GOTGOT表中的一個地址,能夠理解爲必定是一一對應的,以下圖:

圖44



PLTPLT表中的每一項的數據內容都是對應的GOTGOT表中一項的地址這個是固定不變的,到這裏你們也知道了PLTPLT表中的數據根本不是函數的真實地址,而是GOTGOT表項的地址,好坑啊。

其實在你們進入帶有@plt@plt標誌的函數時,這個函數其實就是個過渡做用,由於GOTGOT表項中的數據纔是函數最終的地址,而PLTPLT表中的數據又是GOTGOT表項的地址,咱們就能夠經過PLTPLT表跳轉到GOTGOT表來獲得函數真正的地址。

那問題來了,這個@plt@plt函數時怎麼來的,這個函數是編譯系統本身加的,你們能夠經過disas gets看看裏面的代碼,以下圖:

圖55



你們能夠發現,這個函數只有三行代碼,第一行跳轉,第二行壓棧,第三行又是跳轉,解釋:
第一行跳轉,它的做用是經過PLTPLT表跳轉到GOTGOT表,而在第一次運行某一個函數以前,這個函數PLTPLT表對應的GOTGOT表中的數據爲@plt@plt函數中第二行指令的地址,針對圖中來講步驟以下:

jmpjmp指令跳轉到GOTGOT表
GOTGOT表中的數據爲0x4004860x400486
跳轉到指令地址爲0x4004860x400486
執行push 0x3#這個爲在GOTGOT中的下標序號
在執行jmp 0x400440
而0x4004400x400440爲PLT[0]PLT[0]的地址
PLT[0]PLT[0]的指令會進入動態連接器的入口
執行一個函數將真正的函數地址覆蓋到GOTGOT表中
這裏咱們要提幾個問題:
1. PLT[0]PLT[0]處到底作了什麼,按照咱們以前的思路它不是應該跳轉到GOT[0]GOT[0]嗎?
2. 爲何中間要進行pushpush壓棧操做?
3. 壓入的序號爲何爲0x30x3,不是最開始應該爲0x00x0嗎?

解決問題

問題1
看下圖:
圖66



咱們嘗試着查看0x4004400x400440地址的數據內容發現一個問題,從0x400440−0x4004500x400440−0x400450之間的數據徹底不知道是什麼,而真正的PLT[x]PLT[x]中的數據是從0x4004500x400450開始的,從這裏纔有了@plt@plt爲後綴的地址,可是咱們disas gets看代碼的時候是從0x4004400x400440開始的,咱們能夠經過x /5i 0x400440查看0x4004400x400440處的代碼,以下:
圖77



咱們看到了後面的#以後又一個1616進制數,一看即可以知道是GOTGOT表的地址,爲何這麼確定呢,由於咱們能夠經過objdump -R ./a.out查看一個程序的GOTGOT函數的地址,以下圖:
圖88



這裏都是些GOTGOT地址,咱們發現都是0x601...0x601...這些,因此能夠判定圖77中的也是GOTGOT地址,那麼咱們能夠猜測出,在正式存儲一個函數的GOTGOT地址前,咱們的PLTPLT表前面有一項進行一些處理,咱們暫且不具體深刻剖析這些代碼有什麼用,可是咱們能夠確定puts@pltputs@plt前面那1616個字節也算是PLTPLT表中的內容,這其實就是咱們的PLT[0]PLT[0],正如咱們以前問題提到的那樣,咱們的PLT[0]PLT[0]根本沒有跳轉到GOT[0]GOT[0],它不像咱們的PLT[1]PLT[1]這些存儲的是GOTGOT表項的地址,它是一些代碼指令,換句話說,PLT[0]PLT[0]是一個函數,這個函數的做用是經過GOT[1]GOT[1]和GOT[2]GOT[2]來正確綁定一個函數的正式地址到GOTGOT表中來。

咦,這裏問題好像又產生了,原本按照最開始的思路PLT[1]PLT[1]也是跳轉到GOT[1]GOT[1]的,GOT[2]GOT[2]同理,可是這兩個數據好像被PLT[0]PLT[0]利用了,同時GOT[0]GOT[0]好像消失了,這裏GOT[0]GOT[0]暫且不說它的做用是什麼,針對GOT[1]GOT[1]和GOT[2]GOT[2]被PLT[0]PLT[0]利用,因此咱們程序中真實狀況實際上是從PLT[1]PLT[1]到GOT[3]GOT[3],PLT[2]PLT[2]到GOT[4]GOT[4],因此咱們推翻了咱們的圖44,創建一張新的處理表

圖99



而plt[0]plt[0]代碼作的事情則是:因爲GOT[2]GOT[2]中存儲的是動態連接器的入口地址,因此經過GOT[1]GOT[1]中的數據做爲參數,跳轉到GOT[2]GOT[2]所對應的函數入口地址,這個動態連接器會將一個函數的真正地址綁定到相應的GOT[x]GOT[x]中。

這就是PLTPLT表和GOTGOT表,總而言之,咱們調用一個函數的時候有兩種方法,一個是經過PLTPLT表調用,一個則是經過GOTGOT表調用,由於PLTPLT表最終也是跳轉GOTGOT表,GOTGOT表中則是一個函數真正的地址,這裏須要注意的是,在一個函數沒有運行一次以前,GOTGOT表中的數據爲@plt@plt函數中下一條指令的地址,圖55有說。

問題2
中間進行的壓棧是爲了肯定PLTPLT對應的GOTGOT表項,便是PLT[1]−>GOT[3]PLT[1]−>GOT[3],0x30x3就是GOTGOT的下標33,也就是說壓棧後咱們跳轉到PLT[0]PLT[0],接着PLT[0]PLT[0]中的指令會經過此次壓棧的序號來肯定操做的GOTGOT表項爲多少

問題3
好像都在第一個問題都已經解決了,這裏壓入0x30x3的緣由是由於,咱們的GOT[0]GOT[0],GOT[1]GOT[1],GOT[2]GOT[2]都有額外用處。要從GOT[3]GOT[3]開始


————————————————
版權聲明:本文爲CSDN博主「77458」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/qq_18661257/article/details/54694748函數

相關文章
相關標籤/搜索