PE文件格式分析

您所在的位置:网站首页 winhex添加文件头 PE文件格式分析

PE文件格式分析

2024-07-13 02:15| 来源: 网络整理| 查看: 265

本文最后更新于:星期二, 六月 16日 2020, 3:16 下午

以test.exe文件为例子,分析PE文件的各部分格式

1. MZ文件头

用winhex打开test.exe,找到MZ文件头,为偏移0x00~0x3F的部分

1568533651252

其中每个字段的意义如下所示:

typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number设置ox5A4D ascii码值为'MZ'(标志,不会变的标志) 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; //PE头文件的偏移地址 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

比较重要的有以下几个字段:

offset 0x00-0x01:e_magic字段,一般为4D5A,表示这是一个PE文件 offset 0x3C-0x3D:e_lfanew字段,表示PE文件头的偏移地址,根据这个可以找到PE文件头,在这里为0x000000B0 2. DOS stub

接下来的偏移0x40-0xAF部分,处于MZ头部和PE文件头之间,称为DOS stub,它与MZ头部一起组成DOS小程序

1568534266390

可以看到其中一部分的ASCII码显示为“This is program connot be run in DOS mode.”,这一部分的长度不确定。

3. PE文件头

从MZ文件头中指示的偏移0x000000B0开始为PE文件头,其中包含三个部分:

