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决定!

上面都是空的,同样可以进行非法利用!

posted @   zpchcbd  阅读(1033)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· 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
点击右上角即可分享
微信分享提示