PE文件-OptionalHeader解剖

已知PE文件的NT头由签名、FileHeader和OptionalHeader组成。本文内容是针对OptionalHeader进行解剖。

//
// Optional header format.
//

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    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;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    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;

这是winnt.h头文件里对OptionalHeader的定义,可以看到整个OptionalHeader分为2大块儿,分别是标准字段和NT可选字段。

标准字段

Magic。魔法或者翻译成幻数,占2个字节,我们看看test.exe该字段的内容

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2)) -l 2 test.exe
00000100: 0b01                                     ..
root@entry0:~#

内容是0x10b,我们看看0x10b是什么意思

%title插图%num
%title插图%num

原来该字段表示的是PE文件是32位系统还是64位系统的,0x10b是PE32,指32位的文件,0x20b是PE32+,指64位的文件。

由此可知我这边的test.exe文件是32位的文件。

MajorLinkerVersion。主链接器版本,占1个字节,看看test.exe文件的主链接器版本是多少

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2)) -l 1 test.exe
00000102: 06                                       .
root@entry0:~#

版本是0x6,即主链接器版本是6。

MinorLinkerVersion。次链接器版本,占1个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1)) -l 1 test.exe
00000103: 00                                       .
root@entry0:~#

我这儿test.exe文件的次链接器版本是0。

SizeOfCode。代码大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1)) -l 4 test.exe
00000104: 00b0 0000                                ....
root@entry0:~#

0x0000b000,test.exe的代码大小是0xb000,即45056个字节。

The size of the code (text) section, or the sum of all code sections if there are multiple sections.

代码(text)段的大小,如果有多个代码段,则为所有代码段的总和。

SizeOfInitializedData。初始化数据的大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4)) -l 4 test.exe
00000108: 00a0 0000                                ....
root@entry0:~#

0x0000a000,test.exe的初始化数据的大小是0xa000,即40960个字节。

The size of the initialized data section, or the sum of all such sections if there are multiple data sections.

初始化数据段(data)的大小,或者如果有多个数据段,则表示所有这些数据段的总和。

SizeOfUninitializedData。未初始化数据的大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4)) -l 4 test.exe
0000010c: 0000 0000                                ....
root@entry0:~#

未初始化的数据大小是0。

The size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections.

未初始化数据段(BSS)的大小,如果有多个BSS段,则表示所有这些段的总和。

AddressOfEntryPoint。程序入口点,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4)) -l 4 test.exe
00000110: 9caf 0000                                ....
root@entry0:~#

test.exe的程序入口点是0x0000af9c,我们验证一下看看是不是

root@entry0:~# r2 -A test.exe
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[0x0040af9c]>

可以看到停在了入口处,入口处是0x0040af9c,0x400000是程序基址,加上0xaf9c就是0x0040af9c。

The address of the entry point relative to the image base when the executable file is loaded into memory. For program images, this is the starting address. For device drivers, this is the address of the initialization function. An entry point is optional for DLLs. When no entry point is present, this field must be zero.

可执行文件加载到内存时,相对于程序的入口点的地址。 对于程序映像,这是起始地址。 对于设备驱动程序,这是初始化函数的地址。 DLL的入口点是可选的。 如果不存在任何入口点,则此字段必须为零。

BaseOfCode。代码段(text)的基地址,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4)) -l 4 test.exe
00000114: 0010 0000                                ....
root@entry0:~#

0x00001000,test.exe的代码段基地址是0x1000

The address that is relative to the image base of the beginning-of-code section when it is loaded into memory.

程序代码段(text)起始位置的地址,当它被加载到内存中时。

BaseOfData。数据段(data)的基地址,占4个字节,如果是64位的文件没有该字段

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4)) -l 4 test.exe
00000118: 00c0 0000                                ....
root@entry0:~#

0x0000c000,test.exe的数据段基地址是0xc000

The address that is relative to the image base of the beginning-of-data section when it is loaded into memory.

程序数据段(data)起始位置的地址,当它被加载到内存中时。

NT可选字段

ImageBase。程序基址,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4)) -l 4 test.exe
0000011c: 0000 4000                                ..@.
root@entry0:~#

