PE文件基础

PE文件结构

前置知识

VA、RVA、RWA

虚拟地址(Virtual Address,VA):装入内存中的实际地址。

相对虚拟地址(Relative Virtual Address,RVA):在内存中相对于PE文件装入地址的偏移位置,是一个相对地址。

文件偏移(Fill Offset):PE文件存储在磁盘上时,相对于文件头的偏移位置。16进制文件编辑器打开后的地址为文件偏移地址。

内存偏移 - 该段起始的RVA(VirtualAddress) = 文件偏移 - 该段的PointerToRawData
内存偏移 = 该段起始的RVA(VirtualAddress) + (文件偏移 - 该段的PointerToRawData)
文件偏移 = 该段的PointerToRawData + (内存偏移 - 该段起始的RVA(VirtualAddress))

整体结构

PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。

从起始位置开始依次是DOS头,NT头,节表以及具体的节。

结构如下:

DOS头:{
		DOS头
		DOS程序
		}
NT头:{
		PE签名
	    PE文件头
		PE可选头
		}
节表
.idata节
.text节
.data节

节可以有很多,不一定是这三种。

DOS头

DOS头的定义(C语言):

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

一般有用的只有第一个和最后一个,

e_magic:WORD型,一般为0x4D5A(MZ),PE文件必须都是’MZ’开头。

e_lfanew:LONG型,用来表示DOS头之后的NT头相对文件起始地址的偏移。

DOS程序

紧跟着E_lfanew的是一个MS-DOS程序,在DOS中能够运行的程序,在windows中不能运行,如果为了节省文件空间大小,也可以直接省略

NT头

NT头的定义(C语言):

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature; 						//PE文件头标志 => 4字节
    IMAGE_FILE_HEADER FileHeader; 			//标准PE头 => 20字节
    IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选PE头 => 32位下224字节(0xE0) 64位下240字节(0xF0)
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

PE签名(Signature)

Signature:DWORD型,类似于DOS头中的e_magic,其高16位是0,低16是0x4550,用字符表示是’PE‘。

PE文件头(IMAGE_FILE_HEADER)

PE文件头的定义(C语言):
typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine; 				//可以运行在什么平台上 任意:0 ,Intel 386以及后续:14C x64:8664
    WORD    NumberOfSections; 		//节的数量
    DWORD   TimeDateStamp; 			//编译器填写的时间戳
    DWORD   PointerToSymbolTable;   //调试相关
    DWORD   NumberOfSymbols; 		//调试相关
    WORD    SizeOfOptionalHeader;   //标识扩展PE头大小
    WORD    Characteristics;        //文件属性 => 16进制转换为2进制根据哪些位有1,可以查看相关属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

结构解释:
Machine
该文件的运行平台,是x86、x64还是I64等等,可以是下面值里的某一个。
#defineIMAGE_FILE_MACHINE_UNKNOWN      0
#defineIMAGE_FILE_MACHINE_I386         0x014c  // Intel 386.
#defineIMAGE_FILE_MACHINE_R3000        0x0162  // MIPS little-endian, 0x160big-endian
#defineIMAGE_FILE_MACHINE_R4000        0x0166  // MIPS little-endian
#defineIMAGE_FILE_MACHINE_R10000       0x0168  // MIPS little-endian
#defineIMAGE_FILE_MACHINE_WCEMIPSV2   0x0169  // MIPS little-endian WCE v2
#defineIMAGE_FILE_MACHINE_ALPHA         0x0184  // Alpha_AXP
#defineIMAGE_FILE_MACHINE_SH3          0x01a2  // SH3 little-endian
#defineIMAGE_FILE_MACHINE_SH3DSP       0x01a3
#defineIMAGE_FILE_MACHINE_SH3E        0x01a4  // SH3E little-endian
#defineIMAGE_FILE_MACHINE_SH4         0x01a6  // SH4 little-endian
#defineIMAGE_FILE_MACHINE_SH5         0x01a8  // SH5
#defineIMAGE_FILE_MACHINE_ARM         0x01c0  // ARM Little-Endian
#defineIMAGE_FILE_MACHINE_THUMB        0x01c2
#defineIMAGE_FILE_MACHINE_AM33         0x01d3
#defineIMAGE_FILE_MACHINE_POWERPC    0x01F0  // IBM PowerPC Little-Endian
#defineIMAGE_FILE_MACHINE_POWERPCFP   0x01f1
#defineIMAGE_FILE_MACHINE_IA64        0x0200  // Intel 64
#defineIMAGE_FILE_MACHINE_MIPS16      0x0266  // MIPS
#defineIMAGE_FILE_MACHINE_ALPHA64     0x0284 //ALPHA64
#defineIMAGE_FILE_MACHINE_MIPSFPU     0x0366  // MIPS
#defineIMAGE_FILE_MACHINE_MIPSFPU16  0x0466  // MIPS
#defineIMAGE_FILE_MACHINE_AXP64       IMAGE_FILE_MACHINE_ALPHA64
#defineIMAGE_FILE_MACHINE_TRICORE    0x0520  // Infineon
#defineIMAGE_FILE_MACHINE_CEF          0x0CEF
#defineIMAGE_FILE_MACHINE_EBC          0x0EBC  // EFI Byte Code
#defineIMAGE_FILE_MACHINE_AMD64       0x8664  // AMD64 (K8)
#defineIMAGE_FILE_MACHINE_M32R        0x9041  // M32R little-endian
#defineIMAGE_FILE_MACHINE_CEE          0xC0EE

