PE文件介绍
PE文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。
基地址
当PE文件通过Windows加载器载入到内存之后,内存中的版本称为模块,映射文件的起始地址称为基地址
虚拟地址
在Windows系统中,PE文件被系统加载器映射到内存中。每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址( Virtual Address,VA )。
相对虚拟地址
假设一个exe文件从400000h处载入,它的代码区块开始于401000h处,那么代码块的相对虚拟地址(Relative Virtual Address,RVA)计算方法如下:
目标地址401000h -载入地址400000h = RVA 1000h
将一个RVA转化成真实地址:
虚拟地址( VA)=基地址( ImageBase )+相对虚拟地址(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
};
e_magic:"MZ标记" 用于判断是否为可执行文件.
e_lfanew:PE头相对于文件的偏移,用于定位PE文件
e_lfanew:PE头相对于文件的偏移,用于定位PE文件
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; //文件属性
};
Machine:程序运行的CPU型号:0x0 任何处理器/0x14C 386及后续处理器
NumberOfSections:文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值.
SizeOfOptionalHeader:可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h 大小可以自定义.
NumberOfSections:文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值.
SizeOfOptionalHeader:可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h 大小可以自定义.
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];
};
Magic:说明文件类型:10B 32位下的PE文件 20B 64位下的PE文件
AddressOfEntryPoint:程序入口//真在地址要加上内存镜像基址
ImageBase:内存镜像基址/内存中所有数据开始的地址,DOS头开始
SizeOfImage:内存中整个PE文件的映射的尺寸(拉伸后的大小),可以比实际的值大,但必须是SectionAlignment的整数倍
AddressOfEntryPoint:程序入口//真在地址要加上内存镜像基址
ImageBase:内存镜像基址/内存中所有数据开始的地址,DOS头开始
SizeOfImage:内存中整个PE文件的映射的尺寸(拉伸后的大小),可以比实际的值大,但必须是SectionAlignment的整数倍
参考书籍:《加密与解密四》