PE文件结构详解(非常详细)

news2024/9/19 13:14:32

最近在参考OpenShell为任务栏设置图片背景时,发现里面使用了IAT Hook,这一块没有接触过,去查资料的时候发现IAT Hook需要对PE文件结构有一定的了解,索性将PE文件结构的资料找出来,系统学习一下。

PE文件结构

Portable Executable (PE),可移植的可执行文件。在Windows平台下,所有的可执行文件(包括.exe, .dll, .sys, .ocx, .com等)均使用PE文件结构。这些使用了PE文件结构的可执行文件也称为PE文件。

PE结构包含的结构体有DOS头,PE标识 、文件头、可选头、目录头、目录结构、节表等。

整体结构如下

从上图可以看出PE结构分为4大部分,其中每个部分又进行了细分。

从数据管理的角度来看,可以把PE文件大致分为两部分,

1、DOS头、PE头和节表属于PE文件的数据管理结构或数据组织结构部分,

2、节表数据才是PE文件真正的数据部分,其中包含着代码、数据、资源等内容。

DOS头

DOS头分为“MZ头部”和"DOS存根“。

MZ头部“是真正的DOS头部,由于其开始处的两个字节为"MZ",因此DOS头也可以叫作MZ头部

这个我们用十六进制编辑器随便打开一个exe就可以看到

该部分用于程序在DOS系统下加载,它的结构被定义为IMAGE_DOS_HEADER

IMAGE_DOS_HEADER定义

 1 //大小为: 0x40(64)字节
 2  #define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ
 3  
 4 typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
 5     WORD   e_magic;                     // MZ标记 0x5a4d 
 6     WORD   e_cblp;                      // 最后(部分)页中的字节数
 7     WORD   e_cp;                        // 文件中的全部和部分页数
 8     WORD   e_crlc;                      // 重定位表中的指针数
 9     WORD   e_cparhdr;                   // 头部尺寸以段落为单位
10     WORD   e_minalloc;                  // 所需的最小附加段
11     WORD   e_maxalloc;                  // 所需的最大附加段
12     WORD   e_ss;                        // 初始的SS值(相对偏移量)
13     WORD   e_sp;                        // 初始的SP值
14     WORD   e_csum;                      // 补码校验值
15     WORD   e_ip;                        // 初始的IP值
16     WORD   e_cs;                        // 初始的SS值
17     WORD   e_lfarlc;                    // 重定位表的字节偏移量
18     WORD   e_ovno;                      // 覆盖号
19     WORD   e_res[4];                    // 保留字
20     WORD   e_oemid;                     // OEM标识符(相对m_oeminfo)
21     WORD   e_oeminfo;                   // OEM信息
22     WORD   e_res2[10];                  // 保留字
23     LONG   e_lfanew;                    // NT头(PE标记)相对于文件的偏移地址
24   } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

DOS存根是一段简单的程序,主要用于输出“This program cannot be run in DOS mode.”类似的提示字符串。

为什么PE结构的最开始位置有这样一段DOS头部呢?

为了该可执行程序可以兼容DOS系统。通常情况下,Win32下的PE程序不能在DOS下运行,因此保留了这样一个简单的DOS程序用于提示“不能运行于DOS模式下”。

DOS头部IMAGE_DOS_HEADER详解

IMAGE_DOS_HEADER的定义在前面我们列出来了,该结构体中需要掌握的字段 只有两个分别是第一个字段 e_magic和最后一个字段e_lfanew

e_magic:DOS可执行文件的标识,占用2字节,该位置保存着字符是“MZ",该标识符在Winnt.h头文件中有一个宏定义,如下所示:

1 #define IMAGE_DOS_SIGNATURE 0x5A4D

我们创建一个简单的控制台程序

1 #include <iostream>
2 
3 int main()
4 {
5     std::cout << "Hello World!\n";
6 }

使用16进制编辑器(我这里用的是ImHex,使用个人习惯的软件即可)打开编译出来的二进制文件(.exe)。

可以看到在0x00000000的位置保存着2字节的内容0x5A4DASCII的MZ)这里使用的是小尾(小端)方式存储,即高位保存高字节,低位保存低字节,所以上图中写的是4D 5A,这也是适合阅读顺序。

----------------------------------------------

说明:

  • 大端模式(Big-Endian):在内存中,多字节数据类型的高位字节存储在低地址处,而低位字节存储在高地址处。这种模式与我们阅读数字的方式相似,即先读高位,后读低位。

  • 小端模式(Little-Endian):在内存中,多字节数据的低位字节存储在低地址处,而高位字节存储在高地址处。这种模式与我们阅读数字的方式相反,即先读低位,后读高位。

如0x0102这样一个数据,

使用大端方式存储,存储方式为:01 02

使用小端方式存储,存储方式为:02 01

