学习PE文件头

本文详细介绍了PE文件(portable executable)的结构,包括DOS文件头、DOS stub程序、NT文件头的组成部分,如Signature、IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER32等,并解析了各部分的关键字段及其作用,如Machine、SizeOfCode、AddressOfEntryPoint等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PE文件(portable executable)

大致结构


1.DOS文件头 64byte 

                    其中最重要的是: e_magic 表示MS-DOS文件头的签名

                                              e_lfanew 指出 image_nt_header在映像中的位置

2.DOS stub程序 128byte 

                        运行在DOS下的16位程序,目的是指出当windows程序在dos下运行时,显示信息表明该程序不能

                        在dos下执行并终止执行

3.NT文件头  

                       PE文件的核心部分,被定义为s64和s32两个版本

              其中包含有三个部分包括一个‘PE’字样的签名,PE文件头(IMAGE_FILE_HEADER),和PE可选头(IMAGE_OPTIONAL_HEADER32)

(1)Signature:类似于DOS头中的e_magic,其高16位是0,低16是0x4550,用字符表示是'PE‘。      

(2)IMAGE_FILE_HEADER            

  1. typedef struct _IMAGE_FILE_HEADER {  
  2.     WORD    Machine;  
  3.     WORD    NumberOfSections;  
  4.     DWORD   TimeDateStamp;  
  5.     DWORD   PointerToSymbolTable;  
  6.     DWORD   NumberOfSymbols;  
  7.     WORD    SizeOfOptionalHeader;  
  8.     WORD    Characteristics;  
  9. } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 

Machine:该文件的运行平台,是x86、x64还是I64等等

NumberOfSections:该PE文件中有多少个节,也就是节表中的项数。

TimeDateStamp:PE文件的创建时间,一般有连接器填写。

PointerToSymbolTable:COFF文件符号表在文件中的偏移。

NumberOfSymbols:符号表的数量。

SizeOfOptionalHeader:紧随其后的可选头的大小。

Characteristics:可执行文件的属性,可以是下面这些值按位相或。

(3)IMAGE_OPTIONAL_HEADER

  1. typedef struct _IMAGE_OPTIONAL_HEADER {  
  2.     WORD    Magic;  
  3.     BYTE    MajorLinkerVersion;  
  4.     BYTE    MinorLinkerVersion;  
  5.     DWORD   SizeOfCode;  
  6.     DWORD   SizeOfInitializedData;  
  7.     DWORD   SizeOfUninitializedData;  
  8.     DWORD   AddressOfEntryPoint;  
  9.     DWORD   BaseOfCode;  
  10.     DWORD   BaseOfData;  
  11.     DWORD   ImageBase;  
  12.     DWORD   SectionAlignment;  
  13.     DWORD   FileAlignment;  
  14.     WORD    MajorOperatingSystemVersion;  
  15.     WORD    MinorOperatingSystemVersion;  
  16.     WORD    MajorImageVersion;  
  17.     WORD    MinorImageVersion;  
  18.     WORD    MajorSubsystemVersion;  
  19.     WORD    MinorSubsystemVersion;  
  20.     DWORD   Win32VersionValue;  
  21.     DWORD   SizeOfImage;  
  22.     DWORD   SizeOfHeaders;  
  23.     DWORD   CheckSum;  
  24.     WORD    Subsystem;  
  25.     WORD    DllCharacteristics;  
  26.     DWORD   SizeOfStackReserve;  
  27.     DWORD   SizeOfStackCommit;  
  28.     DWORD   SizeOfHeapReserve;  
  29.     DWORD   SizeOfHeapCommit;  
  30.     DWORD   LoaderFlags;  
  31.     DWORD   NumberOfRvaAndSizes;  
  32.     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];  
  33. } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;  

Magic:表示可选头的类型。

MajorLinkerVersion和MinorLinkerVersion:链接器的版本号。

SizeOfCode:代码段的长度,如果有多个代码段,则是代码段长度的总和。

SizeOfInitializedData:初始化的数据长度。

SizeOfUninitializedData:未初始化的数据长度。

AddressOfEntryPoint:程序入口的RVA,对于exe这个地址可以理解为WinMain的RVA。对于DLL,这个地址可以理解为DllMain的RVA,如果是驱动程序,可以理解为DriverEntry的RVA。当然,实际上入口点并非是WinMain,DllMain和DriverEntry,在这些函数之前还有一系列初始化要完成,当然,这些不是本文的重点。

BaseOfCode:代码段起始地址的RVA。

BaseOfData:数据段起始地址的RVA。

ImageBase:映象(加载到内存中的PE文件)的基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址。

SectionAlignment:节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0。

FileAlignment:节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignment。

MajorOperatingSystemVersion、MinorOperatingSystemVersion:所需操作系统的版本号,随着操作系统版本越来越多,这个好像不是那么重要了。

MajorImageVersion、MinorImageVersion:映象的版本号,这个是开发者自己指定的,由连接器填写。

MajorSubsystemVersion、MinorSubsystemVersion:所需子系统版本号。

Win32VersionValue:保留,必须为0。

SizeOfImage:映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。

SizeOfHeaders:所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。

CheckSum:映象文件的校验和。

Subsystem:运行该PE文件所需的子系统

DllCharacteristics:DLL的文件属性,只对DLL文件有效

SizeOfStackReserve:运行时为每个线程栈保留内存的大小。

SizeOfStackCommit:运行时每个线程栈初始占用内存大小。

SizeOfHeapReserve:运行时为进程堆保留内存大小。

SizeOfHeapCommit:运行时进程初始占用内存大小。

LoaderFlags:保留,必须为0。

NumberOfRvaAndSizes:数据目录的项数,即下面这个数组的项数。