0x00400000,test.exe的程序基址是0x400000

The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K. The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.

程序的第一个字节的首选地址,当加载到内存时; 必须是64 K的倍数。DLL的默认值为0x10000000。 Windows CE EXE的默认值为0x00010000。 Windows NT,Windows 2000,Windows XP,Windows 95,Windows 98和Windows Me的默认值为0x00400000。

通常都是Windows NT,因此test.exe的程序基址是0x00400000。

SectionAlignment。节在内存中的对齐,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4)) -l 4 test.exe
00000120: 0010 0000                                ....
root@entry0:~#

0x00001000,test.exe的节在内存中对齐字节数是0x1000,即每个节在内存中以4096个字节一对齐

The alignment (in bytes) of sections when they are loaded into memory. It must be greater than or equal to FileAlignment. The default is the page size for the architecture.

将节加载到内存中时的对齐方式(以字节为单位)。 它必须大于或等于FileAlignment。 默认值为体系结构的页面大小。

FileAlignment。节在文件中的对齐,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4)) -l 4 test.exe
00000124: 0010 0000                                ....
root@entry0:~#

0x00001000,test.exe的节在文件中对齐字节数是0x1000,即每个节在文件中以4096个字节一对齐

The alignment factor (in bytes) that is used to align the raw data of sections in the image file. The value should be a power of 2 between 512 and 64 K, inclusive. The default is 512. If the SectionAlignment is less than the architecture’s page size, then FileAlignment must match SectionAlignment.

对齐因子(以字节为单位),用于对齐图像文件中各节的原始数据。 该值应为512到64 K(含)之间的2的幂。 默认值为512。如果SectionAlignment小于体系结构的页面大小,则FileAlignment必须匹配SectionAlignment。

MajorOperatingSystemVersion。操作系统主版本号,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4)) -l 2 test.exe
00000128: 0400                                     ..
root@entry0:~#

0x4,test.exe的操作系统主版本是4

The major version number of the required operating system.

所需操作系统的主要版本号。

MinorOperatingSystemVersion。操作系统次要版本号,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2)) -l 2 test.exe
0000012a: 0000                                     ..
root@entry0:~#

test.exe的操作系统此要版本是0。

The minor version number of the required operating system.

所需操作系统的次版本号。

MajorImageVersion。Image映像主版本号,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2)) -l 2 test.exe
0000012c: 0000                                     ..
root@entry0:~#

test.exe的Image映像主版本是0。

The major version number of the image.

映像的主要版本号。

MinorImageVersion。Image映像次版本号,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2)) -l 2 test.exe
0000012e: 0000                                     ..
root@entry0:~#

test.exe的Image映像次版本也是0。

The minor version number of the image.

映像的次版本号。

MajorSubsystemVersion。子系统主版本号,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2)) -l 2 test.exe
00000130: 0400                                     ..
root@entry0:~#

test.exe的子系统主版本是4。

The major version number of the subsystem.

子系统的主要版本号。

MinorSubsystemVersion。子系统的次要版本号,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2)) -l 2 test.exe
00000132: 0000                                     ..
root@entry0:~#

test.exe的子系统次版本是0。

The minor version number of the subsystem.

子系统的次要版本号。

Win32VersionValue。保留字段,占4个字节,内容默认都是0

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2)) -l 4 test.exe
00000134: 0000 0000                                ....
root@entry0:~#

