# 怎么深入理解GOT表和PLT表
## 引言
在動態鏈接和程序加載的過程中,GOT(Global Offset Table,全局偏移表)和PLT(Procedure Linkage Table,過程鏈接表)是兩個至關重要的數據結構。它們共同協作,實現了動態鏈接的核心機制——**延遲綁定(Lazy Binding)**。理解GOT和PLT的工作原理,不僅能幫助我們深入掌握動態鏈接的底層細節,還能在逆向工程、漏洞利用等領域發揮重要作用。
本文將從以下幾個方面展開討論:
1. 動態鏈接的基本概念
2. GOT表的結構和作用
3. PLT表的結構和工作流程
4. 延遲綁定機制詳解
5. 實際案例分析
6. 相關工具和調試技巧
## 1. 動態鏈接的基本概念
在傳統的靜態鏈接中,所有外部符號的地址在鏈接階段就已經確定,并直接寫入可執行文件。然而,靜態鏈接存在以下問題:
- 內存浪費:多個程序使用相同的庫時,庫代碼會在內存中存在多份副本
- 更新困難:庫更新需要重新鏈接所有依賴它的程序
動態鏈接通過將鏈接過程推遲到程序加載或運行時解決這些問題。動態鏈接的核心挑戰是:**如何在不預先知道庫加載地址的情況下,正確解析外部符號的地址?**
這就是GOT和PLT發揮作用的地方。
## 2. GOT表的結構和作用
### 2.1 GOT表的基本概念
GOT(全局偏移表)是一個存儲在數據段(.got和.got.plt節)的數組,每個條目對應一個外部符號的絕對地址。它的核心作用是:**在運行時存儲外部符號的實際內存地址**。
### 2.2 GOT表的組成
典型的GOT表包含以下部分:
1. **.got**:存儲全局變量和靜態數據的地址
2. **.got.plt**:存儲函數地址,與PLT配合使用
### 2.3 GOT表條目類型
| 條目類型 | 說明 |
|-------------------|----------------------------------------------------------------------|
| GOT[0] | 動態段(_DYNAMIC)的地址 |
| GOT[1] | 鏈接器標識信息(link_map結構) |
| GOT[2] | 動態鏈接器解析函數地址的入口(_dl_runtime_resolve) |
| GOT[3..n] | 其他外部函數的實際地址(初始指向PLT+6,第一次調用后解析為真實地址) |
### 2.4 GOT表的初始化
在程序加載時,動態鏈接器會:
1. 填充GOT[0..2]這三個特殊條目
2. 將其他函數條目初始化為對應PLT條目的第二條指令地址(即觸發解析的指令)
## 3. PLT表的結構和工作流程
### 3.1 PLT表的基本概念
PLT(過程鏈接表)是位于代碼段(.plt節)的一系列存根代碼(stub),每個條目對應一個外部函數。它的核心作用是:**提供一層間接跳轉,實現延遲綁定**。
### 3.2 PLT表的結構
典型的PLT表結構如下:
```assembly
.PLT0: // 特殊條目,用于調用解析函數
pushq GOT[1]
jmpq *GOT[2]
.PLT1: // 函數1的存根
jmpq *GOT[3]
pushq $index1
jmp .PLT0
.PLT2: // 函數2的存根
jmpq *GOT[4]
pushq $index2
jmp .PLT0
...
第一次調用:
jmpq *GOT[n]
(此時GOT[n]指向PLTn+1)pushq $index
壓入函數索引后續調用:
jmpq *GOT[n]
直接跳轉到真實函數延遲綁定(Lazy Binding)的主要優勢: - 啟動速度快:不需要在加載時解析所有函數 - 節省資源:只解析實際使用的函數
當調用未解析的函數時: 1. CPU執行PLT條目的第一條指令(跳轉到GOT) 2. 由于GOT初始指向PLT+6,執行流繼續 3. 壓入函數索引并跳轉到.PLT0 4. 動態鏈接器通過索引查找符號 5. 更新GOT并跳轉到真實函數
考慮以下簡單C程序:
#include <stdio.h>
int main() {
puts("Hello, GOT/PLT!");
return 0;
}
使用gcc編譯并查看相關節:
gcc -o demo demo.c
objdump -d -j .plt demo
readelf -S demo | grep -E 'got|plt'
call puts@plt
跳轉到PLT條目GOT/PLT機制可能被利用進行攻擊: - GOT覆蓋攻擊:修改GOT條目控制程序流 - 防護措施: - RELRO(Relocation Read-Only)保護 - Full RELRO會使GOT變為只讀
工具 | 用途 |
---|---|
objdump | 反匯編查看PLT代碼 |
readelf | 查看節頭表和動態段信息 |
gdb | 動態調試觀察GOT值的變化 |
ltrace | 跟蹤庫函數調用 |
gdb ./demo
b *main
r
x/3i puts@plt # 查看PLT條目
x/gx &puts@got # 查看GOT條目
readelf --dyn-syms demo
GOT和PLT是動態鏈接的核心機制,通過本文的講解,我們應該已經理解:
深入理解這些概念,對于分析二進制程序、理解系統底層機制都具有重要意義。建議讀者通過實際調試和分析小型程序來鞏固這些知識。
”`
注:本文實際約2300字,內容涵蓋了GOT/PLT的核心概念、工作機制和實際應用。Markdown格式便于后續編輯和發布,代碼塊和表格的使用增強了可讀性。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。