我们可以看到下面这样一段程序

 1 #include <iostream>
 2 #include<Windows.h>
 3 #include<winnt.h>
 4 
 5 int main()
 6 {
 7     
 8     HANDLE hFile = CreateFile(L"a.bin", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 9 
10     BYTE buffer[4] = { 1,2,3,4 };  //写入 1 2 3 4 4个字节
11 
12     DWORD dwHexValue = 0x5D4A; //写入0x5D4A(实际存储是4A 5D)
13     DWORD dwDecValue = 1234;   //写入1234  (0x04D2 实际存储D2 04)
14 
15     LPOVERLAPPED lv{};
16     if (hFile)
17     {
18         WriteFile(hFile, buffer, 4, NULL, NULL);
19         WriteFile(hFile, (PVOID)&dwHexValue, 4, NULL, NULL);
20         WriteFile(hFile, (PVOID)&dwDecValue, 4, NULL, NULL);
21         CloseHandle(hFile);
22     }
23 }

用十六进制编辑器打开可以看到

为什么是这种情况,因为对于 Microsoft Visual C++ 的目标平台(x86、x64、ARM、ARM64),所有本机标量类型都是小字节序。

-------------------------

好的,让我们继续回归主题,在0x0000003C 位置处,也就是IMAGE_DOS_HEADERe_lfanew字段,该字段保存着PE头部的起始位置。

说明:IMAGE_DOS_HEADER的大小是64个字节,也就是0-63(0x3F),e_flanew的大小是4,所以 0x3F - 0x04 + 0x01 = 0x3C 

e_lfanew字段是LONG类型,所以这里是4个字节,值为F8 00 00 00,因为是使用的小端字节序,所以我们可以在0x000000F8位置,看到50 45 00 00,与之对应的ASCII字符为”PE\0\0“,这里就是PE头部开始的位置。

IMAGE_DOS_HEADER(e_lfanew字段之后)到"PE\0\0"之间的内容就是DOS存档,可以将该部分删除,然后将PE头部整体向前移动,也可以将一些配置数据保存在此处等。

我们将这里全部填充为0,程序也是可以正常执行的。

我写了下面这样一段测试程序:

 1 #include <iostream>
 2 #include<Windows.h>
 3 #include<winnt.h>
 4 
 5 int main()
 6 {
 7     
 8     HANDLE hFile = CreateFile(L"exepath", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 9 
10     if (hFile)
11     {
12         SetFilePointer(hFile, 0x00000040, NULL, FILE_BEGIN);
13 
14         BYTE buffer[8] = { 1,2,3,4,5,6,7,8 };
15         WriteFile(hFile, buffer, 8, NULL, NULL);
16 
17         CloseHandle(hFile);
18     }    
19 }

e_lfanew字段之后写入了8个字节的数据,程序也是可以照常执行的。

PE 头

DOS头是为了兼容DOS系统而遗留的,DOS头的最后一个字段(e_lfanew)给出了PE头的位置。

PE头部保存着Windows系统加载可执行文件的重要信息(用来装载Win32程序)。它的结构被定义为IMAGE_NT_HEADERS

IMAGE_NT_HEADERS是一个宏,它分为32位和64位版本。

IMAGE_NT_HEADERS64定义如下:

1 typedef struct _IMAGE_NT_HEADERS64 {
2     DWORD Signature;
3     IMAGE_FILE_HEADER FileHeader;
4     IMAGE_OPTIONAL_HEADER64 OptionalHeader;
5 } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

IMAGE_NT_HEADERSIMAGE_NT_SIGNATUREIMAGE_FILE_HEADERIMAGE_OPTIONAL_HEADER三部分组成。

Signature

SignatureIMAGE_NT_HEADERS的第一个字段, Signature是PE标识符,标识该文件是否是PE文件。占用4个字节,即 50 45 00 00

Signature在winnt.h中有一个宏定义IMAGE_NT_SIGNATURE

IMAGE_NT_SIGNATURE定义如下:

1 #define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

这个值非常重要。

如果要简单的判断一个文件是否是PE文件,先判断DOS头部的开始字节是否是MZ,再根据DOS头,找到PE头,判断PE头前四个字节是否是“PE\0\0”。如果是的话,就说明该文件是一个有效的PE文件。

文件头IMAGE_FILE_HEADER

IMAGE_FILE_HEADERIMAGE_NT_HEADERS结构体中的一个结构,紧接在PE标识符(Signature字段)的后面。

IMAGE_FILE_HEADER占用20个字节

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:WORD 2字节,该字段表示可执行文件的目标CPU类型,取值如下:

ValueMeaning

IMAGE_FILE_MACHINE_I386

0x014c

x86

IMAGE_FILE_MACHINE_IA64

0x0200

Intel Itanium

IMAGE_FILE_MACHINE_AMD64

0x8664

x64

这里我们编译的是一个x64版本,可以在十六进制编辑器中看到64 86

NumberOfSections:WORD 2字节,该字段表示PE文件的节区的个数.请注意,Windows 加载程序将节区限制为 96。

该字段的值为06 00,即为0x00000006,表示该PE文件的节区有6个。

TimeDataStamp:DWORD 4字节,该字段表示 文件是何时被创建的,这个值是自1970/1/1以来用格林威治时间计算的秒数。

 界面上可以看到这个字段的取值是0x6641BD1E,我们写一个简单的程序计算一下

1  static void Main(string[] args)
2  {
3      var dt = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(0x6641BD1E).AddHours(8);
4      Console.WriteLine(dt.ToString("yyyy-MM-dd HH:mm:ss"));
5  }

PointerToSymbolTable:DWORD 4字节,符号表的偏移量(以字节为单位),如果没有 COFF 符号表,则为零。

NumberOfSymbols:DWORD 4字节,符号表中的符号数。

SizeOfOptionalHeader:WORD 2字节,该字段 指定IMAGE_OPTIONAL_HEADER结构的大小。

我们这里的值是F0 00,也就是0x000000F0。

注意:在计算IMAGE_OPTIONAL_HEADER的大小时,应该取SizeOfOptionalHeader的值,而不是使用sizeof()函数。

因为IMAGE_OPTIONAL_HEADER结构体的大小可能是会改变的。

Characteristics:WORD 2字节,指定文件的类型。取值如下:

ValueMeaning

IMAGE_FILE_RELOCS_STRIPPED

0x0001

Relocation information was stripped from the file. The file must be loaded at its preferred base address. If the base address is not available, the loader reports an error.

IMAGE_FILE_EXECUTABLE_IMAGE

0x0002

The file is executable (there are no unresolved external references).

IMAGE_FILE_LINE_NUMS_STRIPPED

0x0004

COFF line numbers were stripped from the file.

IMAGE_FILE_LOCAL_SYMS_STRIPPED

0x0008

COFF symbol table entries were stripped from file.

IMAGE_FILE_AGGRESIVE_WS_TRIM

0x0010

Aggressively trim the working set. This value is obsolete.

IMAGE_FILE_LARGE_ADDRESS_AWARE

0x0020

The application can handle addresses larger than 2 GB.

IMAGE_FILE_BYTES_REVERSED_LO

0x0080

The bytes of the word are reversed. This flag is obsolete.

IMAGE_FILE_32BIT_MACHINE

0x0100

The computer supports 32-bit words.

IMAGE_FILE_DEBUG_STRIPPED

0x0200

Debugging information was removed and stored separately in another file.

IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP

0x0400

If the image is on removable media, copy it to and run it from the swap file.

IMAGE_FILE_NET_RUN_FROM_SWAP

0x0800

If the image is on the network, copy it to and run it from the swap file.

IMAGE_FILE_SYSTEM

0x1000

The image is a system file.

IMAGE_FILE_DLL

0x2000

The image is a DLL file. While it is an executable file, it cannot be run directly.

IMAGE_FILE_UP_SYSTEM_ONLY

0x4000

The file should be run only on a uniprocessor computer.

IMAGE_FILE_BYTES_REVERSED_HI

0x8000

The bytes of the word are reversed. This flag is obsolete.

 我们这里是22 00 ,也就是0x00000022,这是一个组合值(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE),代表这是一个可执行文件,且能处理超过2GB的内存。

可选头IMAGE_OPTIONAL_HEADER

可选头IMAGE_OPTIONAL_HEADERIMAGE_NT_HEADERS结构体中的一个结构,紧接在IMAGE_FILE_HEADER类型之后。

可选头是对文件头的一个补充。文件头主要描述文件的相关信息,而可选头主要用来管理PE文件被操作系统装载时所需要的信息。

IMAGE_OPTIONAL_HEADER在现在的大部分资料中都被称为可选头。但该头部实际上是一个必须存在的头。

所以这里应该是最初在翻译时或者其它方面导致的错误,后人一直沿用了。Option也有选项之类的意思 。

IMAGE_OPTIONAL_HEADER的大小在IMAGE_FILE_HEADERSizeOfOptionalHeader字段中给出。

我们这里的值是F0 00,也就是IMAGE_OPTIONAL_HEADER的大小是0x000000F0

IMAGE_OPTIONAL_HEADER(可选头)在IMAGE_FILE_HEADER(文件头)之后

IMAGE_FILE_HEADER的结束位置在0x000010F,那么可选头的起始位置是0x00000110

通过下面的图可以比较清晰的看到

可选头的结束位置是0x00000110 + 0x000000F0 -1 = 0x000001FF,如下图所示

通常情况下,可选头的结尾后面跟的是第一项节表的名称,就是值为.text的位置。

如上图所示:文件偏移0x0000200处的节点名称为".text",也就是说,可选头的结束位置在0x0000200偏移的前一字节,即0x00001FF

下面我们来详细看一下IMAGE_OPTIONAL_HEADER的定义

IMAGE_OPTIONAL_HEADER实际是一个宏,定义如下:

1 #ifdef _WIN64
2 typedef IMAGE_OPTIONAL_HEADER64             IMAGE_OPTIONAL_HEADER;
3 #else
4 typedef IMAGE_OPTIONAL_HEADER32             IMAGE_OPTIONAL_HEADER;
5 #endif

这里我们以x64为例,所以使用的是IMAGE_OPTIONAL_HEADER64类型,定义如下

 1 typedef struct _IMAGE_OPTIONAL_HEADER64 {
 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     ULONGLONG   ImageBase;
11     DWORD       SectionAlignment;
12     DWORD       FileAlignment;
13     WORD        MajorOperatingSystemVersion;
14     WORD        MinorOperatingSystemVersion;
15     WORD        MajorImageVersion;
16     WORD        MinorImageVersion;
17     WORD        MajorSubsystemVersion;
18     WORD        MinorSubsystemVersion;
19     DWORD       Win32VersionValue;
20     DWORD       SizeOfImage;
21     DWORD       SizeOfHeaders;
22     DWORD       CheckSum;
23     WORD        Subsystem;
24     WORD        DllCharacteristics;
25     ULONGLONG   SizeOfStackReserve;
26     ULONGLONG   SizeOfStackCommit;
27     ULONGLONG   SizeOfHeapReserve;
28     ULONGLONG   SizeOfHeapCommit;
29     DWORD       LoaderFlags;
30     DWORD       NumberOfRvaAndSizes;
31     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
32 } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

成员说明:

Magic:WORD 2字节,文件的状态类型,它可以是以下值之一

含义

IMAGE_NT_OPTIONAL_HDR_MAGIC

该文件是可执行映像。 此值在 32 位应用程序中定义为 IMAGE_NT_OPTIONAL_HDR32_MAGIC ,在 64 位应用程序中 定义为IMAGE_NT_OPTIONAL_HDR64_MAGIC 。

IMAGE_NT_OPTIONAL_HDR32_MAGIC

0x10b

该文件是可执行映像。

IMAGE_NT_OPTIONAL_HDR64_MAGIC

0x20b

该文件是可执行映像。

IMAGE_ROM_OPTIONAL_HDR_MAGIC

0x107

该文件是 ROM 映像。

 我们这里的值是0B 02,实际取值是0x0000020B,所以这是一个64位的可执行文件。

MajorLinkerVersion:链接器的主版本号。

MinorLinkerVersion:链接器的次版本号

SizeOfCode:代码节的大小(以字节为单位),如果有多个代码块,则为所有此类块的总和。

SizeOfInitializedData:初始化的数据块的大小(以字节为单位),如果有多个已初始化的数据块,则为所有此类块的总和。

SizeOfUninitializedData:未初始化数据块的大小(以字节为单位),如果有多个未初始化的数据块,则为所有此类块的总和。

AddressOfEntryPoint:指向入口点函数(相对于映像基址)的指针。 对于可执行文件,这是起始地址。 对于设备驱动程序,这是初始化函数的地址。 入口点函数对于 DLL 是可选的。 如果没有入口点,则此成员为零。

BaseOfCode:指向代码部分开头(相对于映像基址)的指针。

ImageBase:文件被装入内存后的首选建议装载地址。对于EXE文件来说,通常情况下,该地址就是装载地址;对于DLL文件来说,可能就不是其装入内存后的地址了。

SectionAlignment:节表被装入内存后的对齐值。节表被映射到内存中需要对其的单位。在Win32下,通常情况下,该值为0x1000,也就是4KB大小。Windows操作系统的内存分页一般为4KB。此值必须大于或等于 FileAlignment 成员。

FileAlignment:节表在文件中的对齐值。通常情况下,该值为0x1000或0x200。在文件对齐值为0x1000时,由于与内存对齐值相同,可以加快装载速度。

而文件对齐值为0x200时,可以占用相对较少的磁盘空间。0x200是512字节,通常磁盘的一个扇区即为512字节。

说明:

程序无论是在内存中还是磁盘上,都无法恰好满足SectionAlignment和FileAlignment值的倍数,在不足的情况下需要补0值,这样就导致节与节之间存在了无用的空隙。这些空隙对于病毒之类程序而言就有了可利用的价值。

MajorOperatingSystemVersion:要求最低操作系统的主版本号。

MinorOperatingSystemVersion:要求最低操作系统的次版本号。

MajorImageVersion:可执行文件的主版本号。

MinorImageVersion:可执行文件的次版本号。

MajorSubsystemVersion子系统的主版本号。

MinorSubsystemVersion子系统的次要版本号。

Win32VersionValue:此成员为保留成员,必须为 0。

SizeOfImage:可执行文件装入内存后的总大小。该大小按内存对齐方式(SectionAlignment )对齐。

SizeOfHeaders:整个PE头部的大小。这个PE头部泛指DOS头、PE头、节表的总和大小。舍入为 FileAlignment 成员中指定的值的倍数。

CheckSum:校验和值。对于EXE文件通常为0;对于SYS文件,则必须有一个校验和。

SubSystem:运行此映像所需的子系统。 定义了以下值。

含义

IMAGE_SUBSYSTEM_UNKNOWN

0

未知子系统。

IMAGE_SUBSYSTEM_NATIVE

1

无需子系统 (设备驱动程序和本机系统进程) 。

IMAGE_SUBSYSTEM_WINDOWS_GUI

2

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

IMAGE_SUBSYSTEM_WINDOWS_CUI

3

Windows 字符模式用户界面 (CUI) 子系统。

IMAGE_SUBSYSTEM_OS2_CUI

5

OS/2 CUI 子系统。

IMAGE_SUBSYSTEM_POSIX_CUI

7

POSIX CUI 子系统。

IMAGE_SUBSYSTEM_WINDOWS_CE_GUI

9

Windows CE系统。

IMAGE_SUBSYSTEM_EFI_APPLICATION

10

可扩展固件接口 (EFI) 应用程序。

IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER

11

具有启动服务的 EFI 驱动程序。

IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER

12

具有运行时服务的 EFI 驱动程序。

IMAGE_SUBSYSTEM_EFI_ROM

13

EFI ROM 映像。

IMAGE_SUBSYSTEM_XBOX

14

Xbox 系统。

IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION

16

启动应用程序。

DllCharacteristics:指定DLL文件的特征,该值大部分时候为0,系统定义了以下值

含义

0x0001

保留。

0x0002

保留。

0x0004

保留。

0x0008

保留。

IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE

0x0040

可以在加载时重新定位 DLL。

IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY

0x0080

强制进行代码完整性检查。 如果设置了此标志,并且某个节仅包含未初始化的数据,请将该节的 IMAGE_SECTION_HEADER 的 PointerToRawData 成员设置为零;否则,映像将无法加载,因为无法验证数字签名。

IMAGE_DLLCHARACTERISTICS_NX_COMPAT

0x0100

该映像与 DEP) (数据执行防护兼容。

IMAGE_DLLCHARACTERISTICS_NO_ISOLATION

0x0200

映像可感知隔离,但不应隔离。

IMAGE_DLLCHARACTERISTICS_NO_SEH

0x0400

映像不使用 SEH) (结构化异常处理。 在此映像中不能调用任何处理程序。

IMAGE_DLLCHARACTERISTICS_NO_BIND

0x0800

请勿绑定映像。

0x1000

保留。

IMAGE_DLLCHARACTERISTICS_WDM_DRIVER

0x2000

WDM 驱动程序。

0x4000

保留。

IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE

0x8000

映像是终端服务器感知的。

SizeOfStackReserve:要为堆栈保留的字节数。 加载时只提交 由 SizeOfStackCommit 成员指定的内存;其余部分一次提供一页,直到达到此保留大小。

SizeOfStackCommit:要为堆栈提交的字节数。

SizeOfHeapReserve:要为本地堆保留的字节数。 加载时仅提交 由 SizeOfHeapCommit 成员指定的内存;其余部分一次提供一页,直到达到此保留大小。

SizeOfHeapCommit:要为本地堆提交的字节数。

LoaderFlags:此成员已过时。

NumberOfRvaAndSizes:数据目录项的个数。该个数在PSDK中有一个宏定义,

定义如下:

1 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

DataDirectory:数据目录表,由NumberOfRvaAndSizesIMAGE_DATA_DIRECTORY结构体组成。该数组包含输入表、输出表、资源、重定位等数据目录项的RVA(相对虚拟地址)和大小。IMAGE_DATA_DIRECTORY结构体的定义如下:

1 typedef struct _IMAGE_DATA_DIRECTORY {
2     DWORD   VirtualAddress;
3     DWORD   Size;
4 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

成员说明:

VirtualAddress:该目录项的相对虚拟地址的起始值。

Size:目录项的长度。

数据目录中的成员在数据中的索引如下定义所示:

 1 #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
 2 #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
 3 #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
 4 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
 5 #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
 6 #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
 7 #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
 8 //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
 9 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
10 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
11 #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
12 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
13 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
14 #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
15 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
16 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

在数据目录中,并不是所有的目录项都会有值很多目录项的值都为0。因为很多目录项的值为0,所以说数据目录项是可选的。

可选头的成员介绍完毕了,因为这里涉及的成员较多,我就没有一个一个在十六进制 编辑器中进行匹配了。后面我们在使用代码进行读取的时候,再进行展示 。

IMAGE_SECTION_HEADER详解

节表的位置在IMAGE_OPTIONAL_HEADER(可选头)后面。节表中的每个IMAGE_SECTION_HEADER中都存放着可执行文件被映射到内存中所在位置的信

息,节的个数由IMAGE_FILE_HEADER中的NumberOfSections给出。这个值我们在前面读取过,它是06 00,也就是0x00000006,所以该文件有6个节表。

IMAGE_SECTION_HEADER的大小为40字节,所以节表总共占用240个字节。

所以节表的起始位置在0x00000200,终止位置在0x00000200 + 0x000000F0(240) -1 = 0x000002EF处。如下图所示

 IMAGE_SECTION_HEADER结构体的定义如下

 1 typedef struct _IMAGE_SECTION_HEADER {
 2     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
 3     union {
 4             DWORD   PhysicalAddress;
 5             DWORD   VirtualSize;
 6     } Misc;
 7     DWORD   VirtualAddress;
 8     DWORD   SizeOfRawData;
 9     DWORD   PointerToRawData;
10     DWORD   PointerToRelocations;
11     DWORD   PointerToLinenumbers;
12     WORD    NumberOfRelocations;
13     WORD    NumberOfLinenumbers;
14     DWORD   Characteristics;
15 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

成员介绍:

Name:节表项的名称,节的名称用ASCII编码来保存。节名称的长度为IMAGE_SIZE_SHORT_NAME(8个字节),这是一个宏,定义如下:

1 #define IMAGE_SIZEOF_SHORT_NAME              8

如果字符串长度正好为 8 个字符,则没有终止字符。否则最后一个字符为终止字符。 对于较长的名称,此成员包含 (/) 的正斜杠,后跟十进制数的 ASCII 表示形式,该数字是字符串表中的偏移量。 可执行文件不使用字符串表,并且不支持长度超过 8 个字符的节名称。

我们这里第一个节表项前面8个字节的数据为2E 74 65 78 74 00 00 00,对应的字符为 “.text”

Misc

Misc是一个联合体,它有以下两个成员

Misc.PhysicalAddress

文件地址。

Misc.VirtualSize

加载到内存中的节的总大小(以字节为单位)。 如果此值大于 SizeOfRawData 成员,则节将填充零。

此字段仅对可执行文件有效,对于对象文件,应设置为 0。

SizeOfRawData:该值为数据实际的节表项大小。 此值必须是 IMAGE_OPTIONAL_HEADER 结构的 FileAlignment 成员的倍数。 如果此值小于 VirtualSize 成员,则部分的其余部分将填充零。 如果节仅包含未初始化的数据,则成员为零。

PointerToRawData:该节表项在磁盘文件上的偏移地址。此值必须是 IMAGE_OPTIONAL_HEADER 结构的 FileAlignment 成员的倍数。 如果节仅包含未初始化的数据,请将此成员设置为零。

PointerToRelocations

A file pointer to the beginning of the relocation entries for the section. If there are no relocations, this value is zero.

这里的翻译总感觉差点意思 ,直接贴MSDN上的英文原版。

PointerToLinenumbers

A file pointer to the beginning of the line-number entries for the section. If there are no COFF line numbers, this value is zero.

NumberOfRelocations

该节表项重定向的条数。如果是可执行文件,该值应该设置为0

NumberOfLinenumbers

The number of line-number entries for the section.

Characteristics

节表项的属性,定义了以下值:

标志含义

0x00000000

保留。

0x00000001

保留。

0x00000002

保留。

0x00000004

保留。

IMAGE_SCN_TYPE_NO_PAD

0x00000008

不应将节填充到下一个边界。 此标志已过时,由 IMAGE_SCN_ALIGN_1BYTES 取代。

0x00000010

保留。

IMAGE_SCN_CNT_CODE

0x00000020

节包含可执行代码。

IMAGE_SCN_CNT_INITIALIZED_DATA

0x00000040

节包含初始化数据。

IMAGE_SCN_CNT_UNINITIALIZED_DATA

0x00000080

节包含未初始化数据。

IMAGE_SCN_LNK_OTHER

0x00000100

保留。

IMAGE_SCN_LNK_INFO

0x00000200

节包含注释或其他信息。 它仅对对象文件有效。

0x00000400

保留。

IMAGE_SCN_LNK_REMOVE

0x00000800

节不会成为映像的一部分。 它仅对对象文件有效。

IMAGE_SCN_LNK_COMDAT

0x00001000

节包含 COMDAT 数据。 它仅对对象文件有效。

0x00002000

保留。

IMAGE_SCN_NO_DEFER_SPEC_EXC

0x00004000

重置本部分的 TLB 条目中处理位的推理异常。

IMAGE_SCN_GPREL

0x00008000

节包含通过全局指针引用的数据。

0x00010000

保留。

IMAGE_SCN_MEM_PURGEABLE

0x00020000

保留。

IMAGE_SCN_MEM_LOCKED

0x00040000

保留。

IMAGE_SCN_MEM_PRELOAD

0x00080000

保留。

IMAGE_SCN_ALIGN_1BYTES

0x00100000

在 1 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_2BYTES

0x00200000

在 2 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_4BYTES

0x00300000

在 4 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_8BYTES

0x00400000

对齐 8 字节边界上的数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_16BYTES

0x00500000

在 16 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_32BYTES

0x00600000

在 32 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_64BYTES

0x00700000

在 64 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_128BYTES

0x00800000

在 128 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_256BYTES

0x00900000

在 256 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_512BYTES

0x00A00000

在 512 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_1024BYTES

0x00B00000

在 1024 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_2048BYTES

0x00C00000

在 2048 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_4096BYTES

0x00D00000

在 4096 字节边界上对齐数据。 它仅对对象文件有效。

IMAGE_SCN_ALIGN_8192BYTES

0x00E00000

对齐 8192 字节边界上的数据。 它仅对对象文件有效。

IMAGE_SCN_LNK_NRELOC_OVFL

0x01000000

节包含扩展重定位。 节的重定位计数超过了节标头中为其保留的 16 位。 如果节标题中的 NumberOfRelocations 字段0xffff,则实际重定位计数将存储在第一次重定位的 VirtualAddress 字段中。 如果设置了 IMAGE_SCN_LNK_NRELOC_OVFL,并且节中的重定位数少于 0xffff,则为错误。

IMAGE_SCN_MEM_DISCARDABLE

0x02000000

可以根据需要丢弃节。

IMAGE_SCN_MEM_NOT_CACHED

0x04000000

无法缓存节。

IMAGE_SCN_MEM_NOT_PAGED

0x08000000

该节不能分页。

IMAGE_SCN_MEM_SHARED

0x10000000

可以在内存中共享节。

IMAGE_SCN_MEM_EXECUTE

0x20000000

节可以作为代码执行。

IMAGE_SCN_MEM_READ

0x40000000

可以读取节。

IMAGE_SCN_MEM_WRITE

0x80000000

可以写入节。

测试文件

本文所用到的exe文件,可以在这里下载

参考资料

pe format

PE Format - Win32 apps | Microsoft Learn

PE-Portable-executable - aldeid

endian

endian enum | Microsoft Learn

IMAGE_FILE_HEADER介绍

IMAGE_FILE_HEADER (winnt.h) - Win32 apps | Microsoft Learn

IMAGE_OPTIONAL_HEADER介绍

IMAGE_OPTIONAL_HEADER64 (winnt.h) - Win32 apps | Microsoft Learn

IMAGE_SECTION_HEADER介绍

IMAGE_SECTION_HEADER (winnt.h) - Win32 apps | Microsoft Learn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2093155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++基础(1)——入门知识

目录 1.C版本更新 2.C参考⽂档&#xff1a; 3.C书籍推荐 4.C的第⼀个程序 5.命名空间 5.1namespace的价值 5.2namespace的定义 5.3 命名空间使⽤ 6.C输⼊&输出 7.缺省参数 8.函数重载 9.引⽤ 9.1引⽤的概念和定义 9.2引⽤的特性 9.3引⽤的使用 9.4const引⽤…

YOLOv5独家改进:一种高效移动应用的卷积加性自注意Vision Transformer

💡💡💡本文独家改进:高效移动应用的卷积加性自注意Vision Transformer,构建了一个新颖且高效实现方式——卷积加性相似度函数,并提出了一种名为卷积加性标记混合器(CATM) 的简化方法来降低计算开销 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/cat…

opencv/c++的一些简单的操作(入门)

目录 读取图片 读取视频 读取摄像头 图像处理 腐蚀 膨胀 调整图像大小 裁剪和缩放 绘制 绘制矩形 绘制圆形 绘制线条 透视变换 颜色检测 轮廓查找 人脸检测 检测人脸 检测嘴巴 可适当调整参数 读取图片 读取路径widows使用vis sto一定是\斜杠 #include <o…

LoRA 和 DoRA 代码笔记

Improving LoRA: Implementing Weight-Decomposed Low-Rank Adaptation (DoRA) from Scratch LoRA LoRA初始化时&#xff0c;A使用正态分布&#xff0c;B使用0. class LoRALayer(nn.Module):def __init__(self, in_dim, out_dim, rank, alpha):super().__init__()std_dev 1…

第L1周:机器学习-数据预处理

第L1周&#xff1a;机器学习-数据预处理 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 学习要点&#xff1a; **** 学习如何处理缺损数据尝试进行Label编码使用train_test_split进行数据划分学习特征标准化…

EXO项目StandardNode;max_generate_tokens;buffered_token_output;is_finished;

目录 StandardNode max_generate_tokens buffered_token_output 构造函数参数 类属性 总结 is_finished max_generate_tokens self.buffered_token_output StandardNode _process_tensor result是一个np.ndarray ,result.size == 1是什么意思 StandardNode max_g…

【Python机器学习】NLP词频背后的含义——反馈及改进

之前学习的LSA方法都没有考虑文档之间的相似度信息&#xff0c;创建的主题对一组通用规则来说是最优的。在这些特征&#xff08;主题&#xff09;提取模型的无监督学习中&#xff0c;没有任何关于主题向量之间应该多么接近的数据。我们也不允许任何关于主题向量在哪里结束或者它…

力扣刷题(复习版1)

文章目录 题目&#xff1a;最大重复子字符串题解 题目&#xff1a; 面试题 16.07. 最大数值题解 题目&#xff1a; 最大字符串配对数目题解 题目&#xff1a; 字符串中第二大的数字题解 题目&#xff1a; 统计最大组的数目题解 题目&#xff1a; 删除每行中的最大值题解 题目&a…

记录|Chart控件使用

目录 前言一、Series集合1.1 什么是Series1.2 IsValueShownAsLabel1.3 Points 二、ChartArea2.1 轴- Axes2.1.1 Title2.1.2 刻度下的Maximum、Minimum2.1.3 间隔- Interval2.1.4 网格刻度线 2.2 游标- CursorX/CursorYMajorGrid属性中的Enabled 更新时间 前言 参考视频&#xf…

七、库存管理——盘点业务

第一节 库存管理盘点 1、盘点的目的 答&#xff1a;是指通过定期或临时对库存商品的实际数量进行盘查、清点&#xff0c;然后掌握货物的流动情况&#xff08;入库、出库、调拨、在库等&#xff09;&#xff0c;最后对仓库现有物品的实际数量与保管账上记录的数量相核对&…

Spring Boot集成Stripe快速入门demo

1.什么是Stripe&#xff1f; 一体化全球支付平台&#xff0c;开启收入增长引擎&#xff0c;针对不同规模业务打造的支付解决方案&#xff0c;满足从初创公司到跨国企业的多维度需求&#xff0c;助力全球范围内线上线下付款。 转化更多客户: 通过内置的优化功能、100 多种支付…

QT QGraphicsView实现预览图片显示缩略图功能

QT QGraphicsView实现预览图片显示缩略图功能QT creator Qt5.15.2 头文件&#xff1a; #ifndef TGRAPHICSVIEW_H #define TGRAPHICSVIEW_H#include <QGraphicsView> #include <QMainWindow> #include <QObject> #include <QWidget>class TGraphicsVie…

vue页面自适应 动态 postcss postcss-pxtorem

vue页面自适应 动态 postcss postcss-pxtorem postcss-pxtorem实现页面自适应1、安装postcss-pxtorem2、根目录创建postcss.config.js&#xff0c;并配置以下内容3、创建rem.js&#xff0c;动态设置root px4、在main.js中引入rem.js5、在main.js中创建全局处理函数px2rem6、对…

【王树森】Vision Transformer (ViT) 用于图片分类(个人向笔记)

图片分类任务 给定一张图片&#xff0c;现在要求神经网络能够输出它对这个图片的分类结果。下图表示神经网络有40%的信心认定这个图片是狗 ResNet&#xff08;CNN&#xff09;曾经是是图像分类的最好模型在有足够大数据做预训练的情况下&#xff0c;ViT要强于ResNetViT 就是Tr…

S7-200编程软件STEP 7打开时界面乱码显示Translation Required

遇到的问题 如题&#xff0c;两个月没有打开过S7-200编程软件&#xff08;软件版本是V4.0 STEP 7 MicroWIN SP9&#xff0c;电脑系统是Windows 11&#xff09;&#xff0c;这一次打开就发现它的那个界面乱码了&#xff0c;原来时中文汉化的地方全都变成了Translation Required…

笔记整理—内核!启动!—uboot部分(1)

常规启动时&#xff0c;各镜像都在SD卡中的各种分区中&#xff0c;内核放在kernel分区&#xff0c;从SD卡到DDR的连接处&#xff08;内核不需要进行重定位&#xff0c;直接从链接处启动&#xff09;。uboot从sd卡分区读使用movi命令。 使用fastboot指令可以查看分区情况&#x…

通过Dot1q终结子接口实现VLAN间互访

如图1所示&#xff0c;SwitchA为支持配置子接口的三层交换机&#xff0c;SwitchB为二层交换机&#xff0c;SwitchA通过一个三层以太网接口与SwitchB互连。用户主机被划分到两个VLAN&#xff1a;VLAN2和VLAN3。由于业务需要&#xff0c;不同VLAN的用户要求互通。 图1 通过Dot1q…

AI革命:清华大学揭秘大模型工具学习的未来

&#x1f31f; 未来已来&#xff1a;大模型工具学习开启智能新时代 &#x1f31f; 清华大学THUNLP最新研究&#xff0c;探索AI工具使用的无限可能 文末有报告免费下载&#xff0c;需要的朋友自行下跳。 亲爱的读者朋友们&#xff0c;人工智能的浪潮已经不可阻挡地涌入我们的…

LabVIEW VI并行执行设置

要在多个程序中运行同一个VI&#xff08;Virtual Instrument&#xff09;&#xff0c;通常需要确保VI的重入性&#xff08;Reentrancy&#xff09;设置正确。在LabVIEW中&#xff0c;可以使用“重入性”&#xff08;Reentrancy&#xff09;选项来允许同一个VI同时在多个地方调用…

RAG噪声的设计及其对大模型问答的作用分析

有趣的大模型中RAG噪声的作用分析 大模型&#xff08;LLMs&#xff09;在多个任务上表现出色&#xff0c;但存在依赖过时知识、幻觉等问题。RAG作为一种提高LLM性能的方法&#xff0c;通过在推理过程中引入外部信息来缓解这些限制。 Figure 1 展示了一个来自 NoiserBench 的示…