1. 程式人生 > >聊聊Linux動態連結中的PLT和GOT(4)—— 穿針引線

聊聊Linux動態連結中的PLT和GOT(4)—— 穿針引線

編譯時的PLT和GOT關係圖

前幾篇文章一直在討論PLT和GOT的結構細節,編譯完成之後,PLT和GOT的對應關係是怎麼樣的呢,下面是編譯完成之後,PLT和GOT關係圖。

編譯時PLT和GOT關係圖

圖中重點標註了從呼叫printf函式語句的彙編指令call [email protected]跳轉過程,圖中使用編號來表標跳轉順序。

PLT表結構有以下特點:

  1. PLT表中的第一項為公共表項,剩下的是每個動態庫函式為一項(當然每項是由多條指令組成的,jmp *0xXXXXXXXX這條指令是所有plt的開始指令)
  2. 每項PLT都從對應的GOT表項中讀取目標函式地址

GOT表結構有以下特點:

  1. GOT表中前3個為特殊項,分別用於儲存 .dynamic段地址、本映象的link_map資料結構地址和_dl_runtime_resolve函式地址;但在編譯時,無法獲取知道link_map地址和_dl_runtime_resolve函式地址,所以編譯時填零地址,程序啟動時由動態連結器進行填充
  2. 3個特殊項後面依次是每個動態庫函式的GOT表項

如果將PLT和GOT抽象起來描述,可以寫成以下的虛擬碼:

plt[0]:
  pushl got[1]
  jmp  *got[2]

plt[n]:                // n >= 1
  jmp *got[n+2
] // GOT前3項為公共項,第3項開始才是函式項,plt[1]對應的GOT[3],依次類推 push (n-1)*8 jmp plt[0] got[0] = address of .dynamic section got[1] = address of link_map object( 編譯時填充0) got[2] = address of _dl_runtime_resolve function (編譯時填充為0) got[n+2] = plt[n] + 6 (即plt[n]程式碼片段的第二條指令)

程序起動後的GOT表

PLT屬於程式碼段,在程序載入和執行過程都不會發生改變,PLT指向GOT表的關係在編譯時已完全確定,唯一能發生變化的是GOT表。

Linux載入程序時,通過execve系統呼叫進入核心態,將映象載入到記憶體,然後返回使用者態執行。返回使用者態時,它的控制權並不是交給可執行檔案,而是給動態連結器去完成一些基礎的功能,比如上述的GOT[1],GOT[2]的填寫就是這個階段完成的。下圖是動態連結器填完GOT[1],GOT[2]後的GOT圖:

程序載入完成後的GOT表

估計大家比較好奇的是,動態連結器怎麼知道GOT的首地址?這個祕密就藏在ELF的.dynamic段裡面,詳見下面readelf -d test輸出結果中的PLTGOT項:

[email protected]:~/test/test$ readelf -d test

Dynamic section at offset 0x600 contains 24 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048274
 0x0000000d (FINI)                       0x8048488
 0x00000019 (INIT_ARRAY)                 0x80495f4
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x80495f8
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x00000004 (HASH)                       0x8048168
 0x00000005 (STRTAB)                     0x80481e0
 0x00000006 (SYMTAB)                     0x8048190
 0x0000000a (STRSZ)                      74 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80496ec
 0x00000002 (PLTRELSZ)                   24 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x804825c
 0x00000011 (REL)                        0x8048254
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x8048234
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x804822a
 0x00000000 (NULL)                       0x0

其實.dynamic段還藏著很多其它資訊,都是跟動態執行相關的資訊,有興趣的讀者可以自行分析,這裡不詳細介紹。

動態重定位執行過程

Linux 動態連結器提供動態重位功能,所有外部函式只有呼叫時才做重定位,實現延遲繫結功能。下面是以呼叫puts函式為例畫出了整個動態重定位的執行過程:

以puts函式為例,畫出整個動態重定位的執行過程