NumberOfSections
该PE文件中有多少个节,也就是节表中的项数。

TimeDateStamp
PE文件的创建时间,一般有连接器填写。表明文件是何时被创建的。这个值是自1970年1月1日以来用格林威治时间(GMT)计算的秒数,这个值是比文件系统(FILESYSTEM)的日期时间更加精确的指示器。

PointerToSymbolTable
COFF文件符号表在文件中的偏移,主要指向调式信息

NumberOfSymbols
符号表的数量。

SizeOfOptionalHeader
紧随其后的可选头的大小,对于32位系统,通常为0X00E0H,64位系统为0X00F0H。

Characteristics
可执行文件的属性,可以是下面这些值按位相或,定义在winnt.h头文件中。

关于Characteristics各位代表的意义:

Bit 0 :表示重定位信息已从文件中删除。该文件必须以其首选的基地址加载。如果基址不可用,则加载程序报告错误。
Bit 1 :表示该文件是可执行文件,是合法的。
Bit 2 ;COFF行号已从文件中删除。
Bit 3 :COFF符号条目已从文件中删除。
Bit 4 :保留
Bit 5 :表示应用程序可以处理大于2GB的地址
Bit 6-7 :保留
Bit 8 :表示机器基于32位体系结构。
Bit 9 :表示没有调试信息
Bit 10:表示该程序不能运行于可移动介质中(如软驱或CD-ROM)。
Bit 11:表示程序不能在网络介质运行。
Bit 12:表示文件是一个系统文件例如驱动程序。
Bit 13:表示文件是一个动态链接库(DLL)。
Bit 14:表示文件只能在单处理机器上运行。
Bit 15:保留
注:bit代表权值,bit0为最低位,bit15为最高位

PE可选头(IMAGE_OPTIONAL_HEADER)

PE可选头的定义(C语言):
typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.					必选的
    //

    WORD    Magic;						//PE32: 010B PE64: 020B
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;					//所有含有代码的区块的大小 编译器填入 没用(可改)
    DWORD   SizeOfInitializedData;		//所有初始化数据区块的大小 编译器填入 没用(可改)
    DWORD   SizeOfUninitializedData;	//所有含未初始化数据区块的大小 编译器填入 没用(可改)
    DWORD   AddressOfEntryPoint;		//程序入口RVA
    DWORD   BaseOfCode;					//代码区块起始RVA
    DWORD   BaseOfData;					//数据区块起始RVA

    //
    // NT additional fields.			可选的
    //

    DWORD   ImageBase;						//内存镜像基址(程序默认载入基地址)
    DWORD   SectionAlignment; 				//内存中对齐大小
    DWORD   FileAlignment; 					//文件中对齐大小(提高程序运行效率)
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;					//内存中整个PE文件的映射的尺寸,可比实际值大,必须是SectionAlignment的整数倍
    DWORD   SizeOfHeaders; 					//所有的头加上节表文件对齐之后的值
    DWORD   CheckSum;						//映像校验和,一些系统.dll文件有要求,判断是否被修改
    WORD    Subsystem;						
    WORD    DllCharacteristics;				//文件特性,不是针对DLL文件的,16进制转换2进制可以根据属性对应的表格得到相应的属性
    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