typedef struct _IMAGE_NT_HEADER { DWORD Signature; //PE Signature : 50450000("PE"00) IMAGE_FILE_HEADER FileHeader; //文件头结构体 IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选头结构体 } IMAGE_NT_HEADER, *PIMAGE_NT_HEADER32; ① PE Signature(签名)

这一字段占0x004字节,为固定子字串”PE \ 0 \ 0”,即50450000,标志着PE文件头的开始

1568534846590

② FileHeader(映像文件头)

紧跟在PE签名后的是映像文件头,占0x014字节,各字段的意义如下:

typedef struct _IMAGE_FILE_HEADER { WORD Machine; //每个CPU都拥有唯一的machine码 WORD NumberOfSections; //节区数量,当定义节区数与实际不同时会发生错误 DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32结构体的大小,固定的 WORD Characteristics; //文件属性,0x0002h为可执行文件,0x2000h为DLL文件 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEAD

test.exe中的实际情况如下:

1568535401922

其中比较重要的几个字段:

offset 0xB6-0xB7:该字段为NumberOfSections,定义了节区的数量,在这里的值为0x0003,说明该程序含有三个节区 offset 0xC4-0xC5:该字段为SizeOfOptionalHeader,定义了可选映像头(OptionalHeader)的大小,在这里的值为0x00E0 ③ OptionalHeader(可选映像头)

根据映像文件头中的信息,可选映像头占0xE0字节,其中各字段意义如下:

typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; //标志字(32位时0x10Bh) BYTE MajorLinkerVersion; //连接器主版本号 BYTE MinorLinkerVersion; //连接器次版本号 DWORD SizeOfCode; //代码段大小 DWORD SizeOfInitializedData; //已初始化数据块大小 DWORD SizeOfUninitializedData; //未初始化数据块大小 DWORD AddressOfEntryPoint; //EP的RVA值,程序最先执行代码的地址 DWORD BaseOfCode; //代码段起始RVA DWORD BaseOfData; //数据段起始RVA DWORD ImageBase; //PE文件的装载地址 DWORD SectionAlignment; //块对齐,节区在内存中最小单位 DWORD FileAlignment; //文件块对齐,节区在文件中的最小单位 WORD MajorOperatingSystemVersion;//所需操作系统版本号 WORD MinorOperatingSystemVersion;// WORD MajorImageVersion; //用户自定义主版本号 WORD MinorImageVersion; //用户自定义次版本号 WORD MajorSubsystemVersion; //win32子系统版本。若PE文件是专门为Win32设计的 WORD MinorSubsystemVersion; //该子系统版本必定是4.0否则对话框不会有3维立体感 DWORD Win32VersionValue; //保留 DWORD SizeOfImage; //内存中整个PE映像体的尺寸 DWORD SizeOfHeaders; //所有头+节表的大小,即整个PE头的大小 DWORD CheckSum; //校验和 WORD Subsystem; //NT用来识别PE文件属于哪个子系统(系统驱动、GUI、CUI) WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; //指定DataDirectory数组的个数 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如引入地址表等 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

test.exe中的实际情况如下:

1568537091093

其中比较重要的几个字段:

offset 0xD8-0xDB:该字段为AddressOfEntryPoint,定义了文件开始执行位置的相对虚拟地址(RVA),在这里的值为0x00001000 offset 0xE4-0xE7:该字段为ImageBase,定义了可执行文件默认装入的内存地址,在这里的值为0x00400000

根据以上两个值,我们可以计算出程序入口的虚拟地址(VA)为0x00401000

除此之外,还有两个特别重要的值:

offset 0x124-0x127:该字段为NumberOfRvaAndSizes,指示了数据目录项(DataDirectory)的项数,在这里的值为0x00000010,即16项 offset 0x128-0x1A7:该字段为DataDirectory,是一个IMAGE_DATA_DIRECTORY数组,里面存放的是可执行文件的一些重要部分的起始RVA和尺寸,目的是使可执行文件更快地进行装载

IMAGE_DATA_DIRECTORY的结构如下:

typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; //数据块的起始RVA 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 #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 8 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 9 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 10 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 11 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 12 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_IAT 13 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 14 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 15 // COM Runtime descriptor

分析数据,可以发现数据目录中只有两项:

IMAGE_DIRECTORY_ENTRY_IMPORT(Import Directory)RVA=0x00002014 Size=0x0000003C IMAGE_DIRECTORY_ENTRY_IAT(Import Address Table)RVA=0x00002000 Size=0x00000014 4. 节表(Section Table)

紧接着PE文件头的是节表。节表实际上是一个结构数组,每个结构包含了一个节的具体信息(每个结构占用0x28字节),该结构的内容如下:

#define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER{ BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 8个字节的节区名称 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;

test.exe 节表中结构的数目由之前映像文件头中的 NumberOfSections 字段确定,一共包含3个该结构,分别是 .text , .rdata 和 .data ,具体如下:

1568538571454

① .text节表

其中比较重要的几个字段:

offset 0x1A8-0x1AF:该字段为Name,可以从ASCII码值看出该节表对应的节为.text节 offset 0x1B0-0x1B3:该字段在exe文件中为Virtual-Size,表示节的实际字节数,这里的值为0x00000046字节 offset 0x1B4-0x1B7:该字段为VirtualAddress,表示本节起始的相对虚拟地址(RVA),这里的值为0x00001000 offset 0x1BC-0x1BF:该字段为PointerToRawData,表示本节在磁盘中对齐后的位置,这里的值为0x00000400 offset 0x1CC-0x1CF:该字段为Characteristic,表示该节的属性,这里的值为0x60000020 = 0x40000000 + 0x20000000 + 0x20,表示的意义是该节包含代码,并且可读可执行 ② .rdata节表

其中比较重要的几个字段:

offset 0x1D0-0x1D7:该字段为Name,可以从ASCII码值看出该节表对应的节为.rdata节 offset 0x1D8-0x1DB:该字段在exe文件中为Virtual-Size,表示节的实际字节数,这里的值为0x000000A6字节 offset 0x1DC-0x1DF:该字段为VirtualAddress,表示本节起始的相对虚拟地址(RVA),这里的值为0x00002000 offset 0x1E4-0x1E7:该字段为PointerToRawData,表示本节在磁盘中对齐后的位置,这里的值为0x00000600 offset 0x1F0-0x1F7:该字段为Characteristic,表示该节的属性,这里的值为0x40000040 = 0x40000000 + 0x40,表示的意义是该节包含已初始化的数据,并且可读 ③ .data节表 offset 0x1F8-0x1FF:该字段为Name,可以从ASCII码值看出该节表对应的节为.data节 offset 0x200-0x203:该字段在exe文件中为Virtual-Size,表示节的实际字节数,这里的值为0x0000008E字节 offset 0x204-0x207:该字段为VirtualAddress,表示本节起始的相对虚拟地址(RVA),这里的值为0x00003000 offset 0x20C-0x20F:该字段为PointerToRawData,表示本节在磁盘中对齐后的位置,这里的值为0x00000800 offset 0x21C-0x21F:该字段为Characteristic,表示该节的属性,这里的值为0xC0000040 = 0x80000000 + 0x40000000 + 0x30,表示的意义是该节包含已初始化的数据,并且可读可写 5. 节(Section)① .text节

这一节含有程序的可执行代码,根据节表中的值,可以确定.text节在文件中的地址为0x00000400,实际长度为0x46字节,具体代码如下:

1568558607717

② .rdata节

这一节称为引入函数节,包含有从其他DLL中引入的函数,根据IMAGE_DATA_DIRECTORY中引入函数表地址为0x00002014,且根据节表,该表在内存中的偏移为0x00002000,即RVA为0x0。根据节表,该节从文件偏移0x600处开始,那么.rdata表在文件中的偏移就为0x614。大小为0xA6字节。

该节的开始是一个成员为IMAGE_IMPORT_DESCRIPTOR结构的数组,该结构的定义如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; // RVA,指向字符串,是这个可执行文件的名字。例如"ACE.dll" DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) } IMAGE_IMPORT_DESCRIPTOR;

该节的实际数据如下:

1568597519788

第一个IDD的分析如下:

offset 0x614-0x617:该字段为OriginalFirstThunk,是一个IMAGE_THUNK_DATA的指针,值为0x00002050,因为该节在内存中的偏移为0x00002000所以RVA为0x50,在文件中即为0x00000650,这个地址对应的即是第一个IID的IMAGE_THUNK_DATA,其结构如下 typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // 一个RVA地址,指向forwarder string DWORD Function; // PDWORD,被导入的函数的入口地址 DWORD Ordinal; // 该函数的序数 DWORD AddressOfData; // 一个RVA地址,指向IMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA32;

(IMAGE_THUNK_DATA64与IMAGE_THUNK_DATA32的区别,仅仅是把DWORD换成了64位整数。)

所以0x650的值在这里对应的字段为AddressOfData,即一个指向IMAGE_IMPORT_BY_NAME的指针,为0x00002064,即RVA为0x64,在文件中的偏移为0x664,该结构的定义如下:

typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; // 该函数的导出序数 BYTE Name[1]; // 该函数的名字 } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

前两个字节为引入函数的序号,其值为0x0080,后面紧跟的为函数名字符串”ExitProcess”,以0x00结尾

offset 0x620-0x623:该字段为Name,是指向字符串的指针,值为0x00002072,所以文件中偏移为0x00000672,对应的就是”kernel32.dll”,以0x00结尾 offset 0x624-0x627:该字段为FirstThunk,是指向字符串的指针,值为0x00002000,所以文件中偏移为0x00000600,该地址在内存中将会设置成API函数ExitProcess的真实地址

第二个IDD的分析如下:

offset 0x628-0x62B:该字段是OriginalFirstThunk,与第一个IDD的相同,值为0x00002058,RVA为0x58,文件中的偏移为0x658,指向的地址的值为0x0000208C,该地址是指向IMAGE_IMPORT_BY_NAME结构的指针,该值的RVA为0x8C,在文件中的偏移为0x68C,对应的前两个字节为引入函数序号0x019D,后面紧跟的为函数名字符串”MessageBox”,以0x00结尾 offset 0x634-0x637:该字段为Name,是指向字符串的指针,值为0x0000209A,所以文件中偏移为0x0000069A,对应的就是”user32.dll”,以0x00结尾 offset 0x638-0x63B:该字段为FirstThunk,是指向字符串的指针,值为0x00002008,所以文件中偏移为0x00000608,该地址在内存中将会设置成API函数MessageBox的真实地址 ③ .data节

.data节称为已初始化的数据节,其中存放的是在编译时刻已经确定的数据。可以从节表中知道,该节从文件偏移0x800处开始,实际大小为0x8E字节,实际数据如下:

1568638958205

可以看到其中存放着一些程序预设好的字符串,这一部分在程序运行时也会被装入内存之中

信息安全 软件安全

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Docker学习笔记 上一篇 Flask学习笔记(一) 下一篇


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3