PE文件是Windows操作系统下使用的可执行文件的格式,可执行系列:EXE、SCR,库系列:DLL、OCX、CPL、DRV,驱动程序:SYS、VXD 等都是PE文件
在Windows系统中,PE文件被系统加载器映射到内存中,每一个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址(Virtual AddressVA)
相对虚拟地址(Relative Virtual AddressRVA)是一个简单的,相对于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个字节
文件头是表示文件大致属性的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;
本文始发于微信公众号(黑白之道):PE文件格式(文件头)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论