表示可选头的类型。

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC    0x10b  // 32位PE可选头
#defineIMAGE_NT_OPTIONAL_HDR64_MAGIC     0x20b  // 64位PE可选头
#defineIMAGE_ROM_OPTIONAL_HDR_MAGIC      0x107  

MajorLinkerVersion
链接器的版本号
MinorLinkerVersion
链接器的版本号
SizeOfCode
代码段的长度,如果有多个代码段,则是代码段长度的总和。
SizeOfInitializedData
初始化的数据长度。
SizeOfUninitializedData
未初始化的数据长度。
AddressOfEntryPoint
程序入口的RVA,对于exe可以理解为WinMain的RVA。对于DLL可以理解为DllMain的RVA,对于驱动程序,可以理解为DriverEntry的RVA。当然,实际上入口点并非是WinMain,DllMain和DriverEntry,在这些函数之前还有一系列初始化要完成。
BaseOfCode
代码段起始地址的RVA。
BaseOfData
数据段起始地址的RVA。
可选字段部分ImageBase
SectionAlignment
节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0。
FileAlignment
节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignment。
MajorOperatingSystemVersion
所需操作系统的版本号,随着操作系统版本越来越多,这个好像不是那么重要了。
MinorOperatingSystemVersion
所需操作系统的版本号,随着操作系统版本越来越多,这个好像不是那么重要了。
MajorImageVersion
映象的版本号,这个是开发者自己指定的,由连接器填写。
MinorImageVersion
映象的版本号,这个是开发者自己指定的,由连接器填写。
MajorSubsystemVersion
所需子系统版本号。
MinorSubsystemVersion
所需子系统版本号。
Win32VersionValue
保留,必须为0。
SizeOfImage
映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。
SizeOfHeaders
所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。
CheckSum
映象文件的校验和。
Subsystem
运行该PE文件所需的子系统,可以是下面定义中的某一个:
#defineIMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE        0x0040     // DLL can move.
#defineIMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY   0x0080     // Code Integrity Image
#defineIMAGE_DLLCHARACTERISTICS_NX_COMPAT            0x0100     // Image is NX compatible
#defineIMAGE_DLLCHARACTERISTICS_NO_ISOLATION        0x0200     // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH               0x0400     // Image does not use SEH.  No SE handler may reside in this image
#defineIMAGE_DLLCHARACTERISTICS_NO_BIND              0x0800     // Do not bind this image.//                                           0x1000     // Reserved.
#defineIMAGE_DLLCHARACTERISTICS_WDM_DRIVER           0x2000    //Driver uses WDM model//                                           0x4000     // Reserved.
#defineIMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     0x8000

SizeOfStackReserve
运行时为每个线程栈保留内存的大小。
SizeOfStackCommit
运行时每个线程栈初始占用内存大小。
SizeOfHeapReserve
运行时为进程堆保留内存大小。
SizeOfHeapCommit
运行时进程堆初始占用内存大小。
LoaderFlags
保留,必须为0。
NumberOfRvaAndSizes
数据目录的项数,即下面这个数组的项数。
DataDirectory
数据目录,这是一个数组,数组的项定义如下:
typedefstruct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;VirtualAddress

节表(IMAGE_SECTION_HEADER)

节表的定义(C语言):
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; //ASCII字符串 可自定义 只截取8个字节
    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个ASCII码组成,用来定义区块的名称的数组。多数区块名都习惯性以一个“.”作为开头(例如:.text),这个“.”实际上是不是必须的。值得我们注意的是,如果区块名达到8 个字节,后面就没有0字符了。前边带有一个“$” 的区块名字会从连接器那里得到特殊的待遇,前边带有“$”的相同名字的区块在载入时候将会被合并,在合并之后的区块中,他们是按照“$”后边的字符的字母顺序进行合并的。每个区块的名称都是唯一的,不能有同名的两个区块。但事实上节的名称不代表任何含义,他的存在仅仅是为了正规统一编程的时候方便程序员查看方便而设置的一个标记而已。所以将包含代码的区块命名为“.Data”或者说将包含数据的区块命名为“.Code”都是合法的。当我们要从PE 文件中读取需要的区块时候,不能以区块的名称作为定位的标准和依据,正确的方法是按照IMAGE_OPTIONAL_HEADER32 结构中的数据目录字段结合进行定位。