SizeOfImage。Image映像大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4)) -l 4 test.exe
00000138: 0060 0100                                .`..
root@entry0:~#

0x00016000,test.exe的Image映像大小是0x16000,即90112个字节

The size (in bytes) of the image, including all headers, as the image is loaded in memory. It must be a multiple of SectionAlignment.

当映像加载到内存中时,映像的大小(以字节为单位),包括所有头部。 它必须是SectionAlignment的倍数。

SizeOfHeaders。头部大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4)) -l 4 test.exe
0000013c: 0010 0000                                ....
root@entry0:~#

0x00001000,test.exe的头部大小占0x1000个字节

The combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of FileAlignment.

MS-DOS存根,PE标头和节标头的组合大小四舍五入为FileAlignment的倍数。

CheckSum。文件校验和,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4)) -l 4 test.exe
00000140: 0000 0000                                ....
root@entry0:~#

test.exe的文件校验和是0

The image file checksum. The algorithm for computing the checksum is incorporated into IMAGHELP.DLL. The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process.

映像文件校验和。 用于计算校验和的算法已合并到IMAGHELP.DLL中。 在加载时检查以下内容的有效性:所有驱动程序,在引导时加载的任何DLL以及在关键Windows进程中加载的任何DLL。

Subsystem。子系统,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4)) -l 2 test.exe
00000144: 0200                                     ..
root@entry0:~#

0x2,test.exe的子系统是2,继续看看2表示什么

The subsystem that is required to run this image. For more information, see Windows Subsystem.

运行该映像所需的子系统。 有关更多信息,请参见Windows子系统

%title插图%num

The Windows graphical user interface (GUI) subsystem

Windows图形用户界面(GUI)子系统

winnt.h头文件里也有记录,Image runs in the WIndows GUI subsystem。

// Subsystem Values

#define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //
#define IMAGE_SUBSYSTEM_EFI_ROM              13
#define IMAGE_SUBSYSTEM_XBOX                 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    17

DllCharacteristics。dll特征,占2个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2)) -l 2 test.exe
00000146: 0000                                     ..
root@entry0:~#

test.exe的dll特征是0,我想应该是由于test.exe并非dll文件所以是0吧

For more information, see DLL Characteristics later in this specification.

有关更多信息,请参见本规范后面的DLL特性

SizeOfStackReserve。栈预留大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2+2)) -l 4 test.exe
00000148: 0000 1000                                ....
root@entry0:~#

test.exe的栈预留大小是0x00100000

The size of the stack to reserve. Only SizeOfStackCommit is committed; the rest is made available one page at a time until the reserve size is reached.

要保留的栈大小。 只有SizeOfStackCommit被提交; 其余部分每次一页可用,直到达到保留大小。

SizeOfStackCommit。栈大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2+2+4)) -l 4 test.exe
0000014c: 0010 0000                                ....
root@entry0:~#

test.exe的栈大小是0x1000,即4096个字节

The size of the stack to commit.

要提交的栈的大小。

SizeOfHeapReserve。堆预留大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2+2+4+4)) -l 4 test.exe
00000150: 0000 1000                                ....
root@entry0:~#

test.exe的堆预留大小是0x100000

The size of the local heap space to reserve. Only SizeOfHeapCommit is committed; the rest is made available one page at a time until the reserve size is reached.

要保留的本地堆空间的大小。 仅SizeOfHeapCommit被提交; 其余部分每次一页可用,直到达到保留大小。

SizeOfHeapCommit。堆大小,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2+2+4+4+4)) -l 4 test.exe
00000154: 0010 0000                                ....
root@entry0:~#

test.exe的堆大小是0x1000,即4096个字节,与栈相同

The size of the local heap space to commit.

要提交的本地堆空间的大小。

LoaderFlags。保留字段,占4个字节,因此都是默认为0填充

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2+2+4+4+4+4)) -l 4 test.exe
00000158: 0000 0000                                ....
root@entry0:~#

NumberOfRvaAndSizes。可选头其余部分中的数据目录条目数,占4个字节

root@entry0:~# xxd -s $((0xe8+4+2+2+4+4+4+2+2+2+1+1+4+4+4+4+4+4+4+4+4+2+2+2+2+2+2+4+4+4+4+2+2+4+4+4+4+4)) -l                           4 test.exe
0000015c: 1000 0000                                ....
root@entry0:~#

test.exe的可选头其余部分中的数据目录条目数是0x10,即16。

The number of data-directory entries in the remainder of the optional header. Each describes a location and size.

可选头其余部分中的数据目录条目数。 每个描述一个位置和大小。

DataDirectory。数据目录,是个结构体数组,如下:

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]

这个结构体数组一共有16个元素,根据NumberOfRvaAndSizes可以得出,也可以查看winnt.h头文件中IMAGE_NUMBEROF_DIRECTORY_ENTRIES的定义

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

该结构体会有下篇文章专门解剖。本篇是专门拆解OptionalHeader,拆解已结束。

发表回复