PE结构的组成
PE结构概述图
由下图可得,PE文件主要分为四部分,分别有DOS部分、PE文件头、节表以及节数据
DOS部分
DOS MZ文件头
DOS文件头结构体代码一共有64字节,最后一个成员指向pe头
在以下结构体代码中的成员只需记住两个,最开始的和最末尾的成员
e_magic和e_lfanew是构成PE指纹的重要成员,不能被修改
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; //通常为"MZ"
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
LONG e_lfanew; // 指向PE头的指针
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
DOS块
又称DOS Sub, 其大小不确定, 可以随意填充垃圾数据或者恶意代码
PE文件头(NT头)
PE头又分为标识PE头、标准PE头、扩展PE头
PE文件一些重要信息通常存储在标准PE头和扩展PE头中
如以下代码所示PE文件头结构体,分别是32位与64位的
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature; //标识PE头,占2字节
IMAGE_FILE_HEADER FileHeader; //标准PE头,占20字节
IMAGE_OPTIONAL_HEADER64 OptionalHeader; //扩展PE头,占224字节
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
标识PE头
标识PE头占2个字节,且不能被修改
标准PE头
标准PE头占20个字节,其结构体如下代码所示
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //决定能运行在什么样的cpu上,若值为0则表示能任意cpu运行;值为14C表示能在intel386及后续版本;值为8664能运行在x64cpu上。因此可以用来判断程序是32位还是64位的
WORD NumberOfSections; //表示当前节的数量
DWORD TimeDateStamp; //编译器填写的时间戳,与文件创建或修改时间无关
DWORD PointerToSymbolTable; //与调试相关
DWORD NumberOfSymbols; //与调试相关
WORD SizeOfOptionalHeader; //扩展PE头的大小,一般32位为E0,64位为F0
WORD Characteristics; //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
扩展PE头
扩展PE头占224个字节
以下结构体成员中有几个成员要重点注意
如果一个可执行文件它的dos部分+pe头+节表的共同大小为332,其FileAlignment(文件对齐)的值为200, 那么SizeOfHeaders的值应该为文件对齐的整数倍,所以SizeOfHeaders的值为400
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; //32位的值为10B,64位的值为20B
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; //所有代码节的总和
DWORD SizeOfInitializedData; //初始化数据节的大小
DWORD SizeOfUninitializedData; //未初始化节的大小
DWORD AddressOfEntryPoint; //程序入口
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase; //内存镜像基址
DWORD SectionAlignment; //内存对齐
DWORD FileAlignment; //文件对齐
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; //内存中整个PE文件映射的尺寸,可比实际值大,必须是内存对齐的整数倍
DWORD SizeOfHeaders; //所有头+节表按照文件对齐后的大小,否则加载会出错
DWORD CheckSum; //校验和,用来判断文件是否被修改
WORD Subsystem; //子系统 驱动程序(1) 图形界面(2) 控制台或dll(3)
WORD DllCharacteristics; //文件特效,并非针对dll文件
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;
节表
PE文件中所有节的属性都定义在节表里面
节表是由一系列IMAGE_SECTION_HEADER结构的元素组成的结构体数组, 每个元素占40个字节
以下是IMAGE_SECTION_HEADER结构体代码
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //ASCII字符串,当前节的名字
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc; //当前节实际的大小(内存未对齐)
DWORD VirtualAddress; //节在内存中的偏移地址(真正的地址需加上Imagebase)
DWORD SizeOfRawData; //节在文件对齐后的大小
DWORD PointerToRawData; //节在文件中的偏移地址
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //节的属性,涉及到此节是否可读可写可执行
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
标签:文件,WORD,IMAGE,HEADER,PE,DWORD,组成,结构
From: https://www.cnblogs.com/henry666/p/16623174.html