[读书][笔记]WINDOWS PE权威指南《三》PE的原理和基础 之 第三章 PE文件头

简介: WINDOWS PE权威指南《三》PE的原理和基础 之 第三章 PE文件头

文章目录



前言

   3.1 PE的数据组织方式

       举例:书库和书

           书库汇编定义数据结构

           书库字节码

           书的数据结构

           书的字节码

   3.2 与PE有关的基本概念

       3.2.1 地址

           1.虚拟内存地址

           2.相对虚拟内存地址

           3.文件偏移地址

           4.特殊地址

       3.2.2 指针

       3.2.3 数据目录

       3.2.4 节(Section)(也有称块、段)

       3.2.5 对齐

       3.2.6 Unicode字符串

   3.3 PE文件结构

       3.3.1 16位系统下的PE结构

           1.DOS MZ头

           2.DOS Stub

       3.3.2 32位系统下的PE结构

           1.定位标准PE头

           2.PE文件结构

       3.3.3 程序员眼中的PE结构

       总结对比

   3.4 PE文件头部解析

       3.4.1 DOS MZ头——IMAGE_DOS_HEADER

       3.4.2 DOS stub

       3.4.3 PE头标识——Signature

       3.4.4 标准PE头——IMAGE_FILE_HEADER

       3.4.5 扩展PE头——IMAGE_OPTIONAL_HEADER32

       3.4.6 PE头——IMAGE_NT_HEADERS

       3.4.7 数据目录项——IMAGE_DATA_DIRECTORY

       3.4.8 节表项——IMAGE_SECTION_HEADER

   3.5 数据结构字段详解

       3.5.1 PE头——IMAGE_NT_HEADER的字段

           1.IMAGE_NT_HEADER.Signature

           2.IMAGE_NT_HEADER.FileHeader

           3.IMAGE_NT_HEADER.OptionalHeader

       3.5.2 标准PE头——IAMGE_FILE_HEADER的字段

           4.IMAGE_FILE_HEADER.Machine

           5 IMAGE_FILE_HEADER.NumberOfSections

           6.IMAGE_FILE_HEADER.TimeDateStamp

           7.IMAGE_FILE_HEADER.PointerToSymbolTable

           8.IMAGE_FILE_HEADER.NumberOfSymbols

           9.IMAGE_FILE_HEADER.SizeOfOptionalHeader

           10.IMAGE FILE HEADER.Characteristics

       3.5.3 扩展PE头——IMAGE_OPTIONAL_HEADER32的字段

           11.IMAGE OPTIONAL HEADER32.Magic

           12.IMAGE OPTIONAL HEADER32.MajorLinkerVersion

           13.IMAGE OPTIONAL HEADER32.MinorLinkerVersion

           14.IMAGE OPTIONAL HEADER32.SizeOfCode

           15.IMAGE OPTIONAL HEADER32.SizeOflnitializedData

           16.IMAGE_OPTIONAL_HEADER32.SizeOfUninitializedData

           17.IMAGE OPTIONAL HEADER32.AddressOfEntryPoint

           18.IMAGE OPTIONAL HEADER32.BaseOfCode

           19.IMAGE OPTIONAL HEADER32.BaseOfData

           20.IMAGE OPTIONAL HEADER32.ImageBase

           21.IMAGE OPTIONAL_HEADER32.SectionAlignment

           22.IMAGE OPTIONAL HEADER32.FileAlignment

           23.IMAGE OPTIONAL HEADER32.MajorOperatingSystemVersion

           24.IMAGE OPTIONAL HEADER32.MinorOperatingSystemVersion

           25.IMAGE_OPTIONAL_HEADER32.MajorlmageVersion

           26.IMAGE OPTIONAL HEADER32.MinorlmageVersion

           27.IMAGE OPTIONAL HEADER32.MajorSubsystemVersion

           28.IMAGE OPTIONAL HEADER32.MinorSubsystemVersion

           29.IMAGE_OPTIONAL_HEADER32.Win32VersionValue

           30.IMAGE OPTIONAL HEADER32.SizeOflmage

           31.IMAGE OPTIONAL HEADER32.SizeOfHeaders

           32.IMAGE OPTIONAL HEADER32.CheckSum

           33.IMAGE OPTIONAL HEADER32.Subsystem

           34.IMAGE OPTIONAL HEADER32.DlICharacteristics

           35.IMAGE OPTIONAL HEADER32.SizeOfStackReserve

           36.IMAGE OPTIONAL HEADER32.SizeOfStackCommit

           37.IMAGE OPTIONAL HEADER32.SizeOfHeapReserve

           38.IMAGE OPTIONAL HEADER32.SizeOfHeapCommit

           39.IMAGE OPTIONAL HEADER32.LoaderFlags

           40.IMAGE OPTIONAL HEADER32.NumberOfRvaAndSize

           41.IMAGE OPTIONAL HEADER32.DataDirectory

       3.5.4 数据目录项——IMAGE_DATA_DIRECTORY的字段

           42.IMAGE DATA DIRECTORY.VirtualAddress

           43.IMAGE DATA DIRECTORY.isize

       3.5.5 节表项——IMAGE_SECTION_HEADER的字段

           44.IMAGE SECTION HEADER.Name1

           45.IMAGE SECTION HEADER.Misc

           46.IMAGE SECTION HEADER.VirtualAddress

           47.IMAGE SECTION HEADER.SizeOfRawData

           48.IMAGE SECTION HEADER.PointerToRawData

           49.IMAGE SECTION HEADER.PointerToRelocations

           50.IMAGE SECTION HEADER.PointerToLinenumbers

           51.IMAGE SECTION HEADER.NumberOfRelocations

           52.IMAGE_SECTION_HEADER.NumberOfLinenumbers

           53.IMAGE SECTION HEADER.Characteristics

       3.5.6 解析HelloWorld程序的字节码

   3.6 PE内存映像

   3.7 PE文件头编程

       3.7.1 RVA与FOA的转换

       3.7.2 数据定位

       3.7.3 标志位操作

       3.7.4 PE校验和

   3.8 总结


