PE文件結構(PE文件簡介)
[2008/3/27 15:56:00| By:perfecter]
1、 PE文件簡介
PE(Portable Executable,可移植的可執行文件)文件是指在Microsoft Windows95及其之後的Microsoft操作系統上運行的可執行文件,包括.EXE文件和.DLL文件。
可移植性(Portable)是指在任何機器(Intel 386 、MIPS 、Alpha 、Power PC 等)上的Microsoft Windows操作系統都可以使用相同的可執行文件格式,使得程序加載器以及程序開發工具不需要針對每一個新的操作系統重寫。
2、 相關概念
① RVA(Relative Virtual Address)
當被裝載到內存中,可執行文件的某一個項目相對於基地址的偏移。比如一個可執行文件被裝載到虛擬地址空間的0x40000處,其中有一個項目位於0x401464處,那麼它的RVA就是0x1464。虛擬地址(0x401464)- 基地址(0x400000)= RVA(0x1464)
② Section(節)
PE文件中最基本的代碼或者數據單元。例如,PE文件中的所有代碼可以被放在同一個節中,或者每一個函數都可以被放到不同的節中去。節有點類似於Intel 8086的段。PE文件可以有多個節,像.text(代碼節)以及.data(數據節)節等。
3、 PE文件結構
下面就是一個典型的PE文件的結構示意圖:
MS-DOS 頭 Offset to |
MS DOS 2.0 Stub Program |
Magic Number PE Header |
Optional header |
Section headers |
Sections |
PE文件結構(PE文件頭一)
[2008/3/27 15:58:00| By:perfecter]
3、 PE文件結構
① MS-DOS頭
MS-DOS頭在winnt.h中定義成爲IMAGE_DOS_HEADER,這個結構中,最需要關心的是成員e_lfanew,它給出了PE Header在文件中的偏移量。比如,e_lfanew的值是0xE0,則PE Header在文件距離開頭0xE0處。
② MS DOS 2.0 Stub Program
這是一段DOS程序,如果把win32的可執行文件放到dos上執行,這段代碼將在屏幕上顯示類似於“This program can not run in dos”的信息。
③ Magic Number和PE Header
這纔是真正的win32可執行文件的開始。Magic Number是一個DWORD類型的數,值是0x4550,對應的ASCII值就是“PE”。PE Header在winnt.h裏被定義成爲:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
其中:
Machine:聲明PE文件是在那種CPU架構下運行,它可以是下表中所列出的值:(通常PE文件運行在x86系列的CPU架構下,這個值就應該是0x
標誌 |
值 |
說明 |
IMAGE_FILE_MACHINE_UNKNOWN |
0x0 |
假定在所有機器上運行 |
IMAGE_FILE_MACHINE_AM33 |
0x1d3 |
Matsushita AM33 |
IMAGE_FILE_MACHINE_AMD64 |
0x8664 |
x64 |
IMAGE_FILE_MACHINE_ARM |
0x |
ARM little endian |
IMAGE_FILE_MACHINE_EBC |
0xebc |
EFI byte code |
IMAGE_FILE_MACHINE_I386 |
0x |
Intel 386家族及其兼容CPU |
IMAGE_FILE_MACHINE_IA64 |
0x200 |
Intel Itanium處理器家族 |
IMAGE_FILE_MACHINE_M32R |
0x9041 |
Mitsubishi M32R little endian |
IMAGE_FILE_MACHINE_MIPS16 |
0x266 |
MIPS16 |
IMAGE_FILE_MACHINE_MIPSFPU |
0x366 |
MIPS with FPU |
IMAGE_FILE_MACHINE_MIPSFPU16 |
0x466 |
MIPS16 with FPU |
IMAGE_FILE_MACHINE_POWERPC |
0x |
Power PC little endian |
IMAGE_FILE_MACHINE_POWERPCFP |
0x |
有浮點支持的Power PC |
IMAGE_FILE_MACHINE_R4000 |
0x166 |
MIPS little endian |
IMAGE_FILE_MACHINE_SH3 |
0x |
|
IMAGE_FILE_MACHINE_SH3DSP |
0x |
|
IMAGE_FILE_MACHINE_SH4 |
0x |
|
IMAGE_FILE_MACHINE_SH5 |
0x |
|
IMAGE_FILE_MACHINE_THUMB |
0x |
Thumb |
IMAGE_FILE_MACHINE_WCEMIPSV2 |
0x169 |
MIPS little-endian WCE v2 |
NumberOfSections:表示PE文件中節的數量。
TimeDateStamp:鏈接器產生這個文件的時間,是自從
PointerToSymbolTable、NumberOfSymbols:一般只對COFF(Common Object File Format)格式文件有用。
SizeOfOptionalHeader:它是Optional header的大小,也就是sizeof(IMAGE_OPTIONAL_HEADER)(Optional header被定義成爲IMAGE_OPTIONAL_HEADER結構)。
Characteristics:聲明這個PE文件的性質,它可以是下表列出的值,並且可以按位或:(對於一個DLL文件,這個值應該通常是0x2102;對於一個EXE文件,這個值通常是0x0103)
標誌 |
值 |
描述 |
IMAGE_FILE_RELOCS_STRIPPED |
0x0001 |
用於Windows CE,Windows NT以及後續操作系統。聲明此PE文件沒有基礎重定位,並且必須裝載到預先定義的基地址。如果基地址不可用,裝載器將報錯。 |
IMAGE_FILE_EXECUTABLE_IMAGE |
0x0002 |
這是一個可執行文件。 |
IMAGE_FILE_LINE_NUMS_STRIPPED |
0x0004 |
這一位應該置零。 |
IMAGE_FILE_LOCAL_SYMS_STRIPPED |
0x0008 |
這一位應該置零。 |
IMAGE_FILE_AGGRESSIVE_WS_TRIM |
0x0010 |
建議不要再Windows 2000機器後續系統中使用,應該置零。 |
IMAGE_FILE_LARGE_ADDRESS_ AWARE |
0x0020 |
程序可以處理大於 |
|
0x0040 |
此位爲將來使用而保留。 |
IMAGE_FILE_BYTES_REVERSED_LO |
0x0080 |
這一位應該置零。 |
IMAGE_FILE_32BIT_MACHINE |
0x0100 |
機器以32位架構爲基礎。 |
IMAGE_FILE_DEBUG_STRIPPED |
0x0200 |
調試信息已經被移除。 |
IMAGE_FILE_REMOVABLE_RUN_ FROM_SWAP |
0x0400 |
如果文件位於可移動的媒體上,那麼將整個文件讀入交換文件。 |
IMAGE_FILE_NET_RUN_FROM_SWAP |
0x0800 |
如果文件位於網絡上,那麼將整個文件讀入交換文件。 |
IMAGE_FILE_SYSTEM |
0x1000 |
文件是一個系統文件,不是用戶文件 |
IMAGE_FILE_DLL |
0x2000 |
文件是一個動態鏈接庫,雖然它們不能直接運行,也被認爲是一個可執行文件。 |
IMAGE_FILE_UP_SYSTEM_ONLY |
0x4000 |
只能在單芯片機器上運行 |
IMAGE_FILE_BYTES_REVERSED_HI |
0x8000 |
這一位應該置零。 |
PE文件結構(PE文件頭二)
[
① Optional header
從字面上看,這個文件頭是可選的,但實際上它是PE文件中必不可少的。它在winnt.h中被定義稱爲:
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
其中:
Magic:聲明PE文件的狀態,如果是普通的PE文件,值爲0x10B;如果是隻讀的,值爲0x107。
MajorLinkerVersion、MinorLinkerVersion:鏈接器的版本號,但實際上這兩個值並不可靠,某些鏈接器不會設置這兩個值。
SizeOfCode:所有代碼節的大小。
SizeOfInitializedData、SizeOfUninitializedData:所有已初始化數據節、未初始化數據節的大小。
AddressOfEntryPoint:這是一個RVA。當PE文件被裝載到內存中以後,第一條可執行指令的地址。對於設備驅動,這是初始化函數的地址;對於DLL,這個入口點是可選的,如果DLL沒有入口點,則這個值必須爲零。
BaseOfCode、BaseOfData:代碼的RVA,已初始化數據的RVA。
ImageBase:PE文件的優先裝載地址。比如,這個值是0x10000000,則裝載器會將PE文件優先裝載到虛擬內存地址0x10000000中。通常.EXE文件的這個值是0x00400000;.DLL文件的是0x10000000。
SectionAlignment:PE文件裝載到內存後,節的對齊粒度,必須大於等於FileAlignment。WIN32下,一般是0x1000,WIN64下,一般是0x2000。
FileAlignment:PE文件中,節的對齊粒度。一般情況下是0x200的倍數。
MajorOperatingSystemVersion、MinorOperatingSystemVersion:期望的操作系統版本。
MajorImageVersion、MinorImageVersion:期望的PE文件版本,某些鏈接器不設定這個值。
MajorSubsystemVersion、MinorSubsystemVersion:期望的子系統版本。
Win32VersionValue:這個是保留的,必須是0。
SizeOfImage:PE文件裝載到內存後,整個鏡像的大小,必須是SectionAlignment的整數倍。
SizeOfHeaders:MS-DOS頭、MS DOS 2.0 Stub Program、Magic Number、PE Header和Optional header大小之和,必須是FileAlignment的整數倍。
CheckSum:對於普通的PE文件,這個值是0。
Subsystem:聲明PE文件在什麼樣的系統上運行,可以是下表中的值:(對於WINDOWS開發,通常選擇第三項或者第四項)
Constant |
Value |
Description |
IMAGE_SUBSYSTEM_UNKNOWN |
0 |
未知子系統。 |
IMAGE_SUBSYSTEM_NATIVE |
1 |
設備驅動以及WINDOWS內部程序。 |
IMAGE_SUBSYSTEM_WINDOWS_GUI |
2 |
WINDOWS GUI 程序。 |
IMAGE_SUBSYSTEM_WINDOWS_CUI |
3 |
WINDOWS控制檯程序。 |
IMAGE_SUBSYSTEM_POSIX_CUI |
7 |
Posix字符子系統程序。 |
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI |
9 |
Windows CE。 |
IMAGE_SUBSYSTEM_EFI_APPLICATION |
10 |
可擴展固件程序。 |
IMAGE_SUBSYSTEM_EFI_BOOT_ SERVICE_DRIVER |
11 |
啓動服務的EFI驅動。 |
IMAGE_SUBSYSTEM_EFI_RUNTIME_ DRIVER |
12 |
運行時的EFI驅動。 |
IMAGE_SUBSYSTEM_EFI_ROM |
13 |
EFI只讀鏡像。 |
IMAGE_SUBSYSTEM_XBOX |
14 |
XBOX。 |
DllCharacteristics:聲明DLL文件的性質,可以是下表中的值:(如果沒有特別需要,這個值是零)
常量 |
值 |
描述 |
|
0x0001 |
保留的,必須爲零。 |
|
0x0002 |
保留的,必須爲零。 |
|
0x0004 |
保留的,必須爲零。 |
|
0x0008 |
保留的,必須爲零。 |
IMAGE_DLL_CHARACTERISTICS_ DYNAMIC_BASE |
0x0040 |
DLL可以在運行時被重置。 |
IMAGE_DLL_CHARACTERISTICS_ FORCE_INTEGRITY |
0x0080 |
強制進行代碼完整性檢查。 |
IMAGE_DLL_CHARACTERISTICS_ NX_COMPAT |
0x0100 |
映像是NX兼容的。 |
IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION |
0x0200 |
不隔離映像文件。 |
IMAGE_DLLCHARACTERISTICS_ NO_SEH |
0x0400 |
不使用結構化異常處理。 |
IMAGE_DLLCHARACTERISTICS_ NO_BIND |
0x0800 |
不綁定映像 |
|
0x1000 |
保留的,必須爲零。 |
IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER |
0x2000 |
WDM驅動。 |
IMAGE_DLLCHARACTERISTICS_ TERMINAL_SERVER_AWARE |
0x8000 |
終端服務器 |
SizeOfStackReserve:預留棧的大小,一般默認爲0x10000。
SizeOfStackCommit:提交棧的大小,一般默認爲0x1000。
SizeOfHeapReserve:預留堆的大小,一般默認爲0x10000。
SizeOfHeapCommit:提交堆的大小,一般默認爲0x1000。
LoaderFlags:保留的,必須爲零。
NumberOfRvaAndSizes:之後的數據目錄的數量,強烈建議使用默認的16。
DataDirectory:數據目錄,每一個對應一個節,聲明該節的RVA和大小。
② Section headers
每一個節對應一個與之相關的節頭,節頭聲明瞭節的大小、RVA以及的性,在winnt.h中,節頭定義如下:
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
其中:
Name:一個8字節長度的變量,給出節的名字。如果名字中的字符少於8字節,則用NULL填充;如果剛好等於8字節,則不需要以NULL結束。一般情況下,代碼節名稱爲“.text”,數據節爲“.data”。
Misc:這是一個聯合體,在可執行映像中,使用的是VirtualSize,聲明對應的節的大小。
VirtualAddress:當PE文件讀入內存後,對應節的RVA。
SizeOfRawData:磁盤上,節根據FileAlignment對齊後的大小,必須是FileAlignment的倍數。
PointerToRawData:從文件開頭到對應節的偏移量。
PointerToRelocations
PointerToLinenumbers
NumberOfRelocations
NumberOfLinenumbers:以上四個變量在可執行文件中用不到。
Characteristics:聲明節的性質:可以是下表中的值,並可以按位或:
標誌 |
值 |
描述 |
IMAGE_SCN_CNT_CODE |
0x00000020 |
節包含可執行代碼。 |
IMAGE_SCN_CNT_INITIALIZED_DATA |
0x00000040 |
節包含已經初始化的數據。 |
IMAGE_SCN_CNT_UNINITIALIZED_ DATA |
0x00000080 |
節包含未初始化的數據。 |
IMAGE_SCN_LNK_INFO |
0x00000200 |
節包含註釋或其他信息。只用於目標文件。 |
IMAGE_SCN_LNK_REMOVE |
0x00000800 |
節不是映像的一部分,只用於目標文件。 |
IMAGE_SCN_LNK_COMDAT |
0x00001000 |
節包含COMDAT數據。只用於目標文件。 |
IMAGE_SCN_GPREL |
0x00008000 |
節包含引用全局指針的數據。 |
IMAGE_SCN_LNK_NRELOC_OVFL |
0x01000000 |
節包含擴展重定位。 |
IMAGE_SCN_MEM_DISCARDABLE |
0x02000000 |
根據需要,節可被廢棄。 |
IMAGE_SCN_MEM_NOT_CACHED |
0x04000000 |
節不可被緩存。 |
IMAGE_SCN_MEM_NOT_PAGED |
0x08000000 |
節不可被分頁。 |
IMAGE_SCN_MEM_SHARED |
0x10000000 |
節可在內存中被共享。 |
IMAGE_SCN_MEM_EXECUTE |
0x20000000 |
節可以執行。 |
IMAGE_SCN_MEM_READ |
0x40000000 |
節可被讀取。 |
IMAGE_SCN_MEM_WRITE |
0x80000000 |
節可被寫入。 |
PE文件結構(節) 完
[2008/4/2 15:07:00| By:perfecter]
① Sections
每一個節對應一個節頭,節的數量必須與PE Header中NumberOfSections聲明的相一致。幾個比較常見的節是:輸出符號節、輸入符號節、數據節、重定位節和代碼節。比較複雜的是輸出符號節、輸入符號節和重定位節:
輸出符號節,包含一些可以供其他映像文件使用的符號信息。GetProcAddress()函數就是通過這個節找到需要的函數的。
輸出符號包含若干個表,見下表。通常只需要輸出目錄表(Export directory table)和輸出地址表(Export address table)。
表名 |
描述 |
Export directory table |
這個表說明其他輸出標的位置和大小。 |
Export address table |
輸出表的RVA數組,是可執行文件內可執行代碼節和數據節中的輸出函數和數據的真實地址。利用這個表,其他的映像可以使用這些符號。 |
Name pointer table |
按升序排列的公共輸出名的數組。 |
Ordinal table |
按Name pointer table的成員順序排序的數組。因此,Name pointer table 和Ordinal table相同數量的成員。 每一個序數都是一個指向Export address table的索引。 |
Export name table |
一組連續的以NULL結尾的ASCII字符。Name pointer table的成員指向這個區域。這些是輸入或輸出符號的公共名稱;它們沒有必要和在映像內部使用的私有名稱相同。 |
當其他的映像文件利用名稱導入一個符號,Win32裝載器在Name pointer table中尋找與之符合的字符。如果找到相符的字符,序號被用來在Ordinal table中查找相應的成員(就是說,兩個表有相同的索引)。找到的序號是指向Export address table的一個索引,指向的成員給出了期望的符號的真實位置。
如果其他的映像文件利用序號導入一個符號,就不需要在Name pointer table中來查找符合的字符。直接使用序號效率會更高。
? Export directory table
在winnt.h中,Export directory table被定義爲如下結構:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
Characteristics:保留的,必須爲零。
TimeDateStamp:輸出數據被建立的時間。
MajorVersion:主版本數字,可以被用戶設定。
MinorVersion:次版本數字,可以被用戶設定。
Name:包含此DLL名稱的ASCII字符串的地址,與鏡像基地址相關。
Base:輸出符號的起始數字,通常是1。
NumberOfFunctions:Export address table中的入口個數。
NumberOfNames:Name pointer table中的名稱個數。
AddressOfFunctions:Export address table的地址,與鏡像基地址相關。
AddressOfNames:Name pointer table的地址,與鏡像基地址相關。
AddressOfNameOrdinals:Export name table的地址,與鏡像基地址相關。
?
表間圖示
II. 輸入符號節
所有需要導入符號的影響文件,都需要一個稱之爲.idata的節。導入信息的典型結構如下圖所示:
Directory Table |
Null Directory Entry |
DLL1 Import Lookup Table |
Null |
DLL2 Import Lookup Table |
Null |
DLL3 Import Lookup Table |
Null |
Hint-Name Table |
? Import Directory Table
導入表由一系列的IMAGE_IMPORT_DESCRIPTOR結構組成,結構的數量取決於程序要使用的DLL文件的數量,每個結構對應一個DLL文件,例如,如果一個PE文件從10個不同的DLL文件中引入了函數,那麼就存在10個IMAGE_IMPORT_DESCRIPTOR結構來描述這些DLL文件,在所有這些結構的最後,由一個內容全爲0的IMAGE_IMPORT_DESCRIPTOR結構作爲結束。
IMAGE_IMPORT_DESCRIPTOR結構的定義如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
OriginalFirstThunk:導入查詢表(import lookup table)的RVA。Characteristics不使用。
TimeDateStamp:在映像被綁定前,這個值是零,在映像被綁定後,這個值是DLL被創建的時間。
ForwarderChain:第一個轉發引用的索引。
Name:這是一個RVA,指向一個以 null 爲結束字符的 ASCII 字符串,內含imported DLL 的名稱。
FirstThunk:導入地址表(import address table)的RVA。
? Import Lookup Table
一個Import Lookup Table實際上就是一個雙字,在winnt.h中定於如下:
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
當雙字(就是指結構!)的最高位爲1時,表示函數是以序號的方式導入的,這時雙字的低位就是函數的序號。可以用預定義值IMAGE_ORDINAL_FLAG32(或80000000h)來對最高位進行測試,當雙字的最高位爲0時,表示函數以字符串類型的函數名方式導入,這時雙字的值是一個RVA,指向Hint-Name Table。
? Hint/Name Table
在winnt.h中,Hint/Name Table被定義爲如下格式:
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Hint:表示函數的序號,不過這個字段是可選的,有些編譯器總是將它設置爲0。
Name:Name1字段定義了導入函數的名稱字符串,這是一個以0爲結尾的字符串。
? 表間圖示
圖中示意了可執行文件導入了Kernel32.dll中的ExitProcess,ReadFile,WriteFile和lstrcmp函數的情況,其中,前面3個函數按照名稱方式導入,最後的lstrcmp函數按照序號導入,這4個函數的序號分別是
PE文件中的導入表
當PE文件被裝入內存後,內存中的映像就被Windows裝載器修正成了下圖所示的樣子,其中由FirstThunk字段指向的那個數組中的每個雙字都被替換成了真正的函數入口地址,之所以在PE文件中使用兩份IMAGE_THUNK_DATA數組的拷貝並修改其中的一份,是爲了最後還可以留下一份拷貝用來反過來查詢地址所對應的導入函數名:
內存中的導入表
III. 重定位節
當鏈接器產生一個可執行文件,它假設這個文件會被加載內存的某處,並且把code 和 data的相關假設地址都寫入 EXE 文件中。在Optional header中的ImageBase成員,記錄了這個文件期望的加載地址,如果windows加載起沒有把PE文件加載的它期望的地址上,則需要對code 和 data的相關假設地址都加以修正。這就是重定位的作用。
重定位節可以分爲多個重定位塊,每個重定位塊描述映像中的4k(1000h)的重定位信息。每個重定位塊都以一個相同的結構開始,在winnt.h中定義如下:
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
VirtualAddress:這是一個頁面RVA。
SizeOfBlock:重定位塊的總大小。
緊接着這個結構,就是一系列重定位的信息了,一個重定位信息包存在一個WORD值中,這個前4位保存這個重定位信息的性質,後12位則是一個以上面VirtualAddress爲基準的偏移。其中前四位的定義如下:
|
值 |
描述 |
IMAGE_REL_BASED_ABSOLUTE |
0 |
可以跳過這個重定位塊,僅僅是用來對齊。 |
IMAGE_REL_BASED_HIGH |
1 |
32位的高16位需要修正。 |
IMAGE_REL_BASED_LOW |
2 |
32位的低16位需要修正。 |
IMAGE_REL_BASED_HIGHLOW |
3 |
整個32位需要被修正。 |
IMAGE_REL_BASED_HIGHADJ |
4 |
16位的區域是一個32位WORD值的高位。這個32位WORD值的低16位跟在這個重定位的後面。 |
IMAGE_REL_BASED_MIPS_JMPADDR |
5 |
重定位用於MIPS跳轉指令。 |
|
6 |
保留的,必須是零。 |
|
7 |
保留的,必須是零。 |
IMAGE_REL_BASED_MIPS_JMPADDR16 |
9 |
重定位用於MIPS16跳轉指令。 |
下面的例子舉出了一個重定位表的實際情況,假設模塊被裝入00400000h處:
重定位表偏移 數據 說明
0000h 00001000h 第一個塊:頁面起始地址是00401000h
0004h 00000010h 重定位塊長度是10h
0008h 3012h 16位重定位項,重定位位置:00401012h
000ah 3040h 16位重定位項,重定位位置:00401040h
000ch 306fh 16位重定位項,重定位位置:0040106fh
000eh 0000h 用於對齊的空白數據
0010h 00002000h 第二個塊:頁面起始地址是00402000h
0014h 0000000Ch 重定位塊長度是0Ch
0018h 3080h 16位重定位項,重定位位置:00402080h
001ah
001ch 00000000h 重定位