VirtualSize
对表对应的区块的大小,这是区块的数据在没有进行对齐处理前的实际大小。
VirtualAddress
该区块装载到内存中的RVA地址。这个地址是按照内存页来对齐的,因此它的数值总是SectionAlignment的值的整数倍。
PointerToRawData
指出节在磁盘文件中所处的位置。这个数值是从文件头开始算起的偏移量。
SizeOfRawData
该区块在磁盘中所占的大小,这个数值等于VirtualSize字段的值按照FileAlignment的值对齐以后的大小。
Characteristics
该区块的属性。该字段是按位来指出区块的属性(如代码/数据/可读/可写等)的标志。

Characteristics的各二进制位的意义如下:

Bit 0-4 :保留
Bit 5 :表示包含可执行代码。
Bit 6 :表示包含初始化数据。
Bit 7 :表示包含未初始化数据,即程序执行前初始化未0。
Bit 8 :保留
Bit 9 :表示包含注释或其他信息。这仅对目标文件有效。
Bit 10 :保留
Bit 11 :表示该节在可执行文件链接后,作为文件一部分的数据被清除。
Bit 12 :表示包含公共块数据,这仅对目标文件有效。
Bit 13 :保留
Bit 14 :重置本部分TLB条目中的推测性异常处理位。
Bit 15 :表示本节包含全局指针引用的数据,需要在执行前侵入。
Bit 16-19 :保留
Bits 20-23 :指定对齐。一般是库文件的对象对齐。
Bit 24 :表示节包含扩展的重定位。
Bit 25 :表示该节可以根据需要丢弃。
Bit 26 :表示节的数据不得缓存。
Bit 27 :表示节的数据不得交换出去。
Bit 28 :表示节的数据在所有映象例程内共享
Bit 29 :表示进程得到“执行”访问节内存。
Bit 30 :表示进程得到“读出”访问节内存。
Bit 31 :表示进程得到“写入”访问节内存。

节(块)

每个节实际上是一个容器,可以包含代码、数据等等,每个节可以有独立的内存权限,比如代码节默认有读/执行权限,节的名字和数量可以自己定义。

通常,区块中的数据在逻辑上是关联的。PE 文件一般至少都会有两个区块:一个是代码块,另一个是数据块。每一个区块都需要有一个截然不同的名字,这个名字主要是用来表达区块的用途。例如有一个区块叫.rdata,表明他是一个只读区块。注意:区块在映像中是按起始地址(RVA)来排列的,而不是按字母表顺序。

另外,使用区块名字只是人们为了认识和编程的方便,而对操作系统来说这些是无关紧要的。微软给这些区块取了个有特色的名字,但这不是必须的。当编程从PE 文件中读取需要的内容时,如输入表、输出表,不能以区块名字作为参考,正确的方法是按照数据目录表中的字段来进行定位。

下列是一些常见的节名:

节名内容
.bss未初始化的数据
.data代码节
.edata导出表
.idata导入表
.idlsym包含已注册的SEH,它们用以支持IDL属性
.pdata异常信息
.rdata只读的已初始化数据(用于常量)
.reloc重定位信息
.rsrc资源目录
.sbss与GP相关的未初始化数据
.sdata与GP相关的已初始化数据
.srdata与GP相关的只读数据
.text默认代码节

导入表和导出表

导入表和导出表统一使用以下结构来寻找:
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
导入表的定义(C语言):
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA 指向 INT (PIMAGE_THUNK_DATA结构数组)
    } 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;							//RVA指向dll名字,以0结尾
    DWORD   FirstThunk;                     // RVA 指向 IAT (PIMAGE_THUNK_DATA结构数组)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
结构解释:
OriginalFirstThunk
指向一个IMAGE_THUNK_DATA数组叫做输入名称表Import Name Table(INT),用来保存函数。
TimeDateStamp
该字段可以忽略。如果那里有绑定的话它包含时间/数据戳(time/data stamp)。如果它是0,就没有绑定在被导入的DLL中发生。在最近,它被设置为0xFFFFFFFF以表示绑定发生。
ForwarderChain
一般情况下我们也可以忽略该字段。在老版的绑定中,它引用API的第一个forwarder chain(传递器链表)。它可被设置为0xFFFFFFFF以代表没有forwarder。
Name
DLL名字的指针, 指向一个用NULL作为结束符的ASCII字符串的一个RVA,该字符串是该导入DLL文件的名称,如:KERNEL32.DLL
FirstThunk
它也指向IMAGE_THUNK_DATA数组叫做输入地址表Import Address Table(IAT)。