DataDirectory:数据目录,这是一个数组,数组的项定义如下:

[cpp]  view plain  copy
  1. typedef struct _IMAGE_DATA_DIRECTORY {  
  2.     DWORD   VirtualAddress;  
  3.     DWORD   Size;  
  4. } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  

这个结构十分重要,它用来描述 windows 执行映象中所使用的各种表格的位置和大小。VirtualAddress 域是一个   RVA (Relative Virtual Address)值,更明白一点就是:它是一个偏移量(基于 PE 文件头),Size 域表示这个表格有多大。 这个数组有 16 个元素,也就是表示,在执行映象中最多可以使用 16 个表格。


实际上这 16 个表格是固定的,对于这些表格 Microsoft 都作了统一的规定,在 WinNT.h 里都作了定义:

// Directory Entries

#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

 介绍见Microsofthttp://msdn.microsoft.com/en-us/library/ms680305(VS.85).asp

下面是t..exe 映象中使用的表

00000148  00 00 00 00                   // IMAGE_DATA_DIRECTORY[0]
0000014A  00 00 00 00
00000150  10 20 00 00                   // IMAGE_DATA_DIRECTORY[1] 
00000154  28 00 00 00 

00000158  00 00 00 00                  // IMAGE_DATA_DIRECTORY[2]
0000015A  00 00 00 00
00000160  00 00 00 00                   // IMAGE_DATA_DIRECTORY[3] 
00000164  00 00 00 00 
00000168  00 00 00 00                   // IMAGE_DATA_DIRECTORY[4]
0000016A  00 00 00 00
00000170  00 40 00 00                   // IMAGE_DATA_DIRECTORY[5] 
00000174  0C 00 00 00 

00000178  00 00 00 00                   // IMAGE_DATA_DIRECTORY[6]
0000017A  00 00 00 00
00000180  00 00 00 00                   // IMAGE_DATA_DIRECTORY[7] 
00000184  00 00 00 00 
00000188  00 00 00 00                   // IMAGE_DATA_DIRECTORY[8]
0000018A  00 00 00 00
00000190  00 00 00 00                   // IMAGE_DATA_DIRECTORY[9] 
00000194  00 00 00 00 
00000198  00 00 00 00                   // IMAGE_DATA_DIRECTORY[10]
0000019A  00 00 00 00
000001A0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[11] 
000001A4  00 00 00 00 
000001A8  00 20 00 00                   // IMAGE_DATA_DIRECTORY[12]
000001AA  10 00 00 00

000001B0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[13] 
000001B4  00 00 00 00 
000001B8  00 00 00 00                   // IMAGE_DATA_DIRECTORY[14]
000001BA  00 00 00 00
000001C0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[15] 
000001C4  00 00 00 00                               

我们的示例程序 t.exe 仅仅使用了 3 个 driectory 表格:import tablerelocation table 以及 import address table

import table 的 RVA 是 0x00002010,size 是 0x28,relocation table 的 RVA 是 0x4000,size 是 0x0c, import address table 的 RVA 是 0x2000,size 是 0x10

t.exe  ImageBase 是 0x00000001_40000000,那么 import table 则在 0x00000001_40002010,relocation table 则在 0x00000001_40004000,import address table 则在 0x00000001_40002000。

(4)导入表

mport table 在 WinNT.h 中定义为一个 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)
    } DUMMYUNIONNAME;
    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;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;


4.节表

IMAGE_NT_HEADER 结构后面紧接着就是 section table(节表)结构, 从 0x1c8 ~ 0x267 共 160 bytes。

这个节表结构在 WinNT.h 中定义为

//
// Section header format.
//

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            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;

#define IMAGE_SIZEOF_SECTION_HEADER          40

映象中包括有多少个节表结构,由 IMAGE_NT_HEADER 结构中的 IMAGE_FILE_HEADER 结构中的 NumberOfSections 域指出。在前面所述的 t.exe 映象里 NumberOfSections 值是 4 那么表示将有 4 个 sections 存在于映象中。从前的面 dumpbin 工具输出可以得出。

 IMAGE_SECTION_HEADER 结构的第 1 个域 Name,用来标识 section table 的名字。它的长度固定为 8 bytes(前面定义的宏),这将意味着,不存在超过 8 bytes 的节表名。接下来使用 VirtualSize 来用表示 section talbe 大小。VirtualAddress 表示 section table 的 RVA

size
描述
Name
8 bytes
Section 表名字
VirtualSize
DWORD
Section 表的大小
VirtualAddress
DWORD
Section 表的 RVA,即:section 表的位置
SizeOfRawData
DWORD
section 表占用映像的大小,这个 size 是以 0x200 为单位的
PointerToRawData
DWORD
section 表在映像中的物理位置,即是:file 位置,而非 virtual 位置
PointerToRelocation
DOWRD
 
PointerToLinenumber
DWORD
 
NumberOfRelocation
WORD
 
NumberOfLineumbers
WORD
 
Characteristics
DWORD
section 的属性 flags,可用于 '|' 多个属性值

所有的 Characteristics 都在 WinNT.h 中有定义,下面是一些常用的 flags:

#define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
... ...
#define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.

下面以 .text 节为例,看看 t.exe  .text 是什么。

.text 节的 Name 是 ".text",VirtualSize 是 0x42 bytes,.text 节在 RVA 为 0x00001000 的位置上,section 的分配单位为 0x1000(4K bytes),第 1 个 section 一般都分配在 0x1000 位置上,SizeOfRawData 为 0x200,这是映像文件分配单位。PointerToRawData 为 0x400,说明 .text 节在映像文件的 0x400 处。Characteristics 是 0x60000020,说明 .text 是 executable/readable/code 属性。




                        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值