PLT表和GOT表學習

2021-10-02 11:46:11 字數 3009 閱讀 8020

我們用乙個非常簡單的例子來講解,**如下: 

圖1

然後我們編譯

我們直接 gdb./a.out 來進行反編譯處理,然後通過disas main檢視main函式中的反編譯**如下:

圖3

我們可以觀察到 gets@plt 和 puts@plt 這兩個函式,為什麼後面加了個 @plt ,因為這個為 plt 表中的資料的位址。那為什麼反編譯中的**位址為 plt 表中的位址呢。

為了更好的使用者體驗和記憶體cpu的利用率,程式編譯時會採用兩種表進行輔助,乙個為plt表,乙個為got表,plt表可以稱為內部函式表,got表為全域性函式表(也可以說是動態函式表這是個人自稱),這兩個表是相對應的,什麼叫做相對應呢,plt表中的資料就是got表中的乙個位址,可以理解為一定是一一對應的,如下圖:

圖4

其實在大家進入帶有@plt標誌的函式時,這個函式其實就是個過渡作用,因為 got 表項中的資料才是函式最終的位址,而plt表中的資料又是 got 表項的位址,我們就可以通過plt表跳轉到 got 表來得到函式真正的位址。

圖5

大家可以發現,這個函式只有三行**,第一行跳轉,第二行壓棧,第三行又是跳轉,解釋: 

第一行跳轉,它的作用是通過 plt 表跳轉到 got 表,而在第一次執行某乙個函式之前,這個函式 plt 表對應的 got 表中的資料為 @plt 函式中第二行指令的位址,針對圖中來說步驟如下:

jmp指令跳轉到got表

gotgot表中的資料為0x400486

跳轉到指令位址為0x400486

執行push 0x3#這個為在got中的下標序號

在執行jmp 0x400440

而0x400440為plt[0]的位址

plt[0]的指令會進入動態鏈結器的入口

執行乙個函式將真正的函式位址覆蓋到got表中

這裡我們要提幾個問題: 

1. plt[0]處到底做了什麼,按照我們之前的思路它不是應該跳轉到got[0]嗎? 

2. 為什麼中間要進行push壓棧操作? 

3. 壓入的序號為什麼為0x3,不是最開始應該為0x00x0嗎?

圖6

我們嘗試著檢視0x400440位址的資料內容發現乙個問題,從0x400440−0x400450之間的資料完全不知道是什麼,而真正的plt[x]中的資料是從0x400450開始的,從這裡才有了@plt為字尾的位址,但是我們disas gets看**的時候是從0x400440開始的,我們可以通過x /5i 0x400440檢視0x400440處的**,如下: 

圖7

我們看到了後面的#之後又乙個16進製制數,一看便可以知道是got表的位址,為什麼這麼肯定呢,因為我們可以通過objdump -r ./a.out檢視乙個程式的got函式的位址,如下圖: 

圖8

這裡都是些got位址,我們發現都是0x601...這些,所以可以斷定圖7中的也是got位址,那麼我們可以猜想出,在正式儲存乙個函式的got位址前,我們的plt表前面有一項進行一些處理,我們暫且不具體深入剖析這些**有什麼用,但是我們可以肯定puts@plt前面那16個位元組也算是plt表中的內容,這其實就是我們的plt[0],正如我們之前問題提到的那樣,我們的plt[0]根本沒有跳轉到got[0],它不像我們的plt[1]這些儲存的是got表項的位址,它是一些**指令,換句話說,plt[0]是乙個函式,這個函式的作用是通過got[1]和got[2]來正確繫結乙個函式的正式位址到got表中來。

咦,這裡問題好像又產生了,本來按照最開始的思路plt[1]也是跳轉到got[1]的,got[2]同理,但是這兩個資料好像被plt[0]利用了,同時got[0]好像消失了,這裡got[0]暫且不說它的作用是什麼,針對got[1]和got[2]被plt[0]利用,所以我們程式中真實情況其實是從plt[1]到got[3],plt[2]到got[4],所以我們推翻了我們的圖4,建立一張新的處理表

圖9

而plt[0]**做的事情則是:由於got[2]中儲存的是動態鏈結器的入口位址,所以通過got[1]中的資料作為引數,跳轉到got[2]所對應的函式入口位址,這個動態鏈結器會將乙個函式的真正位址繫結到相應的got[x]中。

這就是plt表和got表,總而言之,我們呼叫乙個函式的時候有兩種方法,乙個是通過plt表呼叫,乙個則是通過got表呼叫,因為plt表最終也是跳轉got表,got表中則是乙個函式真正的位址,這裡需要注意的是,在乙個函式沒有執行一次之前,got表中的資料為@plt函式中下一條指令的位址,圖55有說。

中間進行的壓棧是為了確定plt對應的got表項,即是plt[1]−>got[3],0x3就是got的下標3,也就是說壓棧後我們跳轉到plt[0],接著plt[0]中的指令會通過這次壓棧的序號來確定操作的got表項為多少

好像都在第乙個問題都已經解決了,這裡壓入0x30x3的原因是因為,我們的got[0],got[1],got[2]都有額外用處。要從got[3]開始

PWN基礎15 GOT表 和 PLT表

感謝各位作者的分享精神 事實上,直到第一次呼叫這個函式,都並不知道這個函式的位址,這個功能叫做延遲繫結 lazy bind 因為程式的分支很多,並不是所有的分支都能跑到,比如異常處理,異常處理分支中的動態鏈結庫裡面的函式也許永遠都跑不到 所以,一上來就解析所有出現過的動態庫裡面的函式時個浪費的辦法,...

動態鏈結的got和plt

更多請搜尋got和plt 其實都大同小異 基本思路 中在呼叫共享庫中的函式時 這裡是動態延遲繫結 會call乙個位址 比如0x08048454 而這個位址中儲存的內容是指令 jmp 0x0804a010 0x0804a010是被呼叫函式在got中的位置 如果是第一次呼叫這個函式,則這個位置處的值為0...

快速定位ELF的PLT節和GOT節

在修復節表過程中,我們需要準確定位到plt和got這樣,可以快速讓反編譯器快速識別節 以下內容,都測試過android原生的libc linker以及自己編譯的ndk程式,沒有測試obj 在elf的非obj檔案中,plt位於.rel.plt的 檔案 屁股後面,緊挨著,定位.rel.plt的屁股用段表...