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 14C 。)
标志 |
值 |
说明 |
IMAGE_FILE_MACHINE_UNKNOWN |
0x0 |
假定在所有机器上运行 |
IMAGE_FILE_MACHINE_AM33 |
0x1d3 |
Matsushita AM33 |
IMAGE_FILE_MACHINE_AMD64 |
0x8664 |
x64 |
IMAGE_FILE_MACHINE_ARM |
0x 1c 0 |
ARM little endian |
IMAGE_FILE_MACHINE_EBC |
0xebc |
EFI byte code |
IMAGE_FILE_MACHINE_I386 |
0x 14c |
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 1f 0 |
Power PC little endian |
IMAGE_FILE_MACHINE_POWERPCFP |
0x 1f 1 |
有浮点支持的Power PC |
IMAGE_FILE_MACHINE_R4000 |
0x166 |
MIPS little endian |
IMAGE_FILE_MACHINE_SH3 |
0x 1a 2 |
Hitachi SH3 |
IMAGE_FILE_MACHINE_SH3DSP |
0x 1a 3 |
Hitachi SH3 DSP |
IMAGE_FILE_MACHINE_SH4 |
0x 1a 6 |
Hitachi SH4 |
IMAGE_FILE_MACHINE_SH5 |
0x 1a 8 |
Hitachi SH5 |
IMAGE_FILE_MACHINE_THUMB |
0x 1c 2 |
Thumb |
IMAGE_FILE_MACHINE_WCEMIPSV2 |
0x169 |
MIPS little-endian WCE v2 |
NumberOfSections:表示PE文件中节的数量。
TimeDateStamp:链接器产生这个文件的时间,是自从 1969 年 12 月 3 1 日4:00 P.M. 之后的总秒数。
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 |
程序可以处理大于 2G 的地址。 |
|
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文件头二)
[ 2008/4/1 9:05 :00| By:perfecter]
① 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声明的相一致。几个比较常见的节是:输出符号节、输入符号节、数据节、重定位节和代码节。比较复杂的是输出符号节、输入符号节和重定位节:
I. 输出符号节
输出符号节,包含一些可以供其他映像文件使用的符号信息。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个函数的序号分别是 02f 6h,0111h,002bh和0010h:
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 30f 0h 16位重定位项,重定位位置: 004020f 0h
001ch 00000000h 重定位