[PE结构分析] 5.IMAGE_OPTIONAL_HEADER

结构体源代码如下:

typedef struct _IMAGE_OPTIONAL_HEADER
{
    //
    // Standard fields.
    //
+18h    WORD    Magic;                   // 标志字, ROM 映像(0107h),普通可执行文件(010Bh)
+1Ah    BYTE    MajorLinkerVersion;      // 链接程序的主版本号
+1Bh    BYTE    MinorLinkerVersion;      // 链接程序的次版本号
+1Ch    DWORD   SizeOfCode;              // 所有含代码的节的总大小
+20h    DWORD   SizeOfInitializedData;   // 所有含已初始化数据的节的总大小
+24h    DWORD   SizeOfUninitializedData; // 所有含未初始化数据的节的大小
+28h    DWORD   AddressOfEntryPoint;     // 程序执行入口RVA ***(必须了解)***
+2Ch    DWORD   BaseOfCode;              // 代码的区块的起始RVA
+30h    DWORD   BaseOfData;              // 数据的区块的起始RVA
    //
    // NT additional fields.    以下是属于NT结构增加的领域。
    //
+34h    DWORD   ImageBase;               // 程序的首选装载地址 ***(必须了解)***
+38h    DWORD   SectionAlignment;        // 内存中的区块的对齐大小 ***(必须了解)***
+3Ch    DWORD   FileAlignment;           // 文件中的区块的对齐大小 ***(必须了解)***
+40h    WORD    MajorOperatingSystemVersion;  // 要求操作系统最低版本号的主版本号
+42h    WORD    MinorOperatingSystemVersion;  // 要求操作系统最低版本号的副版本号
+44h    WORD    MajorImageVersion;       // 可运行于操作系统的主版本号
+46h    WORD    MinorImageVersion;       // 可运行于操作系统的次版本号
+48h    WORD    MajorSubsystemVersion;   // 要求最低子系统版本的主版本号
+4Ah    WORD    MinorSubsystemVersion;   // 要求最低子系统版本的次版本号
+4Ch    DWORD   Win32VersionValue;       // 莫须有字段,不被病毒利用的话一般为0
+50h    DWORD   SizeOfImage;             // 映像装入内存后的总尺寸
+54h    DWORD   SizeOfHeaders;           // 所有头 + 区块表的尺寸大小
+58h    DWORD   CheckSum;                // 映像的校检和
+5Ch    WORD    Subsystem;               // 可执行文件期望的子系统 ***(必须了解)***
+5Eh    WORD    DllCharacteristics;      // DllMain()函数何时被调用,默认为 0
+60h    DWORD   SizeOfStackReserve;      // 初始化时的栈大小
+64h    DWORD   SizeOfStackCommit;       // 初始化时实际提交的栈大小
+68h    DWORD   SizeOfHeapReserve;       // 初始化时保留的堆大小
+6Ch    DWORD   SizeOfHeapCommit;        // 初始化时实际提交的堆大小
+70h    DWORD   LoaderFlags;             // 与调试有关,默认为 0
+74h    DWORD   NumberOfRvaAndSizes;     // 下边数据目录的项数,这个字段自Windows NT 发布以来一直是16
+78h    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
// 数据目录表 ***(必须了解,重点)*** winNT发布到win10,IMAGE_NUMBEROF_DIRECTORY_ENTRIES一直都是16
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

AddressOfEntryPoint  ***(必须了解)***

程序开始执行的地址,这是一个RVA(相对虚拟地址)。对于exe文件,这里是启动代码;对于dll文件,这里是libMain()的地址。如果在一个可执行文件上附加了一段代码并想让这段代码首先被执行,那么只需要将这个入口地址指向附加的代码就可以了。在脱壳时第一件事就是找入口点,指的就是这个值。

ImageBase  ***(必须了解)***

PE文件的优先装入地址。也就是说,当文件被执行时,如果可能的话(当前地址没有被使用),Windows优先将文件装入到由ImageBase字段指定的地址中。

对于EXE文件来说,由于每个文件总是使用独立的虚拟地址空间,优先装入地址不可能被**模块占据,所以EXE总是能够按照这个地址装入

这也意味着EXE文件不再需要重定位信息。

对于DLL文件来说,由于多个DLL文件全部使用宿主EXE文件的地址空间,不能保证优先装入地址没有被**的DLL使用,所以DLL文件中必须包含重定位信息以防万一。

因此,在前面介绍的 IMAGE_FILE_HEADER 结构的 Characteristics 字段中,DLL 文件对应的 IMAGE_FILE_RELOCS_STRIPPED 位总是为0,而EXE文件的这个标志位总是为1。

如果没有指定的话,dll文件默认为0x10000000;exe文件默认为0x00400000,但是在Windows CE平台上是0x00010000。此值必须是64K bytes的倍数!

SectionAlignment ***(必须了解)***

内存中区块的对齐单位。区块总是对齐到这个值的整数倍。此字段必须大于或等于 FileAlignment ,默认值是系统页面的大小。32位cpu通常值为 0x1000(十六进制),即4096,即4KB。64位cpu通常为 8kB
FileAlignment ***(必须了解)***

pe文件中区块的对齐单位,以bytes(字节)为单位。此值必须是2的次方倍,但是必须在512和64K区间之间(闭区间[521, 64*1024=65536]),如果SectionAlignment小于系统页面的大小,那么SectionAlignment的大小就和FileAlignment相同。pe文件中默认值为 521 字节(0.5KB) 即 0x200(十六进制)。

Subsystem ***(必须了解)***

pe文件的用户界面使用的子系统类型。定义如下:

#define IMAGE_SUBSYSTEM_UNKNOWN              0   // 未知子系统
#define IMAGE_SUBSYSTEM_NATIVE               1   // 不需要子系统(如驱动程序)
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Windows GUI 子系统
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Windows 控制台子系统
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // OS/2 控制台子系统
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // Posix 控制台子系统
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // 镜像是原生 Win9x 驱动程序
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Windows CE 图形界面

例如,Visual Studio 2015中编译程序时可以在图形界面设置链接选项:

更多请查看:

微软官方文档:https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx

DataDirectory ***(必须了解,重要)***

这个字段可以说是最重要的字段之一,它由16个相同的IMAGE_DATA_DIRECTORY结构组成。其结构如下:

typedef struct _IMAGE_DATA_DIRECTORY {
   DWORD   VirtualAddress; // 相对虚拟地址 
   DWORD   Size;           // 数据块的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

也就是定义了某块的位置和大小。

虽然PE文件中的数据是按照装入内存后的页属性归类而被放在不同的节中的,但是这些处于各个节中的数据按照用途可以被分为导出表、导入表、资源、重定位表等数据块,这16个IMAGE_DATA_DIRECTORY结构就是用来定义多种不同用途的数据块的(如下表所示)。IMAGE_DATA_DIRECTORY结构的定义很简单,它仅仅指出了某种数据块的位置和长度。

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // 导出表
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // 导入表
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // 资源表
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // 异常表(具体资料不详)
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // 安全表(具体资料不详)
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // 重定位表
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // 调试表
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage) 版权信息
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // 版权信息
#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   // 导入函数地址表
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors(不详)
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor(不详)
时间: 2024-11-07 07:55:59

[PE结构分析] 5.IMAGE_OPTIONAL_HEADER的相关文章

[PE结构分析] 6.IMAGE_SECTION_HEADER

IMAGE_SECTION_HEADER 的源代码如下: typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节表名称,如“.text” //IMAGE_SIZEOF_SHORT_NAME=8 union { DWORD PhysicalAddress; // 在文件中的物理地址 DWORD VirtualSize; // 真实长度,这两个值是一个联合结构,可以使用其中的任何一个,一般是取后一

[PE结构分析] 9.导出表 IMAGE_EXPORT_DIRECTORY

typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // 未使用,总为0 DWORD TimeDateStamp; // 文件创建时间戳 WORD MajorVersion; // 未使用,总为0 WORD MinorVersion; // 未使用,总为0 DWORD Name; // 指向一个代表此 DLL名字的 ASCII字符串的 RVA DWORD Base; // 函数的起始序号 DWORD NumberOfFu

[PE结构分析] 10.基址重定位

源代码如下: typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD TypeOffset[1]; } IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION; 重定位表是一个数组,这个数组的大小记载在 _IMAGE_OPTIONAL_HEADER 的 .D

