文章目錄
- 本篇内容介紹
- 導出表 IMAGE_EXPORT_DIRECTORY
- 導入表 IMAGE_IMPORT_DESCRIPTOR
- 資源 IMAGE_RESOURCE_DIRECTORY
本篇内容介紹
上節文章記錄了PE檔案的結構概述,我們知道了PE頭中第三個字段
IMAGE_OPTIONAL_HEADER32
結構體中變量
DataDirectory
數組中的資料類型為
IMAGE_DATA_DIRECTORY
,定義如下,而sdk中定義了其指向的一些不同類型的成員資訊。本篇文章就記錄一些比較重要的成員的資訊。
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; //資料塊起始RVA位址
DWORD Size; //資料塊大小
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
補充: 這個數組大小為16,sdk定義了15個(值為0~14)類型資訊,剩下一個預留保持全零。
導出表 IMAGE_EXPORT_DIRECTORY
導出表是PE檔案為其他應用程式提供API的一種函數示例導出方式。Windows下存在導出表的可執行檔案以指定自身的一些變量、函數以及類,并将其導出,以便提供給其他程式使用。
導出表結構為
IMAGE_EXPORT_DIRECTORY
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用,總為0
DWORD TimeDateStamp; // 檔案建立時間戳
WORD MajorVersion; // 總為0
WORD MinorVersion; // 總為0
DWORD Name; // 指向一個代表此dll名字的ASCII字元串的RVA
DWORD Base; // 函數的起始序号
DWORD NumberOfFunctions; // 導出函數的總數
DWORD NumberOfNames; // 以名稱方式導出的函數的總數
DWORD AddressOfFunctions; // 指向輸出函數位址的RVA,是以 4 位元組為一個機關的數組元素,每個元素代表函數入口
DWORD AddressOfNames; // 指向輸出函數名字的RVA,是以 4 位元組為一個機關的數組元素,每個元素代表一個指向字元串的 RVA
DWORD AddressOfNameOrdinals; // 指向輸出函數序号的RVA,是以 2 位元組為一個機關的數組元素,每個元素代表對應名字在 AddressOfFunctions 中的序号
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
注意: 檔案中儲存的序号并不是調用函數時使用的,平時調用函數使用的序号減去Base(序号基數)的值才能得到檔案中儲存的序号。也将檔案中儲存的序号稱為原始序号,而将調用函數時使用的序号稱為調用序号。
另外從邏輯上講,導出表由3部分構成,分别是名稱表、函數表與序号表,其中函數表與序号表是必須要有的,而名稱表則是可選的。序号表與名稱表的作用就是索引,引導調用者找到真正需要的函數表,而函數表中儲存的就是這個被導出的函數位址資訊。
示例:分析一個dll檔案的内容
用LordPE檢視區段資訊。