导入表还需要使用另外两种结构体:_IMAGE_THUNK_DATA64和**_IMAGE_IMPORT_BY_NAME**

typedef struct _IMAGE_THUNK_DATA64 {
    union {
        ULONGLONG ForwarderString;  // 指向一个转向者字符串的RVA
        ULONGLONG Function;         // 被输入的函数的内存地址
        ULONGLONG Ordinal;			// 被输入API的序数值
        ULONGLONG AddressOfData;    // 指针指向 IMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA64;
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint; //可能为空,编译器决定,如果不为空,是函数在导出表的索引
    BYTE    Name[1]; //函数名称,以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
结构解释:
当IMAGE_THUNK_DATA 的值最高位为1时,表示函数是以序号方式输入,这时低31为被当作函数序号。当最高位是0时,表示函数是以字符串类型的函数名方式输入的,这时,IMAGE_THUNK_DATA 的值为指向IMAGE_IMPORT_BY_NAME 的结构的RVA。
typedef struct_IMAGE_IMPORT_BY_NAME {     
WORD Hint;   
BYTE Name[1];  
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;

Hint
表示这个函数在其所驻留DLL的输出表的序号,不是必须的。

Name
表示 函数名,是一个ASCII字符串以0结尾,大小不固定。
导出表的定义(C语言):
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;					// 指针指向该导出表文件名字符串
    DWORD   Base;					// 导出函数起始序号
    DWORD   NumberOfFunctions;		// 所有导出函数的个数
    DWORD   NumberOfNames;			// 以函数名字导出的函数个数
    DWORD   AddressOfFunctions;     // 指针指向导出函数地址表RVA
    DWORD   AddressOfNames;         // 指针指向导出函数名称表RVA
    DWORD   AddressOfNameOrdinals;  // 指针指向导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
结构解释:
Characteristics
现在没有用到,一般为0。
TimeDateStamp
导出表生成的时间戳,由连接器生成。
MajorVersion,MinorVersion
看名字是版本,实际貌似没有用,都是0。
Name
模块的名字。
Base
序号的基数,按序号导出函数的序号值从Base开始递增。
NumberOfFunctions
所有导出函数的数量。
NumberOfNames
按名字导出函数的数量。
AddressOfFunctions
一个RVA,指向一个DWORD数组,数组中的每一项是一个导出函数的RVA,顺序与导出序号相同。
AddressOfNames
一个RVA,依然指向一个DWORD数组,数组中的每一项仍然是一个RVA,指向一个表示函数名字。
AddressOfNameOrdinals
一个RVA,还是指向一个WORD数组,数组中的每一项与AddressOfNames中的每一项对应,表示该名字的函数在AddressOfFunctions中的序号。

导出表里面最后还有三个表,这三个表可以让我们找到函数真正的地址,在编写PE格式解析器的时候可以用到,AddressOfFunctions 是函数地址表,指向每个函数真正的地址,AddressOfNames 和 AddressOfNameOrdinals 分别是函数名称表和函数序号表,我们知道DLL文件有两种调用方式,一种是用名字,一种是用序号,通过这两个表可以用来寻找函数在 AddressOfFunctions 表中真正的地址。

重定向表

重定向表的定义(C语言):
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress; // 重定位数据的开始 RVA 地址
    DWORD   SizeOfBlock;	// 重定位块的长度
//  WORD    TypeOffset[1];	// 重定位项数组
} IMAGE_BASE_RELOCATION;
结构解释:
VirtualAddress
是这一组重定位数据的开始RVA地址.各重定位项的地址加上这个值才是该重定位项完整的RVA地址.
SizeOfBlock
是重定位结构的大小
TypeOffset
是一个数组.数组每项大小为两个字节,共16位.它又分为高4位和低12位,高4位代表重定位类型;低12位是重定位地址,它与VirtualAddress相加即是指向PE映像中需要修改的地址数据的指针.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值