前言



PE文件头是PE(PE文件头+PE文件体)的一部分,PE常常用来记录和标识PE信息,包括PE入口地址(OEP),节表(Section Table),


一般,PE文件头由以下几部分组成:


   DOS头

   PE头

   节表


DOS头:主要用来标识PE文件和PE文件运行的环境。

PE头:主要记录PE的信息,包括拓展PE入口地址(OEP)

节表:主要记录PE文件各种起始地址,比如.text代码段地址,.data/.rdata起始地址等


3.1 PE的数据组织方式



   PE的数据组织是一种数据管理技术,但笔者更愿意把它看成是一种艺术。希望读者在学习完PE格式后,不仅能把握PE格式本身,还能够从中学会使用数据结构管理数据的方式,当大家理解其他格式的文件(如音视频格式、数据库文件格式、图片格式等)时会有所启发。


   1993年,第一个 Windows NT操作系统诞生,到现在有将近18年的时间。目前,使用PE作为可执行文件格式的Windows操作系统已经更换了很多版本。对于操作系统来说,其结构的变化、新特性的添加、文件存储格式的转换,以及内核的重新定位等,都发生了翻天覆地的变化,而这些变化对PE格式的影响却不大。由于PE有较好的数据组织方式和数据管理算法,面对如此多的变化却依然能保持其一贯的优雅和优越。


   简言之,PE的数据组织是大量的字节码与数据结构的有机融合。字节码是一些毫无意义的数字,而数据结构却为这些数字赋予了人类可以理解的精准含义。


举例:书库和书


书库汇编定义数据结构

BookStore STRUCT
  Name    db  8 dup (0);书库的名字
  Address dd  ?;书库所在地址
  Count   dd  ?;书库中的藏书量
BookStore ENDS


书库实例化:

name1  db '书库一 ',0,
lib1   BookStore <?>
mov    ebx,lib1
assume ebx:ptr BookStore
invoke MemCopy,addr name1,[ebx].Name;为书库命名
mov    eax ,123456h;确定书库的位置
mov    [ebx].Address, eax
mov    eax ,      2;指定书库的藏量
mov    [ebx].Count , eax
assume ebx : nothing

如上所示,通过定义变量lib1,实例化一个 BookStore结构,然后通过赋值语句为该结构中的每个字段指定不同的值。


书库字节码


书库定义好后,管理员通过书库名找到书库一,再找到书库一的地址,和该书库的书的数量。

图片.png

由于要管理书籍,有引入了书的数据结构。于是抽象出书库和书的数据结构。


书的数据结构


新的书库和书的结构:

;书库的定义:
Bookstore STRUCT
  Name db 8 dup (0);书库的名字
  Address dd ?;书库所在地址
  Count dd ?;书库中的藏书量
BookStore ENDS
;书的定义:
Book STRUCT
  Name db 50 dup (0);书的名字
  Contents dd ?;书字节码所在地址
Book ENDS

实例化代码:

name1 db  '书库一', 0
name2 db '《windows PE权威指南》',0
lib1 Bookstore <?>
book1 Book <?>
book2 Book <?>
bookArray dd offset book1:指向第一本书
      dd offset book2;指向第二本书
      dd 0;结束
assume ebx : ptr Book
invoke MemCopy , addr name1, [ebx].Name;为书命名
mov eax,234567h;确定书字节码的位置
mov [ebx].Address,eax
assume ebx : nothing
. . . .. .;此处省略了对book2的定义
mov ebx , lib1
assume ebx:ptr BookStore
invoke MemCopy, addr name1,[ebx].Name;为书库命名
mov  eax ,offset bookArray;确定书库的位置
mov [ebx].Address , eax
mov  eax , 2;指定书库的藏书量
mov [ebx].Count , eax
assume ebx ; nothing