在 _dl_runtime_resolve函式內完成puts符號查詢後,將該函式地址地址重定位到對應的GOT表項,並呼叫。

重定位之後的呼叫

GOT表項已完成重定位的情況下,PLT利用GOT表直接呼叫到真實的動態庫函式,下面puts函式的呼叫過程:

重定位後的呼叫過程

總結

對於PLT和GOT的原理,一共分享了以下知識點:
1. 為什麼會有PLT和GOT表,它完成什麼功能
2. Linux如何通過 PLT和GOT表配合,完成延遲重定位功能
3. PLT和GOT的結構是怎麼樣的,並且介紹每種場景下PLT的執行過程

關於PLT/GOT的基本知識寫到這樣就有清晰的認識了,但是Linux還有其它場景也會使用PLT/GOT,以後遇到時再展開討論。

最後,本系列文章所有二進位制分析,都是基於以下程式碼編譯出來的可執行檔案(32位)進行分析。

#include <stdio.h>

void print_banner()
{
    printf("Welcome to World of PLT and GOT\n");
}

int main(void)
{
    print_banner();

    return 0;
}

相關推薦

聊聊Linux動態連結PLTGOT4—— 穿針引線

編譯時的PLT和GOT關係圖 前幾篇文章一直在討論PLT和GOT的結構細節,編譯完成之後,PLT和GOT的對應關係是怎麼樣的呢,下面是編譯完成之後,PLT和GOT關係圖。 圖中重點標註了從呼叫printf函式語句的彙編指令call [email 

動態連結PLTGOT

最近在研究緩衝區溢位攻擊的試驗,發現其中有一種方法叫做ret2plt。plt?這個詞好熟悉,在彙編程式碼裡經常見到,和plt經常一起出現的還有一個叫got的東西,但是對這兩個概念一直很模糊,趁著這個機會研究一下。 可以先說一下結論 : plt和got是**動態連結**中用來重定位的。 ### GOT 我們

pythonxrangerange

log item .py 對象 nbsp net range all file 說到序列,我們第一想到的是一組有序元素組成的集合。同時,每個元素都有唯一的下標作為索引。 在Python中,有許多內界的序列。包括元組tuple,列表list,字符串str等。上面提到的序列

Linux下MySQL的安裝啟動轉載

enable linu char cal mysql用戶 客戶端程序 ast 初學 unix 原文鏈接:http://www.linuxidc.com/Linux/2016-07/133234.htm 一、MySQL各類安裝方法的比較 在Linux系統下,MySQL有3種主

linux下 elasticsearch的安裝配置

1. 安裝地址 https://www.elastic.co/products/elasticsearch 2. 使用 xshell 將壓縮包上傳到linux上,解壓elasticsearch-5.6.1.tar.gz到/home目錄下。 切記不要放在root目錄下 3

linux裝置樹pinctrl的配置

最近在移植linux,用到kernel版本為3.18.22和4.1.3,在高版本的核心原始碼中用到了裝置樹(device-tree),裝置樹中用到pinctrl的配置,記錄一下。 1、普通設定 在配置串列埠時,pinctrl的配置資訊如下所示: <span st

linux裝置樹pinctrl的配置

上一篇記錄了裝置樹檔案中管腳普通配置的查詢與確定,這篇介紹一下特殊的配置。 首先還是先看程式碼,看看到底特殊到哪裡。 <span style="font-size:14px;"> pi

細說UGUIAnchorPivot

背景 相信剛接觸UGUI的時候,這套新系統中RectTransform裡的position,anchor,pivot一定讓大家有點迷糊不適應吧。因此就想詳細介紹一下這幾個概念,之所以標題中沒有提position,是因為主要還是以介紹Anchor和Pivot為主

Lua使用動態連結庫呼叫C模組VS2015

建立一個win32專案,起名mylib(因為我已經建立過mylib了,所以這裡用的mylib2)。 下面開始編寫C模組: mylib.h如下: /*mylib.h*/ #define _CRT_SECURE_NO_WARNINGS #includ

WPF繪畫動畫3

1. 矩形 矩形由筆觸(Stroke,即邊線)和填充(Fill)構成。Stroke屬性的設置於Line一樣,Fill屬性的資料型別是Brush。Brush是個抽象類,所以我們不可能拿一個Brush類的例項為Fill屬性賦值而只能用Brush派生類的例項進行賦值。 WPF的繪

WPF繪畫動畫2

一、以下一一講解: 1. 直線 直線是最簡單的圖形。使用X1、Y1 兩個屬性可以設定它的起點座標,X2、Y2倆個屬性則用來設定其終點座標。控制起點/終點座標就可以實現平行、交錯等效果。Stroke(筆觸)屬性的資料型別是Brush(畫刷),凡是Brush的派生類均可用於給這

動態規劃初識狀態壓縮入門

想必很多人還不知道動態規劃是可以狀態壓縮的吧,通俗的講就是把維數變小,一般就是把二維陣列降為一維。維數變小意味著空間變小,速度還不變,不用空間換時間,這就是狀態壓縮的強大之處。 以leetcode64題最小路徑和為例,帶大家一步一步見識一下狀態壓縮這個小技巧 題意:給定一個包含非負整數的 m x n 網格

struts2學習(14)struts2文件上傳下載4多個文件上傳下載

sym ring spl out urn ide http iso length 四、多個文件上傳: 五、struts2文件下載: 多個文件上傳action com.cy.action.FilesUploadAction.java: package com.cy.a

Django使用bookstarp框架4

取出 閱讀 manage http get png con 下載 join Django中使用bookstarp框架(4) 註意:要使用bookstarp框架前,要先有css的基礎 因為主要是研究後臺的使用方法,就引入前端的框架,簡化html上的耗時(主要是不想把時間浪費在

在VS2012采用C++調用DLL的函數4

color style 屬性 cls weight 項目 新建工程 ifdef xxx 轉自:http://www.cnblogs.com/woshitianma/p/3683495.html 這兩天因為需要用到VS2012來生成一個DLL代碼,但是之前並沒有用過DLL相關

python---djangoorm的使用4字段,參數on_delete重點補充

protect 設置 lean 速度 str through 存在 也會 ren 1.索引: 普通索引:加快查找速度 唯一索引:加快查找速度,唯一約束 主鍵索引:加快查找速度,唯一索引,不為空 class UserInfo(models.Model): user

Linux運維常見基礎面試練習題4- 提升

Linux運維 Linux學習 Linux入門 Linux基礎 Linux運維常見基礎面試練習題(4)- 提升1 (ZZ)命令是在vi編輯器中執行存盤退出 A :q B ZZ C :q! D :WQ 2 用虛擬機安裝了一臺Linux系統,突然想克隆一臺服務器,克隆後發現無法上網

指針引用4指向指針的指針

錯誤 可能 中修改 數組 三個參數 參數 argc clas 指向 1.知識點 (1)在程序中可以聲明指向任何數據類型的指針,指針也可以指向指針類型,成為指向指針的指針。下面是一個使用例子 1 int a=10,b=20; 2 int *q=&a; 3 int *

ldap 使用者組使用者4

      Posixgroup使用者組屬性   預設情況下openldap的使用者組屬性是Posixgroup,Posixgroup使用者組屬性和使用者沒有實際的對應關係。如果我們一定要把Posixgroup和user對應起來的話,就需要單獨把使用者設定到Posixgr

Linux初學時的一些常用命令4

1. 磁碟 檢視當前磁碟使用情況 df -h 檢視某個檔案大小 du -sh 檔名 如果不輸入檔名,預設是當前目錄的所有檔案之和,即當前目錄大小 2. 系統記憶體 free 3. CPU CPU 使用情況 top top -bn 1 -i -c