PE文件是Windows操作系统下使用的可执行文件的格式,可执行系列:EXE、SCR,库系列:DLL、OCX、CPL、DRV,驱动程序:SYS、VXD 等都是PE文件
在Windows系统中,PE文件被系统加载器映射到内存中,每一个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址(Virtual Address\VA)
相对虚拟地址(Relative Virtual Address\RVA)是一个简单的,相对于PE文件载入地址的偏移位置,它是一个相对的地址(偏移)
当PE文件在磁盘中时,某个数据位置相对于文件头的偏移量称为文件偏移地址(File Offset)
所有PE文件以64字节DOS头开始,DOS头只是为了兼容早期操作系统
e_magic:0x5A4D,MZ标志
e_lfanew:0x000000E8,NT头偏移量
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
这里采用的小端序存储数据,地址高位存储数据的高位,地址低位存储数据的低位,是一种逆序存储方式
000000E8就是NT头的位置,与DOS头之间隔了一段DosStub数据,这个数据是可变的,所以需要e_lfanew来指定NT头的位置
NT头定义如下
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; // PE标识 IMAGE_FILE_HEADER FileHeader; //文件头 IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选头} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;Signature类型为DWORD,占用4个字节#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
文件头是表示文件大致属性的IMAGE_FILE_HEADER结构体
typedef struct _IMAGE_FILE_HEADER { WORD Machine; // 2个字节,指明支持的CPU类型 WORD NumberOfSections; // 2个字节,指明节区数量 DWORD TimeDateStamp; // 4字节,编译器生成这个PE文件的时间 DWORD PointerToSymbolTable; // 4字节,调试符号相关,不作研究 DWORD NumberOfSymbols; // 4字节,调试符号相关,不作研究 WORD SizeOfOptionalHeader; // 2个字节,指明可选头 IMAGE_OPTIONAL_HEADER32 大小 WORD Characteristics; // 2个字节,表明文件属性,如DLL文件,SYS文件等} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;IMAGE_OPTIONAL_HEADER32是PE头结构体中最大的,定义如下typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; // 普通可执行文件为0x010B,PE32 (64位)值为0x020B BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; // 代码区块的大小,通常是.text区块大小 DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; // 程序入口点,RVA值 DWORD BaseOfCode; // 代码段的起始地址,RVA值,通常是.text区块的起始RVA DWORD BaseOfData; // 数据段的起始地址,RVA值,通常是.data区块的起始RVA // // NT additional fields. // DWORD ImageBase; // PE文件在内存中首选的装载基地址 DWORD SectionAlignment; // PE文件装载到内存时区块的对齐大小,假设.text区块的大小为0x7748,而SectionAlignment的大小为0x1000,那么对齐后的大小为0x8000字节 DWORD FileAlignment; // 磁盘上PE文件中区块的对齐大小,对齐方式类似SectionAlignment WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; // PE文件被装载到内存空间后总的大小,指从ImageBase到最后一个区块的大小 DWORD SizeOfHeaders; // Dos头、DosStub、PE头以及区块头的总大小,并进行FileAlignment对齐后的大小 DWORD CheckSum; // 校验和,一般的EXE文件通常为0,判断文件是否被修改 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;
写一个程序,关掉随机基址
AddressOfEntryPoint是一个RVA值,所以当程序实际被加载到内存时,对应的入口点地址的虚拟地址
(VA) = ImageBase+AddressOfEntryPoint = 0x00400000+0x000012DA = 0x004012DA
子系统查看
SizeOfImage验证
DWORD SizeOfImage;
// PE文件被装载到内存空间后总的大小,指从ImageBase到最后一个区块的大小
OPTIONAL_HEADER末尾是一个数据目录表数组,数组元素个数为16
各元素对应表项:
IMAGE_DATA_DIRECTORY定义如下:
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; // 数据块的起始RVA地址 DWORD Size; // 数据块的长度} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;