当我们增加了书的结构定义后,书库一中的Address就有了一个明确的含义。它指向了地址数组bookArray的起始位置,该数组中的每个地址都是一个指向Book的双字地址。从这个意义上讲,bookArray是书库一的字节码。


书的字节码

图片.png

   可以简单地套用Windows操作系统管理文件的组织方式:

   目录类似于这里的BookStore,

   文件类似于这里的Book,

   而文件存储在 硬盘上的字节码为Book的字节码。 也可以用其他任意一种文件格式来分析,大部分文件格式的数据组织方式基本上是一样的。笔者将这种信息组织方式称为“头部+身体”。


3.2 与PE有关的基本概念



图片.png


3.2.1 地址


PE中涉及的地址有四类,它们分别是:


   虚拟内存地址(VA)

   相对虚拟内存地址(RVA)

   文件偏移地址(FOA)

   特殊地址


要想了解这些概念,需要先简单地了解一下32位环境下Windows对内存的管理,以及分页机制的原理。


   扩展阅读:32位环境下的Windows内存管理

   32位CPU的寻址能力为4GB(即2”个字节),但有些用户的物理内存达不到这个值。于是操作系统和CPU的内存管理单元共同作用,为用户提供了虚拟内存的管理机制。即分页机制。该机制可以让用户感觉自己好像在使用4GB的内存。

   分页机制的基本原理是:

   操作系统假设一个进程独立拥有4GB内存,按照某个固定的大小(如 4KB)将这4GB空间分成N(1M)个页。在某一时刻,所有这些页只有一部分和物理内存是对应的(所以这种机制允许物理内存比4GB小)。没有物理内存对应的页面被标记为脏(dirty)的页面,一般存储在一个名为“交换文件”的磁盘文件中。

   在Windows XP系统中,交换文件为pagefile.sys,它位于系统盘的根目录,是一个系统隐藏文件。当系统需要读取未在内存中的数据时,这部分数据会将内存中不经常读写的页交换出内存,而把要读取的、位于交换文件中的页换进内存。

   通过这种存取机制可以让一个进程拥有比实际内存大得多的内存。利用这种机制管理的内存称为虚拟内存。


1.虚拟内存地址


   用户的PE文件被操作系统加载进内存后,PE对应的进程支配了自己独立的4GB虚拟空间。在这个空间中定位的地址称为虚拟内存地址(Virtual Address,VA),所以虚拟内存地址的范围是00000000h ~0FFF FFFFh。

   在PE中,进程本身的VA被解释为:进程的基地址+相对虚拟内存地址。


2.相对虚拟内存地址


   一个进程被操作系统加载到虚拟内存空间后,其相关的动态链接库也会被加载。这些同时加载到进程地址空间的文件称为模块。每一个模块在加载时都会有一个基地址,也就是预先告诉操作系统:它会占用4GB空间的哪个部分(即从哪里开始存储该模块)。不同模块的基地址一般是不同的,如果两个模块的基地址相同,就由操作系统来决定这两个模块在虚拟空间中的具体位置。


相对虚拟内存地址(Reverse Virtual Address ,RVA)是相对于基地址的偏移,即RVA是虚拟内存中用来定位某个特定位置的地址,该地址的值是这个特定位置距离某个模块基地址的偏移量,所以说RVA是针对某个模块而存在的。


图片.png

如图3-4所示,假设模块2的基地址为0x01000000,而模块2中的某个位置距离模块⒉的基地址偏移为400h,那么值0x00000400就是模块2中某个位置的RVA,而值0x01000400是该位置的VA。记住,RVA是相对于模块而言的,VA是相对于整个地址空间而言的。


   注意:

   RVA与具体模块相关,它有一个范围,该范围从模块的开始到模块结束,脱离开这个范围的RVA是无效的,称为越界。越界的RVA地址没有任何意义。


3.文件偏移地址


文件偏移地址(File Offset Address,FOA)和内存无关,它是指某个位置距离文件头的偏移。


4.特殊地址


在PE结构中还有一种特殊地址,其计算方法并不是从文件头算起,也不是从内存的某个模块的基地址算起,而是从某个特定的位置算起。这种地址在PE结构中很少见,如在资源表里就出现过这样的地址。


3.2.2 指针


   PE数据结构中的指针的定义:如果数据结构中某个字段存储的值为一个地址,那么这个字段就是一个指针。 例如,在3.1节数据组织方式的实例中,BookStore.Address是一个指针,Book.Address也是一个指针。

   有时候,你还会遇到一个指针指向了另一个指针的情况。比如,在第10章中,加载配置信息中的数据结构加载配置目录(IMAGE_LOAD_CONFIG_DIRECOTRY)的字段SEHandlerTable的值是一个VA,但该指针所指的位置是一个RVA,该RVA指向了安全的SEH处理函数的Handler。所以,在数据结构中,可能会碰到指针和地址叠加使用的情况,大家需要引起重视。


3.2.3 数据目录


   Windows下的可执行文件是PE中的一种,这种文件中除了包含代码及数据段的相关数据以外,还包含许多与文件执行有关的其他数据,比如引用外部函数的信息、PE程序的图标、内部导出函数等,这些数据可能会随着操作系统新特性的出现而增加。


