PE文件介绍

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

PE.jpg

基地址

当PE文件通过Windows加载器载入到内存之后,内存中的版本称为模块,映射文件的起始地址称为基地址

.jpg

虚拟地址

在Windows系统中,PE文件被系统加载器映射到内存中。每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址( Virtual Address,VA )。

相对虚拟地址

假设一个exe文件从400000h处载入,它的代码区块开始于401000h处,那么代码块的相对虚拟地址(Relative Virtual Address,RVA)计算方法如下:
目标地址401000h -载入地址400000h = RVA 1000h
将一个RVA转化成真实地址:
虚拟地址( VA)=基地址( ImageBase )+相对虚拟地址(RVA )

MS-DOS头部

每个PE文件都是以一个DOS程序开始的。DOS头中的重点就是e_magic(MZ标识)和e_lfanew(指出真正PE头文件的偏移位置)

struct _IMAGE_DOS_HEADER { 
    0x00 WORD e_magic; //Dos可执行文件标记“Mz"
    0x02 WORD e_cblp; 
    0x04 WORD e_cp; 
    0x06 WORD e_crlc; 
    0x08 WORD e_cparhdr; 
    0x0a WORD e_minalloc; 
    0x0c WORD e_maxalloc; 
    0x0e WORD e_ss; 
    0x10 WORD e_sp; 
    0x12 WORD e_csum; 
    0x14 WORD e_ip; //Dos代码入口IP
    0x16 WORD e_cs; //DOs代码入口cs
    0x18 WORD e_lfarlc; 
    0x1a WORD e_ovno; 
    0x1c WORD e_res[4]; 
    0x24 WORD e_oemid; 
    0x26 WORD e_oeminfo; 
    0x28 WORD e_res2[10]; 
    0x3c DWORD e_lfanew; //指向PE文件头"PE",0 , 0
};


4.jpg

PE文件头

紧接着dos头的是PE文件头(PE Header),它是PE相关结构NT映像头(IMAGE_NT_HEADERS)的简称,PE装载器将从IMAGE_DOS_HEADER 结构的e_lfanew字段里找到PE Header的起始偏移量,用其加上基址,得到PE文件头的指针。

  PNTHeader = ImageBase + dosHeader -> e_lfanew

struct _IMAGE_NT_HEADERS { 
    0x00 DWORD Signature; //PE文件标识
    0x04 _IMAGE_FILE_HEADER FileHeader; 
    0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader; 
};

在一个有效的PE文件里,Signature字段被设置为0x00004550,ASCII码字符是“PEOO”,

IMAGE_FILE_HEADER结构

struct _IMAGE_FILE_HEADER { 
    0x00 WORD Machine; //运行平台,可执行文件的目标CPU类型
    0x02 WORD NumberOfSections; //文件区块的数目,块表在NT_Header后面
    0x04 DWORD TimeDateStamp; //文件创建的时间
    0x08 DWORD PointerToSymbolTable; //指向符号表(用于调试)
    0x0c DWORD NumberOfSymbols; //符号表中的符号个数(用于调试)
    0x10 WORD SizeOfOptionalHeader; IMAGE_OPTIONAL_HEADER32结构的大小。
    0x12 WORD Characteristics; //文件属性
};


3.jpg
5.jpg

IMAGE_OPTIONAL_HEAADER

虽然名字是可选PE头,但并不意味它不重要,相反还非常重要!

struct _IMAGE_OPTIONAL_HEADER { 
    0x00 WORD Magic; //标志字
    0x02 BYTE MajorLinkerVersion; //链接器主版本号
    0x03 BYTE MinorLinkerVersion; //链接器次版本号
    0x04 DWORD SizeOfCode; //所有含有代码的区块的大小
    0x08 DWORD SizeOfInitializedData; //所有初始化数据区块的大小
    0x0c DWORD SizeOfUninitializedData; //所有未初始化数据区块的大小
    0x10 DWORD AddressOfEntryPoint; //**程序执行人口 RVA**
    0x14 DWORD BaseOfCode; //代码区块起始RVA
    0x18 DWORD BaseOfData; //数据区块起始RVA
    0x1c DWORD ImageBase; //**程序默认载入基地址**
    0x20 DWORD SectionAlignment; //内存中区块的对齐值
    0x24 DWORD FileAlignment; //文件中区块的对齐值
    0x28 WORD MajorOperatingSystemVersion; 
    0x2a WORD MinorOperatingSystemVersion; 
    0x2c WORD MajorImageVersion; 
    0x2e WORD MinorImageVersion; 
    0x30 WORD MajorSubsystemVersion; 
    0x32 WORD MinorSubsystemVersion; 
    0x34 DWORD Win32VersionValue; 
    0x38 DWORD SizeOfImage; //映像载入内存后的总尺寸
    0x3c DWORD SizeOfHeaders; //MS-DOS头部、PE文件头、区块表总大小
    0x40 DWORD CheckSum; 
    0x44 WORD Subsystem; 
    0x46 WORD DllCharacteristics; 
    0x48 DWORD SizeOfStackReserve; 
    0x4c DWORD SizeOfStackCommit; 
    0x50 DWORD SizeOfHeapReserve; 
    0x54 DWORD SizeOfHeapCommit; 
    0x58 DWORD LoaderFlags; 
    0x5c DWORD NumberOfRvaAndSizes; 
    0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16]; 
};


6.jpg

参考书籍:《加密与解密四》
最后修改:2021 年 04 月 17 日
如果觉得我的文章对你有用,请随意赞赏