01edit中找到導入表的位置,0x00002570是導入表的RVA,0x00000089是其大小。
注意: 這裡的0x00000089大小和導出表結構體40位元組大小貌似沒有關系,這個大小到底指的是什麼意思以後再回來探索。如下圖為DataDirectory數組内容
導出表在檔案中的偏移=0x00002570-0x00002000+0x00000E00=0x1370,
找到0x1370位置資料跟
IMAGE_EXPORT_DIRECTORY
對應
結構體IMAGE_EXPORT_DIRECTORY中的變量Name
0x000025C0 --> 檔案偏移位置0x000025C0-0x00002000+0x00000E00=0x13C0 --> PEDemo.dll
結構體IMAGE_EXPORT_DIRECTORY中的變量AddressOfFunctions(函數入口位址),按照上面的資訊斷定有4個導出函數
0x00002598 --> 0x00002598-0x00002000+0x00000E00=0x1398
結構體IMAGE_EXPORT_DIRECTORY中的變量AddressOfNames
0x000025a8 --> 0x000025A8-0x00002000+0x00000E00=0x13a8
檢視0x000025cb-0x00002000+0x00000e00=0x13cb,可以看到導出的函數名
結構體IMAGE_EXPORT_DIRECTORY中的變量AddressOfNameOrdinals
0x000025b8 --> 0x000025b8-0x00002000+0x00000E00=0x13b8
導出表結構
導入表 IMAGE_IMPORT_DESCRIPTOR
導入表本身僅相當于一個引導者的角色,并不能完成PE檔案中的整個導入工作,隻負責引導系統找到真正儲存有導入資訊的其他兩個結構,這兩個結構分别為
IMAGE_THUNK_DATA
與
IMAGE_IMPORT_BY_NAME
。
IMAGE_IMPORT_DESCRIPTOR結構的個數是由導入映像檔案的個數決定的,需要從多少個映像檔案中導入函數,就要有多少個結構,最後以一個空的 IMAGE_IMPORT_DESCRIPTOR結構結束。
IMAGE_IMPORT_DESCRIPTOR結構定義如下
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // 包含指向INT(輸入名稱表)RVA 的結構數組
};
DWORD TimeDateStamp; //時間辨別
DWORD ForwarderChain; //第一個被轉向的API索引
DWORD Name; //指向被導入的DLL名稱
DWORD FirstThunk; //指向輸入位址表(IAT)RVA,IAT是一個IMAGE_THUNK_DATA結構的數組
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
OriginalFirstThunk指向導入名稱表的數組,數組元素為IMAGE_THUNK_DATA。數組以空的結構體結束。
一般情況下,數組中的每個元素會再指向IMAGE_IMPORT_BY_NAME結構體。
IMAGE_IMPORT_BY_NAME結構以一個結構體數組形式存在,并以一個空的結構體結束。
IMAGE_THUNK_DATA定義如下
{
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData; //指向IMAGE_IMPORT_BY_NAME結構,當以上三個值都無效時,此值有效。
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 *PIMAGE_THUNK_DATA32;
IMAGE_IMPORT_BY_NAME結構定義如下
typedef struct _IMAGE_IMPORT_BY_NAME {
USHORT Hint; //導入函數的序号
UCHAR Name[1]; //導入函數的名稱
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
導入表結構
資源 IMAGE_RESOURCE_DIRECTORY
資源在PE檔案中是以目錄結構的形式存在的,一般情況下這個目錄分為3層,從根目錄開始分别為資源類型、目錄資源ID與資源代碼頁。
這3層目錄結構都是由一個 IMAGE_RESOURCE_DIRECTORY結構為頭部的,并且在其後面跟着一個IMAGE_RESOURCE _DIRECTORY_ENTRY結構數組。
IMAGE_RESOURCE_DIRECTORY結構主要負責指出後面結構數組的成員個數,而後面結構數組的每個成員則分别指向下一層目錄結構(或資源資料)。
IMAGE_RESOURCE_DIRECTORY定義如下
{
DWORD Characteristics; //資源屬性辨別
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries; //資源名稱個數
WORD NumberOfIdEntries; //資源id個數
} IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY;
IMAGE_RESOURCE_DIRECTORY_ENTRY定義如下
{
__C89_NAMELESS union {
__C89_NAMELESS struct {
DWORD NameOffset:31; //資源名偏移(指向一個結構體)
DWORD NameIsString:1; //值為1時,NameOffset指向IMAGE_RESOURCE_DIR_STRING_U結構體,其中儲存資源名稱
}DUMMYSTRUCTNAME;
DWORD Name; //位于第一層目錄時儲存資源類型的值,位于第三層目錄時儲存資源語言區域類型值
WORD Id; //資源的id
}DUMMYUNIONNAME;
__C89_NAMELESS union {
DWORD OffsetToData; //資料偏移RAV
__C89_NAMELESS struct {
DWORD OffsetToDirectory:31; //指出下一層子目錄相對資源目錄起始位址偏移
DWORD DataIsDirectory:1;
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;
在經過三層目錄的索引後,最後會到達一個 IMAGE_RESOURCE_DATA_ENTRY結構中,這個結構指引到資源資料。
定義如下
{
DWORD OffsetToData; //資源資料RAV指針
DWORD Size; //資源資料大小
DWORD CodePage; //資源代碼頁資訊
DWORD Reserved; //保留,恒為0
} IMAGE_RESOURCE_DATA_ENTRY,*PIMAGE_RESOURCE_DATA_ENTRY;