数据目录:PE中有一个数据结构称为数据目录,其中记录了所有可能的数据类型。

这些类型中,目前已定义的有15种,包括:


   导出表、

   导入表、

   资源表、

   异常表、

   属性证书表、

   重定位表、

   调试数据、

   Architecture、

   Global Ptr、

   线程局部存储(TLS)、

   加载配置表、

   绑定导入表、

   IAT、

   延迟导入表

   CLR运行时头部。


3.2.4 节(Section)(也有称块、段)


   无论是结构化程序设计,还是面向对象程序设计,都提倡程序与数据的独立性,因此,程序中的代码和数据通常是分开存放的。为了保证程序执行的安全,保障内核的稳定,Windows操作系统通常对不同用途的数据设置不同的访问权限。比如,代码段(.text)中的字节码在程序运行的时候,一般不允许用户进行修改,数据段(.data)则允许在程序运行过程中读和写,常量(.)只能读等。Windows操作系统在加载可执行程序时,会为这些具有不同属性的数据分别分配标记有不同属性的页面(当然,相同属性的数据可能会被放到同一个页面中),以确保程序运行时的安全。正是基于这个原因,PE中才出现了所谓的节的概念。


节(Section)就是存放不同类型数据(比如代码、数据、常量、资源等)的地方,不同的节具有不同的访问权限。节是PE文件中存放代码或数据的基本单元。例如,一个目标文件中的所有代码可以组合成单个节,或者每个函数独占一个 (上命.个书中的所有原始数据必须加文件开销,但是链接器在链接代码时会有更大的选择余地。一个节中的所有原始数据必须

被加载到连续的内存空间中。


   从操作系统加载角度来看,节是相同属性数据的组合。与数据目录不同的是,尽管有些数据类型不同,分别属于不同的数据目录,但由于其访问属性相同,便被归类到同一个节中这个节最终可能会占用一个或多个页面;但无论有多少个,所有相关页面均会被赋予相同的页属性。这些属性包括只读、只写、可读、可写等。

   汇编语言中以“.”开头的一些伪指令其实就是在声明不同的数据类型。比如“.data”声明的是初始化的数据,“.data?”声明的是未初始化的数据,“.code”声明的是可执行的代码等。Windows操作系统在装载PE文件时会对这些数据执行抛弃、合并、新增、复制等操作这些不同的操作交叉组合导致了内存中的节和文件中的节会出现很大的不同。例如“.data?的数据在磁盘中不存在,但在内存中存在,而“.reloc”重定位表数据却恰恰相反。


3.2.5 对齐


对齐这个概念并非只在PE结构中出现,许多文件格式都会有对齐的要求。有的对齐是为了美观,有的对齐则是为了效率。PE中规定了三类对齐:


   1.数据在内存中的对齐( 内存对齐)

   2.数据在文件中的对齐(文件对齐)

   3.资源文件中资源数据的对齐。(资源对齐)


   内存对齐

   由于Windows 操作系统对内存属性的设置以页为单位,所以通常情况下,节在内存中的对齐单位必须至少是一个页的大小。对32位的Windows XP系统来说,这个值是4KB( 1000h),而对于64位操作系统来说,这个值就是8KB (2000h)。

   文件对齐

   相对来说,节在磁盘文件中的对齐尺寸没有那么严格。为了提高磁盘利用率,通常情况下,定义的节在文件中的对齐单位要远小于内存对齐的单位﹔通常会以一个物理扇区的大小作为对齐粒度的值,即512字节,十六进制表示为200h。这就是我们在第1章中看到数据段、代码段等起始地址都是200h的倍数的原因了。

   出于节约资源的考虑,操作系统允许节在内存和文件中的对齐尺寸不一致。这就直接造成了PE 在文件中和在内存中的大小也会不一致。通常情况下,PE在内存中的尺寸要比在文件中的尺寸大。用户可以自己定义这些对齐的值。


   注意:如果内存对齐被定义为小于操作系统页的大小,则文件对齐和内存对齐的值必须一致!


   资源对齐

   资源文件中,资源字节码部分一般要求以双字(4个字节)方式对齐,在资源表部分(详见本书第7章)我们会详细讲解。


3.2.6 Unicode字符串


Unicode是继ASCII字符编码后的另一种新型字符编码。严格意义上讲,ASCII 码的每个字符使用7位表示,Unicode(UTF-16)则使用全16位表示一个字符。Unicode字符串中的每个字符均为双字节,所以又称为宽字符串。


由于Unicode兼容ASCII字符,所以被大多数程序所支持,如Windows内核。Unicode的前128个字符码(十六进制,Ox0000~Ox007F)同ASCII码具有同样的字节值。比如,字母“a”的Unicode编码是0x0061,而“a”的ASCII编码是0x61。虽然占用的字节数不一样,但是两者的值是一样的。接下来的128个Unicode字符(代码为0x0080~Ox0OFF)是ISO 8859-1对ASCII码的扩展。中国、日本和韩国的象形文字(总称为CJK)占用了0x3000~Ox9FFF的代码。如“汉”字的Unicode编码是6C49h(其GB码为0BABAh)。


本书所有的程序都使用一个字节来表示字符串中的字符,称为ANSI字符串。PE格式中涉及字符串的部分均采用ANSI字符串。然而,在资源表中,对菜单名、对话框标题等的描述则全部使用Unicode字符串。所以,在读取这些资源的字符串时,首先需要使用一些API函数实现从宽字符集到窄字符集的转换。


   注意:Unicode字符串不像ANSI字符串那样,保证用字符“\0”结束;如果开发者在程序设计时以字符“\0”作为Unicode字符串结尾的判断条件,就可能发生错误。


在汇编语言中,Unicode字符串被定义为一个结构体,它的定义如下:

图片.png


由于我们无法保证Unicode字符串结尾一定是“\0”,所以在结构体中,字段Length定义了字符串的长度。一个安全的字符串还必须限定字符的总长度,这由MaximumLength 来实现。


3.3 PE文件结构



3.3.1 16位系统下的PE结构


在16位系统下,PE结构可以大致划分为两部分:DOS头和冗余数据

图片.png

如上图所示,在16位系统下,PE的四部分内容被重新组合成两部分——可以在16位系统下运行的DOS头和冗余数据。把Windows下的PE文件存储到DOS系统并运行,它就是DOS系统下的一个EXE文件。

DOS头分为两部分,DOS MZ头和 DOS Stub(即指令字节码)。


大部分情况下,这些指令实现的功能都非常简单,根本不会涉及重定位信息。再往后的PE头和PE数据区可以看做是16位系统下的可执行文件的冗余数据。


1.DOS MZ头


在Windows的PE格式中,DOS MZ头的定义如下:

图片.png

如上所示,加粗部分在16位系统下是没有定义的。由于其开始的标志字为“MZ”(Mark Zbikowski,他是DOS操作系统的开发者之一),所以称它为“DOS MZ头”。


下面来看第Ⅰ章提到的HelloWorld.exe的字节数据,HelloWorld中的DOS头如下所示:

在这里插入图片描述


   注意:

   这部分内容在源程序HelloWorld.asm中是找不到相应的定义语句的。因为DOS MZ头部分的字节码(包括DOS Stub程序字节码)的添加是由链接程序link.exe自动实现的。


2.DOS Stub


在这里插入图片描述

DOS Stub 特点:

由于DOS Stub的大小不固定,因此DOS头的大小也是不固定的。DOS Stub部分是该程序在DOS系统下运行的指令字节码。


主要作用:

用来兼容DOS系统。当我们的程序运行在DOS系统的时候,就会运行DOS存根中的代码,代码内容就是输出一段字符串告诉用户,这个程序不能在16位系统运行。


3.3.2 32位系统下的PE结构


在16位系统中,PE头和PE数据部分被当成是冗余数据;

在32位系统中,刚好相反,即 DOS头成为冗余数据。


所谓冗余,是针对DOS头不参与32位系统运行过程而言的。尽管该部分不参与运行,但也不能把这些数据从PE结构中除去。


因为在DOS MZ头中有一个字段非常重要,即IMAGE_DOS_HEADER.e_lfanew,没有它操作系统就定位不到标准的PE头部,可执行程序也就会被操作系统认为是非法的PE映像。 所以经常也用它定位PE头起始地址。


1.定位标准PE头


由于DOS Stub的长度不固定,导致了DOS头也不是一个固定大小的数据结构。那么,在Windows PE中,既然把DOS头放在了PE的起始位置,


如何去定位后面的标准PE头所在的位置呢?


字段e_lfanew即起这个作用。该字段的值是一个相对偏移量,绝对定位时需要加上DOS MZ头的基地址。也就是说,通过以下公式可以得出PE头的绝对位置:

图片.png

对比本书1.6节中代码清单1-2列出的HelloWorld.exe的字节码,可以看到该位置是以ASCII字符“PE”开头的,所以,通过字段 IMAGE_DOS_HEADER.e_lfanew的值可以定位到PE头的起始位置。


2.PE文件结构

图片.png

如上图所示,32位系统下的PE文件结构被划分为5个部分,包括:


   DOS MZ头

   DOS Stub

   PE头

   节表

   节内容


节表(Section Table)和节内容()两部分其实就是图3-5中所示的PE数据区。DOS MZ头的大小是64个字节,PE头的大小是456个字节(由于数据目录表项不一定是16个,所以准确地说,PE头也是一个不能确定大小的结构,该结构的实际大小由字段IMAGE_FILE_HEADER.SizeOfOptionalHeader来确定)。


节表的大小之所以不固定,是因为每个PE中节的数量是不固定的。每个节的描述信息则是个固定值,共40个字节,节表是由不确定数量的节描述信息组成的,其大小等于节的数量×40,节的数量由字段IMAGE_FILE_HEADER.NumberOfSections来定义。DOS Stub和节内容都是大小不确定的。


节表是PE中所有节的目录,每个目录都是一个“BookStore”,其字节码就是节内容。它按照目录里的指针指向的地址,分别将节的字节码在文件空间中排列起来,从而组成了一个完整的PE文件。PE文件头部等于DOS头+PE头。


3.3.3 程序员眼中的PE结构


图片.png

如图所示,一个标准的PE文件一般由四大部分组成:


   DOS头

   PE头 ( IMAGE_NT_HEADERS)

   节表(多个IMAGE_SECTION_HEADER结构)

   节内容(具体数据段)


其中,PE头的数据结构最为复杂。简单来说,PE头包含:


   4个字节的标识符号( Signature)

   20个字节的基本头信息(IMAGE_FILE_HEADER)

   216个字节的扩展头信息(IMAGE_OPTIONAL_HEADER32)


   说明如果按照“头部+身体”的信息组织方式来看:


       PE文件头部 = DOS头+PE头+节表

       PE文件身体=节内容


节内容中会出现各种不同的数据结构,如导入表、导出表、资源表、重定位表等,关于这些数据的组织方式会在后面的章节中陆续接触到。


总结对比


1.PE不同位数特点

PE类型 相同 不同

16位PE



32位PE  




3.4 PE文件头部解析



3.4.1 DOS MZ头——IMAGE_DOS_HEADER


IMAGE_DOS_HEADER具体定义:

图片.png

图片.png


3.4.2 DOS stub


DOS MZ头的下面是DOS Stub。


整个DOS Stub是一个字节块,其内容随着链接时使用的链接器不同而不同,PE中并没有与之对应的相关结构。


3.4.3 PE头标识——Signature


紧跟在DOS Stub后面的是PE头标识Signature。

与大部分文件格式的头部结构一样,PE头部信息中有一个四字节的标识,

该标识位于指针IMAGE DOS_HEADER.e_Ifanew指向的位置。

其内容固定,对应于ASCⅡ码的字符串“PE\O\0”。


3.4.4 标准PE头——IMAGE_FILE_HEADER


标准PE头IMAGE FILE HEADER紧跟在PE头标识后,即位于IMAGE DOS HEADER的e lfanew值+4的位置。


由此位置开始的20个字节为数据结构标准PE头IMAGE FILE HEADER的内容。


该结构在微软的官方文档中被称为标准通用对象文件格式(Common Object File Format,,COFF)头。


作用:

它记录了PE文件的全局属性 如该PE文件运行的平台、PE文件类型(是EXE文件还是DLL文件)、文件中存在的节的总数等


其详细定义如下:

图片.png

图片.png


注意:

该结构常用于判断PE文件是EXE类型还是DLL类型,不但可以通过该结构得到PE文件中节的总量,还可以当成对节区信息进行遍历操作时的循环次数。


3.4.5 扩展PE头——IMAGE_OPTIONAL_HEADER32


定义:

图片.png

图片.png

图片.png

作用:

   文件执行时的入口地址OEP、

   文件被操作系统装入内存后的默认基地址Image_base

   节在磁盘和内存中的对齐单位等信息均可以在此结构中找到。


注意:

对该结构中的某些数值的随意改动可能会造成PE文件的加载或运行失败。


3.4.6 PE头——IMAGE_NT_HEADERS


广义的PE头(即由PE头标识、标准PE头、拓展PE头三部分的组成):

图片.png


其定义如下:

图片.png


   与DOS头一样,PE头开始也是一个标志,用一个双字的“PEO0”来命名,这也是PE头的由来。


Windows头文件winnt.h中IMAGE_NT_HEADERS结构体

图片.png


3.4.7 数据目录项——IMAGE_DATA_DIRECTORY


IMAGE OPTIONAL HEADER32(扩展PE头)结构的最后一个字段为DataDirectory。


该字段定义了PE文件中出现的所有不同类型的数据的目录信息。


如前所述,应用程序中的数据被按照用途分成很多种类:


   导出表、

   导入表、

   资源、

   重定位表等。


在内存中,这些数据被操作系统以页为单位组织起来,并赋以不同的访问属性;

在文件中,这些数据也同样被组织起来,按照不同类别分别存放在文件的指定位置。


作用:该结构就是用来描述这些不同类别的数据在文件(和内存)中的位置及大小的,因为这个字段比较重要


从Windows NT3.1操作系统开始到现在,该数据目录中定义的数据类型一直是16种。


PE中使用了一种称作“数据目录项——IMAGE DATA DIRECTORY”的数据结构来定义每种数据。


该结构只有两个字段,结构具体定义如下:

图片.png

Windows c++中:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

两个字段依次为VirtualAddress和isize。


如图所示,总的数据目录一共由16个相同的IMAGE DATA DIRECTORY结构连续排列在一起组成。

图片.png

在Windows头文件winnt.h中 属于拓展PE头结构体IMAGE_OPTIONAL_HEADER的成员DataDirectory:

图片.png

这16个元组的数组每一项均代表PE中的某一个类型的数据:

图片.png

如果想在PE文件中寻找特定类型的数据,就需要从该结构开始。


   比如:

   要想查看PE中都调用了哪些动态链接库的函数?

   则需要从数据目录表的第2个元素(数组编号为1)的IMAGE DATA DIRECTORY结构 获取导入表在文件中的起始位置和大小,然后再根据VirtualAddress地址指向的位置找到导入表相关的字节码。


这种信息组织方式正是本章最开始介绍的“头部+身体”的数据组织方式。


3.4.8 节表项——IMAGE_SECTION_HEADER


PE头IMAGE NT HEADERS后紧跟着节表。


它由许多个节表项(IMAGE SECTION HEADER)组成,每个节表项记录了PE中与某个特定的节有关的信息,如节的属性、节的大小、在文件和内存中的起始位置等。


节表中节的数量由字段IMAGE FILE HEADER, NumberOfSections来定义。节表项的数据结构详细定义如下:

图片.png

Windows 头文件winnt.h中IMAGE_SECTION_HEADER结构体:

图片.png

在这里插入图片描述


在这里插入图片描述

节表后面就是节的内容。截至节表,PE文件头部涉及的所有数据结构已经全部介绍完毕。


3.5 数据结构字段详解



参考于2010年9月21日发布的Microsoft Portable Executable and Common Object File Format Specification(Revision v8.2)


3.5.1 PE头——IMAGE_NT_HEADER的字段


1.IMAGE_NT_HEADER.Signature

2.IMAGE_NT_HEADER.FileHeader

3.IMAGE_NT_HEADER.OptionalHeader

3.5.2 标准PE头——IAMGE_FILE_HEADER的字段

4.IMAGE_FILE_HEADER.Machine

5 IMAGE_FILE_HEADER.NumberOfSections

6.IMAGE_FILE_HEADER.TimeDateStamp

7.IMAGE_FILE_HEADER.PointerToSymbolTable

8.IMAGE_FILE_HEADER.NumberOfSymbols

9.IMAGE_FILE_HEADER.SizeOfOptionalHeader

10.IMAGE FILE HEADER.Characteristics


3.5.3 扩展PE头——IMAGE_OPTIONAL_HEADER32的字段


11.IMAGE OPTIONAL HEADER32.Magic

12.IMAGE OPTIONAL HEADER32.MajorLinkerVersion

13.IMAGE OPTIONAL HEADER32.MinorLinkerVersion

14.IMAGE OPTIONAL HEADER32.SizeOfCode

15.IMAGE OPTIONAL HEADER32.SizeOflnitializedData

16.IMAGE_OPTIONAL_HEADER32.SizeOfUninitializedData

17.IMAGE OPTIONAL HEADER32.AddressOfEntryPoint

18.IMAGE OPTIONAL HEADER32.BaseOfCode

19.IMAGE OPTIONAL HEADER32.BaseOfData

20.IMAGE OPTIONAL HEADER32.ImageBase

21.IMAGE OPTIONAL_HEADER32.SectionAlignment

22.IMAGE OPTIONAL HEADER32.FileAlignment

23.IMAGE OPTIONAL HEADER32.MajorOperatingSystemVersion

24.IMAGE OPTIONAL HEADER32.MinorOperatingSystemVersion

25.IMAGE_OPTIONAL_HEADER32.MajorlmageVersion

26.IMAGE OPTIONAL HEADER32.MinorlmageVersion

27.IMAGE OPTIONAL HEADER32.MajorSubsystemVersion

28.IMAGE OPTIONAL HEADER32.MinorSubsystemVersion

29.IMAGE_OPTIONAL_HEADER32.Win32VersionValue

30.IMAGE OPTIONAL HEADER32.SizeOflmage

31.IMAGE OPTIONAL HEADER32.SizeOfHeaders

32.IMAGE OPTIONAL HEADER32.CheckSum

33.IMAGE OPTIONAL HEADER32.Subsystem

34.IMAGE OPTIONAL HEADER32.DlICharacteristics

35.IMAGE OPTIONAL HEADER32.SizeOfStackReserve

36.IMAGE OPTIONAL HEADER32.SizeOfStackCommit

37.IMAGE OPTIONAL HEADER32.SizeOfHeapReserve

38.IMAGE OPTIONAL HEADER32.SizeOfHeapCommit

39.IMAGE OPTIONAL HEADER32.LoaderFlags

40.IMAGE OPTIONAL HEADER32.NumberOfRvaAndSize

41.IMAGE OPTIONAL HEADER32.DataDirectory


3.5.4 数据目录项——IMAGE_DATA_DIRECTORY的字段


42.IMAGE DATA DIRECTORY.VirtualAddress

43.IMAGE DATA DIRECTORY.isize


3.5.5 节表项——IMAGE_SECTION_HEADER的字段

44.IMAGE SECTION HEADER.Name1

45.IMAGE SECTION HEADER.Misc

46.IMAGE SECTION HEADER.VirtualAddress

47.IMAGE SECTION HEADER.SizeOfRawData

48.IMAGE SECTION HEADER.PointerToRawData

49.IMAGE SECTION HEADER.PointerToRelocations

50.IMAGE SECTION HEADER.PointerToLinenumbers

51.IMAGE SECTION HEADER.NumberOfRelocations

52.IMAGE_SECTION_HEADER.NumberOfLinenumbers

53.IMAGE SECTION HEADER.Characteristics


3.5.6 解析HelloWorld程序的字节码


3.6 PE内存映像


图片.png


3.7 PE文件头编程



3.7.1 RVA与FOA的转换


3.7.2 数据定位


3.7.3 标志位操作


3.7.4 PE校验和


3.8 总结


相关文章
|
4天前
|
Python Windows
在 Windows 平台下打包 Python 多进程代码为 exe 文件的问题及解决方案
在使用 Python 进行多进程编程时,在 Windows 平台下可能会出现将代码打包为 exe 文件后无法正常运行的问题。这个问题主要是由于在 Windows 下创建新的进程需要复制父进程的内存空间,而 Python 多进程机制需要先完成父进程的初始化阶段后才能启动子进程,所以在这个过程中可能会出现错误。此外,由于没有显式导入 Python 解释器,也会导致 Python 解释器无法正常工作。为了解决这个问题,我们可以使用函数。
13 5
|
7天前
|
编解码 Windows
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
XviD是开源MPEG-4视频编码器,与DivX相似但后者非开源。早期MP4常使用XviD或DivX编码,现已被H.264取代。在Windows上集成FFmpeg的XviD编解码库libxvid,需访问<https://labs.xvid.com/source/>下载源码,解压后在MSYS环境中配置、编译和安装。之后重新配置FFmpeg,启用libxvid并编译安装。详细步骤包括configure命令、make和make install。成功后,通过`ffmpeg -version`检查是否启用libxvid。更多音视频开发技术可参考《FFmpeg开发实战:从零基础到短视频上线》。
36 0
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
|
1月前
|
算法 Linux Windows
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
在Windows环境下为FFmpeg集成字幕渲染库libass涉及多个步骤,包括安装freetype、libxml2、gperf、fontconfig、fribidi、harfbuzz和libass。每个库的安装都需要下载源码、配置、编译和安装,并更新PKG_CONFIG_PATH环境变量。最后,重新配置并编译FFmpeg以启用libass及相关依赖。完成上述步骤后,通过`ffmpeg -version`确认libass已成功集成。
46 1
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
|
28天前
|
编解码 5G Linux
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
AVS3是中国首个8K及5G视频编码标准,相比AVS2和HEVC性能提升约30%。解码器libuavs3d支持8K/60P视频实时解码,兼容多种平台。《FFmpeg开发实战》书中介绍了在Windows环境下如何集成libuavs3d到FFmpeg。集成步骤包括下载源码、使用Visual Studio 2022编译、调整配置、安装库文件和头文件,以及重新配置和编译FFmpeg以启用libuavs3d。
41 0
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
|
1月前
|
安全 数据安全/隐私保护 Windows
Windows文件搜索神器Everything安装配置结合内网穿透实现公网查询本地文件
Windows文件搜索神器Everything安装配置结合内网穿透实现公网查询本地文件
|
1月前
|
Apache 项目管理 数据安全/隐私保护
Windows安装TortoiseSVN客户端结合Cpolar实现公网提交文件到本地服务器
Windows安装TortoiseSVN客户端结合Cpolar实现公网提交文件到本地服务器
|
1月前
|
Oracle Java 关系型数据库
windows 下 win11 JDK17安装与环境变量的配置(配置简单详细,包含IJ中java文件如何使用命令运行)
本文介绍了Windows 11中安装JDK 17的步骤,包括从官方网站下载JDK、配置环境变量以及验证安装是否成功。首先,下载JDK 17的安装文件,如果没有Oracle账户,可以直接解压缩文件到指定目录。接着,配置系统环境变量,新建`JAVA_HOME`变量指向JDK安装路径,并在`Path`变量中添加。然后,通过命令行(cmd)验证安装,分别输入`java -version`和`javac -version`检查版本信息。最后,作者分享了如何在任意位置运行Java代码,包括在IntelliJ IDEA(IJ)中创建的Java文件,只需去掉包声明,就可以通过命令行直接运行。
576 1
|
1月前
|
Java 程序员 Windows
【windows自带exe】使用`findstr.exe`来搜索包含某个字符串的文件
【windows自带exe】使用`findstr.exe`来搜索包含某个字符串的文件
63 0
|
1月前
|
安全 Linux 网络安全
Windows搭建Emby媒体库服务器,无公网IP远程访问本地影音文件
Windows搭建Emby媒体库服务器,无公网IP远程访问本地影音文件
37 0