PE結構:導入表中的雙橋結構

導入表的知識曾經整理過,網址如下;現再對其中的雙橋結構進行整理。
《PE文件:導入表》:https://blog.csdn.net/weixin_43742894/article/details/105155489

導入表結構:

struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics; 
        DWORD   OriginalFirstThunk; //指向INT    橋1    
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;         //時間戳
    DWORD   ForwarderChain;         
    DWORD   Name;				   //dll名稱
    DWORD   FirstThunk;            //指向IAT    橋2
} IMAGE_IMPORT_DESCRIPTOR;

OriginalFirstThunk
+0000h,雙字。因爲它是指向另外數據結構的通路,因此簡稱爲橋1.該字段指向一個包含一系列結構的數組。
指向的數組中的每個結構定義了一個導入函數的信息,最後以一個全0的結構作爲結束。指向的數組中每一項爲一個結構,此結構的名稱是IMAGE_THUNK_DATA。該結構實際上只是一個雙字,但在不同的時刻卻擁有不同的解釋。
該字段有兩種解釋:

雙字最高位爲0,表示導入符號是一個數值,該數值是一個RVA。

雙字最高位爲1,表示導入符號是一個名稱。

FirstThunk

+0010h,雙字。與OriginalFirstThunk相同,它指向的連接表定義了對Name1這個動態鏈接庫引入的所有導入函數,簡稱橋2。

雙橋結構:
其中OriginalFirstThunk和FirstThunk指向相同的結構體_IMAGE_THUNK_DATA。這倆個結構體又分別指向同一個結構體IMAGE_THUNK_DATA,他們倆個又同時指向IMAGE_IMPORT_BY_NAME,這也就是雙橋結構。

struct _IMAGE_THUNK_DATA32{
    union {
        DWORD ForwarderString; //指向一個轉向者字符串的RVA
        DWORD Function ;       //被輸入的函數的內存地址
        DWORD Ordinal ;        //被輸入的API的序數值
       DWORD  AddressOfData ;  //高位爲0則指向IMAGE_IMPORT_BY_NAME 結構體二
    }u1;
}IMAGE_THUNK_DATA32;

這個結構的重要成員有:

Function:輸入函數的內存地址。
Ordinal :被輸入的API的序數值。
AddressOfData:指向IMAGE_IMPORT_BY_NAME

typedef struct _IMAGE_IMPORT_BY_NAME{
    WORD Hint;      //序號
    BYTE NAME[1];   //函數名
}IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;

Hint:

+0000h,雙字。函數的編號,在DLL中對每個函數都進行了編號,訪問函數時可以通過名稱訪問,也可以通過編號訪問。
Name1:

+0004h,大小不確定。函數名字字符串的具體內容,以“\0”作爲字符串結束標誌。

在文件中儘管通過橋2和橋1指向的數據值相同,但其實存儲的位置卻是不同的。橋1指向的INT與橋2指向的IAT內容完全一樣,但INT和IAT卻存儲在文件的不同位置。

雙橋結構圖示:
在這裏插入圖片描述
橋1和橋2最終指向了一個目的地,都指向了引入函數的IMAGE_IMPORT_BY_NAME描述部分。而從橋2到目的地的過程中,還經歷了另外一個很重要的結構IAT。

INT: 橋1,最高位爲0,這是一個RVA,表明函數是以字符串類型的函數名導入的先將RVA轉換爲FOA,值爲0x00000654,從文件的該位置開始讀取雙字,知道去除的雙字爲“0”結束。每一個雙字都 是結構IMAGE_THUNK_DATA。

差異:
在內存中,橋1可以讓你找到調用的函數名稱或函數的索引編號,橋2卻可以幫助你找到該函數指令代碼在內存空間的地址。
導入表與IAT的關係如下:
當PE被加載進虛擬地址空間以後,IAT的內容會被操作系統更改爲函數的VA。這個修改最終導致通向“值-名稱”描述的橋2發生斷裂,如
在這裏插入圖片描述
當橋2發生斷裂後,如果沒有橋1作爲參照(因爲橋1和橋2維護了兩個一一對應的函數RVA),我們就無法重新找到該地址到底是調用了那個函數。這就是爲什麼在導入表數據結構中存在兩個橋的原因,也是爲什麼單橋導入表結構中無法實施綁定的原因。

雙橋斷裂的過程:
在PE加載的時候,雙橋結構會斷裂,IAT 會被PE加載器重寫,PE加載器先搜索INT,PE加載器迭代搜索INT數組中的每個指針,找出 INT所指向的IMAGE_IMPORT_BY_NAME結構中的函數在內存中的真正的地址,並把它替代原來IAT中的值。
也就是當程序加載到內存以後,導入表部分發生變化的值正是IMAGE_IMPORT_DESCRIPTOR結構中的FirstThunk字段指向的函數指針表內容。
這些內容已經不是指向函數名的指針了,而是指向了虛擬內存中該函數的可執行代碼的地址所以其含義也由原來的函數指針更改爲函數的入口地址。現在看來,所有的這些值最終都指向了同一片連續的區域,從而形成了我們常說的IAT。

總結:

1.在內存中,橋1可以讓你找到調用的函數名稱或函數的索引編號;橋2卻可以幫助你找到該函數指令代碼在內存空間的地址。
2.IAT原本指向函數名的指針,雙橋結構斷裂後,指向了虛擬內存中該函數的可執行代碼的地址。
3.爲什麼要有INT和IAT倆個橋:當橋2發生斷裂後,如果沒有橋1作爲參照(因爲橋1和橋2維護了兩個一一對應的函數RVA),我們就無法重新找到該地址到底是調用了那個函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章