PE文件结构
可执行文件:可以由操作系统进行加载执行的文件
在Windows平台中若要程序可执行,必须遵守可执行文件的格式
可执行文件的格式:
Windows平台:PE(Portable Executable)文件结构,其中Portable也就是Windows系统中能够跨平台运行
Linux平台:ELF(Executable and Linking Format)文件结构
PE文件特征:
如exe、dll、vxd、sys和vdm等文件都遵守可执行文件的格式!
常用的识别PE文件的方法:
1、用HEX编辑器打开文件,头两个字节是不是MZ
,然后向后到第64字节的位置,例如为10 01 00 00
,因为存储模式为小端存储所以,实际是0x00000110
2、再去找到0x00000110的位置,向后2字节,如果为字符显示为PE则基本就是PE文件了
PE文件结构:
PE文件结构分为四大部分:
1、Dos部分 2、PE文件头 3、节表 4、节数据
1、Dos部分:
MZ文件头
:IMAGE_DOS_HEADER
结构体,其大小占64个字节,并且该结构中的最后一个LONG类型e_lfanew
成员指向PE文件头的位置为中的PE文件头标志的地址
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header 指向PE文件头的位置为中的PE文件头标志的地址 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
Dos Stub
:属于链接器进行填充的,大小不一定,属于DOS部分的DOS块,无实际作用,但是可以作为注入手段进行利用!图中第二个红框则是Dos Stub
2、PE文件头:
整个PE头的结构体
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; //PE文件头标志 IMAGE_FILE_HEADER FileHeader; //PE文件表头 IMAGE_OPTIONAL_HEADER32 OptionalHeader; //扩展PE头 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
PE文件头分为三部分:
1、PE文件头标志(PE标识),占4个字节,作为标识用,如上图查看是否为PE文件就是利用这个方法!
2、PE文件表头(标准PE头),占20个字节,为IMAGE_FILE_HEADER
结构体,该结构体中SizeOfOptionalHeader
结构体中有个成员可以看出可扩展文件表头的大小
typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; // 可以看出可扩展文件表头的大小,也分为x32 x64 x32大小为E0 x64大小为F0 WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
3、可扩展文件表头(扩展PE头),占224字节,结构体分两种 一种是32位的,一种是16位的,分别是IMAGE_OPTIONAL_HEADER32
结构体 和 16xIMAGE_DATA_DIRECTORY
结构体
typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; // 分辨32位程序还是64位,如果32位则10B,64位则20B BYTE MajorLinkerVersion; //链接器版本号 BYTE MinorLinkerVersion; //链接器版本号 DWORD SizeOfCode; //所有代码节的总和 文件对齐后的大小 编译器填写的,无用处 DWORD SizeOfInitializedData; //已经初始化数据的节的总大小 文件对齐后的大小 编译器填写的,无用处 DWORD SizeOfUninitializedData; // 未初始化数据的节的总大小 文件对齐后的大小 编译器填写的,无用处 DWORD AddressOfEntryPoint; // 程序入口 DWORD BaseOfCode; //代码开始的基址 编译器填写的,无用处 DWORD BaseOfData; //数据开始的基址 编译器填写的,无用处 // // NT additional fields. // DWORD ImageBase; //内存镜像基址 DWORD SectionAlignment; //内存对齐 DWORD FileAlignment; //文件对齐 WORD MajorOperatingSystemVersion; //操作系统版本号 WORD MinorOperatingSystemVersion; //操作系统版本号 WORD MajorImageVersion; //PE文件自身的版本号 WORD MinorImageVersion; //PE文件自身的版本号 WORD MajorSubsystemVersion; //运行所需要子系统的版本号 WORD MinorSubsystemVersion; //运行所需要子系统的版本号 DWORD Win32VersionValue; //子系统版本的值,必须为0 DWORD SizeOfImage; //内存中整个PE文件的映射尺寸,比实际的值大,必须是SectionAlignment整数倍 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;
其中FileAlignment
成员的值为文件对齐之后的大小,SizeOfHeaders
成员的值一定是FileAlignment
的值的整数倍,而且SizeOfHeaders
保存的值是所有的头(有DOS部分 PE文件头)加上节表的大小
如果FileAlignment
(文件对齐之后的大小)是200 那么SizeOfHeaders
头一定是200的整数倍,那么就是400,如果FileAlignment
不到200那么SizeOfHeaders
就按照200对齐,如下图可以看到文件对齐后的FileAlignment
的大小为0x200H,SectionAlignment
是在内存中加载的时候,文件对齐之后的大小,在FileAlignment
前四个字节,那么就是0x1000H
那么再去看下SizeOfHeaders
的大小,相差二十个字节,继续往后移,结果如下:0x400,的确是FileAlignment
的两倍
如果每次都是整数倍,那肯定有很多地方空出来,实际上是用空间换取时间,增加了处理效率,具体以后自己知道了再补上!
3、节表: 其中每个节大小占40个字节,十六进制 0x28
IMAGE_SECTION_HEADER
结构体(40字节) * 节的数量(大小取决于节的数量)
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; //当前节的名称 , 占8字节 DWORD VirtualSize; //内存中文件对齐大小 } Misc; DWORD VirtualAddress; //在内存中的偏移地址 DWORD SizeOfRawData; //当前节在文件中对齐后的大小 DWORD PointerToRawData; //当前节在文件中的偏移 DWORD PointerToRelocations; // 调试相关 DWORD PointerToLinenumbers;// 调试相关 WORD NumberOfRelocations;// 调试相关 WORD NumberOfLinenumbers;// 调试相关 DWORD Characteristics; //文件属性,比如该节数据属性是否为可执行属性,都在这里面 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
我这边有五个节,如下显示:
4、节数据:
sizeofHeaders 按照文件对齐.存储着 头 + 节表的大小,这里就是0x400之后的数据了!也就是如下:
在硬盘中存储的时候文件的每个节数据之间的大小由FileAlignment
决定!
上面都是空的,同样可以进行非法利用!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY