Windows:32位PE文件结构

参考

小甲鱼PE详解系列

什么是PE文件

PE(Portable Executable)即可移植的执行体,使用了PE文件结构的可执行文件被称为PE文件,Win下包括EXE、DLL、SYS、OCX等

PE结构图

在这里插入图片描述

PE文件结构简介

1. DOS头

1.1 MZ头

MZ头是真正的DOS头,其开始处的两个字节为MZ。此部分用于程序在DOS系统下加载,它的结构被定义为IMAGES_DOS_HEADER
在这里插入图片描述

1.2 DOS残留

一段简单的程序,主要用于输出"This program cannot be run in DOS mode."的提示字符串
在这里插入图片描述

2. PE头

2.1 PE标识

宏:IMAGE_NT_SIGNATRUE

2.2 文件头

结构体:IMAGE_FILE_HEADER

2.3 可选头

结构体:IMAGE_OPTIONAL_HEADER

3. 节表

节表与节
程序的组织按照不同的属性,被保存在不同的节中,每个节用一个IMAGE_SECTION_HEADER表示。如果PE文件中有N个节,那么节表就是由N个IMAGE_SECTION_HEADER结构体和一个表示结束的空IMAGE_SECTION_HEADER结构体组成的数组。

空结构体不计入节区数 pimageNtHeaders->FileHeader.NumberOfSections

节表中存储了各节的属性、文件位置、内存位置等信息,是节的索引。

4. 节

节事实上就是相同属性数据的组合,一个节中可能有多种类型的数据,它们只是因为读写属性相同而被装入同一节。当节被装入到内存中的时候,相同一个节所对应的内存页都将被赋予相同的页属性,事实上,Windows 系统对内存属性的设置是以页为单位进行的,所以节在内存中的对齐单位必须至少是一个页的大小。(小甲鱼温馨提示:对于32位操作系统来说,这个值一般是4KB即1000H; 对于64位操作系统这个值一般是8KB即2000H)

结构体关系图

来源
在这里插入图片描述

PE详解

1. DOS头

DOS头部是用于装载DOS残留用的,且其中的一个字段保存着指向PE头部的位置
此结构体中
e_magic:2B,DOS可执行文件的标识符
该标识符在winnt.h中有一个宏定义
#define IMAGES_DOS_SIGNATURE 0x5A4D
因为系统是小端方式存储,所以实际保存时前两个字节是4D 5A
在这里插入图片描述

e_lfanew:2B,PE头的起始位置
保存于偏移为0x0000003C的位置
在这里插入图片描述它的值为0x0110

2. PE头

在这里插入图片描述

2.1 PE标识

查看0x00000110位置
在这里插入图片描述Signature:4B,值是0x00004550,即PE\0\0

2.2 文件头

PE标识后20个字节为文件头,主要描述文件的相关信息
在这里插入图片描述
结构体_IMAGE_FILE_HEADER定义:
在这里插入图片描述
在这里插入图片描述
Machine:2B,可执行文件的目标处理器类型
如图中0x8664,代表x64处理器

NumberOfSections:2B,节区个数
图中0x0008,表示有8个节区

TimeDataStamp:4B,从1970年至文件被创建时间的秒数

SizeOfOptionHeader:2B,可选头大小。图中为0x00f0,转十进制为240字节

Characteristics:2B,文件类型。图中为0x0022

2.3 可选头

可选头主要用来管理PE文件被操作系统装载时需要的信息。

注意:可选头之所以被称为可选头,是因为其数据目录数组中,有的数据目录项是可有可无的,但是此头必须存在。

由文件头的SizeOfOptionHeader知可选头大小为240字节,紧挨文件头。文件头的结束位置为0x00000127,则可选头的地址范围为:0x00000128 ~ 0x00000217
在这里插入图片描述
结构体定义如下:
在这里插入图片描述
SizeOfCode:4B,所有代码节大小的总和。图中为0x001c6400

AddressOfEntryPoint
在这里插入图片描述
在这里插入图片描述
DataDirectory:
在这里插入图片描述如导入表的RVA:8000H,大小:1964
在这里插入图片描述
当要从PE文件中读取所需要的区块(节)时,不能以区块名作为定位的标准,应该以DataDirectoryVirtualAddress作为标准,因为区块名没有任何意义。

3. 节表

紧挨在可选头之后,由文件头标识有8个节区
一个IMAGE_SECTION_HEADER大小为40字节,图中从0x00000218开始,下图为第一个节区对应的节表
在这里插入图片描述
ISH结构体定义如下:
在这里插入图片描述

Name:8B,多余的自动截断。图中为
在这里插入图片描述
VirtualSize:该区块的数据没有进行对齐处理前的实际大小。

VirtualAddress:装入内存中的RVA地址,按内存页对齐,因此其数值是OptionalHeader->SectionAlignment的整数倍
如内存分页大小为4096B
在这里插入图片描述
两个节的RVA分别为4096和12288
在这里插入图片描述

4.节

一个区块(节)中的数据仅仅只是由于属性相同而放在一起,并不一定是同一种用途的内容。如输入表、输出表等就有可能和只读常量一起被放在同一个区块中,因为他们的属性都是可读不可写的

几种地址

文件偏移地址FileOffset

PE文件中某处相对于文件头的地址

基地址ImageBase

内存中PE文件的头地址

虚拟地址VA

内存中某处相对于基地址的位置

相对虚拟地址RVA

内存中某一虚拟地址相对于基地址的偏移量

PE文件映射到内存

注意:块与块之间有空隙,因为要对齐
在这里插入图片描述

映射与装载的区别:
在这里插入图片描述

RVA转FileOffset

PE文件在磁盘上和内存上的结构是一致的,略有不同的是,在磁盘上PE文件是按照可选头的FileAlignment值对齐的,在内存中,PE文件是按可选头的SectionAlignment值对齐的。

FA的最小值是512B,Win32下的SA值一般为4096B

如果PE文件的SA值和FA值相等,则其磁盘文件结构与内存映像的结构是完全一样的。如果不相等,则略有差异。

当知道某数据的RVA,想要在文件中读取同样的数据时,就要将RVA转换为FileOffset,反之同理。

如:
在这里插入图片描述
观察输入表
RVA:6000H
ImageBase:40000H
SectionAlignment:1000H
FileAlignment:200H
内容:
在这里插入图片描述
接下来进行RVA到FileOffset的转换:
总体的思路是:

输入表属于节表数据的部分,所以首先要判断此RVA在哪一节。
依次计算各节的RVA范围:起始RVA 到 起始RVA+节数据大小
判断出在哪一节之后,用此RVA减去此节的起始RVA,得到一个偏移量。
无论是在内存中还是在文件中,这个偏移量都是相同的。
而节的FileOffset在节表中存着(IMAGE_SECTION_HEADER->PointerTORawData)
所以可以得到此RVA的FileOffset = 节FileOffset + 目标RVA相对于节的偏移量。
  1. 查看区段表
    在这里插入图片描述
    .text节的RVA范围是:VisualOffset 到 VisualOffset + RawDataSize
    即:1000H到2788H
    .data节的RVA范围是:3000H到3030H

    .idata节的RVA范围是6000H到659EH
  2. 输入表的RVA为6000H,属于.idata节。
  3. 输入表的RVA相对于.idata节的偏移量为6000H - 6000H = 0
  4. .idata节的ROffset (2400H)即此节的文件偏移地址
  5. 用2400H + 0H 即得到导入表的FileOffset 2400H

使用C32ASM查看其文件偏移地址为2400H的内容,一致
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值