0 DLL介紹c++
DLL翻譯器爲動態連接庫,原來不存在DLL的概念只有,庫的概念,編譯器會把從庫中獲取的二進制代碼插入到應用程序中。在如今windows操做系統使用了數量龐大的庫函數(進程,內存,窗口,消息)採用這種包含庫的方式,會形成內存嚴重的浪費。所以,windows OS 設計者根據須要引入了DLL這一律念windows
(1) 不要把庫包含到程序中,單獨組成DLL文件,須要時調用便可
(2) 內存映射技術使加載後的DLL代碼,資源在多個進程中實現共享
(3) 更新庫時只要替換相關的DLL文件便可數組
IMAGE_IMPORT_DESCRIPTOR 結構體記錄着PE文件要導入哪些庫文件
IMAGE_IMPORT_DESCRIPTOR 結構體以下函數
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound INT (PIMAGE_THUNK_DATA) 存着INT表地址 } DUMMYUNIONNAME; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; //導入庫的名字 DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) 存着IAT表地址 } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
執行一個普通程序每每須要導入多個庫,導入多個庫就存在多少IMAGE_IMPORT_DESCRIPTOR結構體,這些結構體造成了數組,且結構體數組最後以NULL結構體結束。測試
項目 | 含義 |
---|---|
OriginalFirstThunk | INT的地址(RVA) |
Name | 庫的名字 |
FirstThunk | IAT的地址(RVA) |
IAT 和 INT 存着不少IMAGE_THUNK_DATA,IMAGE_THUNK_DATA結構體彙總只有一個聯合體,通常用四字節的AddressOfData來獲取this
typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; //RVA 指向_IMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; //可能爲0,編譯器決定,若是不爲0,是函數在導出表中的索引 BYTE Name[1]; //函數名稱,以0結尾,因爲不知道到底多長,因此乾脆只給出第一個字符,找到0結束 } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
INT 導入姓名錶 IAT爲導入地址表,程序在初始化時須要把獲取庫中的函數地址存入IAT表中進行IAT的初始化,在開始時INT和IAT指的是同一個結構體IMAGE_IMPORT_BY_NAME(我在XP環境中測試的時候記事本程序時在文件中,INT表存的是IMAGE_IMPORT_BY_NAME 結構體,IAT表存的是寫死的函數地址)操作系統
描述IMAGE_IMPORT_DESCRIPTOR 的示意圖
翻譯
下面瞭解一下PE裝載器把導入函數輸入至IAT的順序設計
- 讀取IID的Name成員,獲取庫名稱字符串("kernel32.dll")
- 裝載相應庫 ->LoadLibrary("kernel32.dll")
- 讀取IID的OriginalFirstThunk成員,獲取INT地址
- 逐一讀取INT中數組的值,該值是IMAGE_IMPORT_BY_NAME的地址,而後獲取IMAGE_IMPORT_BY_NAME結構體中的Hint 或者Name項 使用GetProcAddress()獲取相應的起始地址
- 讀取IID的FirstThunk(IAT)成員,獲取IAT地址
- 將上面獲取的函數地址輸入相應的IAT數組值
- 重複4-6這幾個步驟直到INT結束
IMAGE_IMPORT_DESCROPTOR 結構的起始地址存在於 可選頭的DataDirectory 數組第二個位置IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress的值code
- 首先經過PEwiew查看 notepad.exe的PE可選頭 看出 IMPORT表 RVA 7604 => RAW 6A04
- 咱們查到了導入表的首地址咱們用HXd查看一下
咱們能夠看到數組的最後是以NULL結尾的
- 我先列一下第一個導入表對應的各個地址,全是F的話就是還未裝載,在進入內存是將會裝載
地址(RVA) | 含義 | 地址(RAW) |
---|---|---|
7990 | OriginalFirstThunk | 6D90 |
FFFFFFFF | TimeDateStamp | 0 |
FFFFFFFF | ForwarderChain | 0 |
7AAC | Name | 6EAC |
12C4 | FirstThunk | 06C4 |
- 先看一下這個庫的名字 是 comdlg32.dll
- 查看導入姓名錶 中的 IMAGE_THUNK_DATA32 結構
RVA 7A7A => RAW 6E7A 爲 IMAGE_IMPORT_BY_NAME 結構體的首地址
- 查看IMAGE_IMPORT_BY_NAME 結構體
能夠看出000F爲函數的編號 ,後面則是導入的函數名字 PageSetupDlgW
- 查看IAT表中的 IMAGE_THUNK_DATA32 結構
IAT表中的IMAGE_THUNK_DATA32 結構存的就不是姓名錶的地址了,而是一個寫死了的,該函數的內存地址,爲了兼容其餘系統,程序在執行時會經過IAT和庫的導出表來動態獲取該函數的地址。並把地址存入IAT表的對應位置,而後進行下一個函數名稱查找,直到把全部函數查找完。
- 咱們經過OllyIce查看在內存中IAT表的樣子
看着該函數地址與在hxd文件同樣,那是由於在xp系統下,換個系統這兩個值就可能會不一致。