利用PE数据目录的导入表获取函数名及其地址

PE文件是以64字节的DOS文件头开始的(IMAGE_DOS_HEADER),接着是一段小DOS程序,然后是248字节的 NT文件头(IMAGE_NT_HEADERS),NT的文件头位置由IMAGE_DOS_HEADER的e_lfanew给出! NT文件头的前4个字节是文件签名(“PE00"字符串),紧接着是20字节的IMAGE_FILE_HEADER结构,它的 后面是224字节的IMAGE_OPTIONAL_HEADER结构,而就在这个结构里,里面有模块基地址,代码和数据大 小和基地址.线程堆

小甲鱼PE详解之IMAGE_NT_HEADERS结构定义即各个属性的作用(PE详解02)

PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着许多PE装载器用到的重要字段.下边小甲鱼将为大家详细讲解哈~ (视频教程:http://fishc.com/a/shipin/jiemixilie/) 首先是IMAGE_NT_HEADERS 结构的定义:(啥?结构不会,先看看小甲鱼童鞋的<零基础入门学习C语言>关于结构方面的章节吧~) IMAGE_NT_HEADERS STRUCT { +0hDWORDSignature  // +4h  IMAG

PE文件格式

以下内容摘录自<加密与解密>: 为了在PE文件中避免有确定的内存地址,出现了相对虚拟地址(RVA)的概念.RVA只是内存中的一个简单的相对于PE文件装入地址的偏移位置.它是一个“相对”地址,或称为“偏移量”. 例如,假设一个EXE文件从地址400000h处装入,并且它的代码区块开始于401000h,代码区块的RVA将是401000h-400000h=RVA1000h. PE装载器将从IMAGE_DOS_HEADER结构中的e_lfanew字段里找到PE Header的起始偏移量,加上基址得到P

向PE文件中添加一个Section

背景 之前说过直接向类HelloWorld.exe的可执行文件添加一个MessageBox弹窗, 但有时候, 需要添加的内容太多了, 因为数据与代码一起插入, 以至于可执行文件本身没有足够的空闲空间存放这些内容时, 就需要添加一个Section. 确认节区头后面还有空间 用工具查看一下最后一个节区头后面是否还有多余的空间, 一般情况都会有的. 但若没有的话, 就要移动节区头后面的文件内容, 这个比较复杂, 在这里不说. 一般会结合PE View 和 WinHex 这两个工具, 如之前的Hello

PE文件结构

PE头 typedef struct _IMAGE_NT_HEADERS { DWORD Signature; PE头标识 为固定的ascii码 PE\0\0 IMAGE_FILE_HEADER FileHeader; 标准PE头 IMAGE_OPTIONAL_HEADER OptionalHeader; 扩展PE头 } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; 标准PE头结构 typedef struct _IMAGE_FILE_HEADER { WORD M

PE详解之IMAGE_NT_HEADERS结构定义即各个属性的作用(PE详解02)

首先是IMAGE_NT_HEADERS 结构的定义:(啥?结构不会,先看看小甲鱼童鞋的<零基础入门学习C语言>关于结构方面的章节吧~) IMAGE_NT_HEADERS STRUCT { +0hDWORDSignature  // +4h  IMAGE_FILE_HEADER FileHeader // +18hIMAGE_OPTIONAL_HEADER32OptionalHeader   // } IMAGE_NT_HEADERS ENDS Signature 字段: 在一个有效的 PE 文