PE文件头入门
PE文件
首先需要知道DOS头,其其数据结构不太需要知道,但需要知道其中的两个和偏移
e_magic WORD ;0000h exe标志,"MZ"头
e_lfanew DWORD 003ch "PE"头的偏移地址
整个数据结构大小为40h
这里我们随便找一个.exe程序为例
找到了e_flanew后就可以去找PE头,其头有一个标识,且占4字节,其值也是"PE"的ascii,以两个0结尾"PE\0\0"
PE标志后面接着就是IMAGE_FILE_HEADER(PE标准头),其结构大小为14h,加上标记,就是18h
IMAGE_FILE_HEADER
Machine WORD 004h
SizeOfSection WORD 006h PE中节的数量
TimeDateStamp DWORD 008h 文件创建日期
PointerToSymbolTable DWORD 00ch 指向符号表的指针
NumberOfSymbols DWORD 010h 符号表中符号个数
SizeOfOptionalHeader WORD 014h 扩展PE头的长度
Characteristics WORD 016h 文件属性
也就是一下部分
跟在其后面的是PE扩展头
PE扩展头大小不一定,但可以通过PE标准头的成员SizeOfOptionHeader来获取PE扩展头的大小
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; /*机器型号,判断是PE是32位还是64位*/
BYTE MajorLinkerVersion; /*连接器版本号高版本*/
BYTE MinorLinkerVersion; /*连接器版本号低版本,组合起来就是 5.12 其中5是高版本,C是低版本*/
DWORD SizeOfCode; /*代码节的总大小(512为一个磁盘扇区)*/
DWORD SizeOfInitializedData; /*初始化数据的节的总大小,也就是.data*/
DWORD SizeOfUninitializedData; /*未初始化数据的节的大小,也就是 .data ? */
DWORD AddressOfEntryPoint; /*程序执行入口(OEP) RVA(相对偏移)*/
DWORD BaseOfCode; /*代码的节的起始RVA(相对偏移)也就是代码区的偏移,偏移+模块首地址定位代码区*/
DWORD BaseOfData; /*数据结的起始偏移(RVA),同上*/
DWORD ImageBase; /*程序的建议模块基址(意思就是说作参考用的,模块地址在哪里)*/
DWORD SectionAlignment; /*内存中的节对齐*/
DWORD FileAlignment; /*文件中的节对齐*/
WORD MajorOperatingSystemVersion; /*操作系统版本号高位*/
WORD MinorOperatingSystemVersion; /*操作系统版本号低位*/
WORD MajorImageVersion; /*PE版本号高位*/
WORD MinorImageVersion; /*PE版本号低位*/
WORD MajorSubsystemVersion; /*子系统版本号高位*/
WORD MinorSubsystemVersion; /*子系统版本号低位*/
DWORD Win32VersionValue; /*32位系统版本号值,注意只能修改为4 5 6表示操作系统支持nt4.0 以上,5的话依次类推*/
DWORD SizeOfImage; /*整个程序在内存中占用的空间(PE映尺寸)*/
DWORD SizeOfHeaders; /*所有头(头的结构体大小)+节表的大小*/
DWORD CheckSum; /*校验和,对于驱动程序,可能会使用*/
WORD Subsystem; /*文件的子系统 :重要*/
WORD DllCharacteristics; /*DLL文件属性,也可以成为特性,可能DLL文件可以当做驱动程序使用*/
DWORD SizeOfStackReserve; /*预留的栈的大小*/
DWORD SizeOfStackCommit; /*立即申请的栈的大小(分页为单位)*/
DWORD SizeOfHeapReserve; /*预留的堆空间大小*/
DWORD SizeOfHeapCommit; /*立即申请的堆的空间的大小*/
DWORD LoaderFlags; /*与调试有关*/
DWORD NumberOfRvaAndSizes; /*下面的成员,数据目录结构的项目数量*/
IMAGE_DATA_DIRECTORY DataDirectory[16];/*数据目录,默认16个,16是宏,这里方便直接写成16*/
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
在扩展头里,最后一个成员为数据目录项
其结构为
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; 虚拟地址(表格位置) DWORD Size; 大小 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
其成员为
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
PE扩展头后面便是节表
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; /*节区的名字*/ union { DWORD PhysicalAddress; DWORD VirtualSize; /*节区的尺寸*/ } Misc; DWORD VirtualAddress; /*虚拟地址 节区的RVA地址(偏移)*/ DWORD SizeOfRawData; /*在文件中对齐的尺寸*/ DWORD PointerToRawData; /*在文件中的偏移*/ DWORD PointerToRelocations; /*在OBJ文件中使用*/ DWORD PointerToLinenumbers; /*行号表位置,调试使用*/ WORD NumberOfRelocations; /*在OBJ文件中使用*/ WORD NumberOfLinenumbers; /*行号表的数量*/ DWORD Characteristics; /*节的属性*/ } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;