PE结构(二)PE头字段说明

2024-04-25 10:44
文章标签 说明 结构 pe 头字段

本文主要是介绍PE结构(二)PE头字段说明,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PE头字段 = DOS头 + PE标记 + 标准PE头 + 可选PE头

我们今天分析一下PE头字段中所有重要成员的含义

DOS头

DOS头中我们需要去分析的是如下两个成员:

1.WORD e_magic:MZ标记,用于判断是否为可执行文件,即如果显示4D 5A,说明该文件是一个可执行文件,如:.sys/.dll/.exe等

2.DWORD e_lfanew:NT头(PE签名)相对于文件首地址的偏移,用于定位PE文件真正开始的地址,但该值不是固定的

PE标记

该PE标记的地址即e_Ifanew指向的地址

PE标记中我们要分析的成员只有这一个:

DWORD Signature;  该值存储的是PE两个字,用来表示该文件是PE文件,因此一个可执行文件应该同时满足MZ标记和PE标记,如果这两点不满足可能被修改过,或者就不是一个可执行文件

标准PE头

标准PE头中我们需要去分析的是如下几个成员:

1.WORD Machine:用于约定该文件能在什么样的CPU上运行:如果是0x00表示能在任何CPU上执行,如果是0x14C表示能在386及后续CPU上执行

2.WORD NumberOfSections:用于告知该PE文件中分节的总数(不包括DOS头、NT头、节表):如果要新增节或者合并节,就要修改这个值。

4.DWORD TimeDateStamp:时间戳:文件的创建时间(和操作系统的创建时间无关),当程序被编译器编译时,由编译器填写的

时间戳的使用案例:比如现在要给一个.exe文件加壳,有些加壳软件不光要提供给.exe文件,还需要其对应的.map文件。.map文件中记录了此.exe文件中的所有的函数的名字、地址、参数等信息。这两个文件由编译器编译源文件时生成,.map和.exe的时间戳是一致的。如果现在这个.exe文件被反复修改了以后,.exe文件的时间戳就会改变,但是map的时间戳没有更新,这会导致exe和map不同步。此时如果加壳还是按照map中的记录信息去加壳,就可能出现错误。所以很多加壳软件在加壳之前会检查exe和map文件的时间戳是否一致

5. WORD SizeOfOptionalHeader:可选PE头的大小,该大小不确定:32位PE文件默认E0h,64位PE文件默认为F0h,但其大小可以自定义

6. WORD Characteristics:特征:其大小16位的每个位都表示不同的特征,可执行文件值一般都是0x010F。当某位为1时,表示此文件有此位对应的特征,为0时表示没有此特征

characteristic每一位表示的含义如下图:

把内存中的值读出来后,即读作0x010F,此时化成二进制,第七位省略,其他的每一位都表示一个特征,如果为1,则表示此文件有此位对应的特征;为0表示没有此特征

可选PE头

可选PE头中我们需要去分析的是如下几个成员:

  1. Magic:说明文件类型:如果值为0x010B,表示是32位下的PE文件;如果值为0x020B,表示是64位下的PE文件
  2. SizeOfCode:所有代码节大小的和,必须是FileAlignment的整数倍

比如文件只有一个代码节,大小为100h字节,如果文件对齐粒度是200h,那么会补0填充够200h字节,所以会显示200h(编译器填的);若文件有两个代码节,两个都是10字节,这个值应为400h。但是计算机发展到现在已经不使用这个值了,改了也没事,删除它程序也可以正常运行,但是现在之所以保留下来是因为向下兼容之前的文件

3.SizeOfInitializedData:已初始化数据(比如定义一个有值的全局变量,便是已初始化的数据)大小的和,必须是FileAlignment的整数倍:由编译器填写,目前计算机已经不使用这个值了,保留下来只是为了向下兼容。

4.SizeOfUninitializedData:未初始化数据(比如定义一个没有值的全局变量,便是未初始化的数据)大小的和,必须是FileAlignment的整数倍:编译器填的,现在没用了。

5.AddressOfEntryPoint:程序入口点OEP,即程序真正执行的起始地址:这个值是相对于文件基地址偏移的程序入口地址,而不是真正运行在内存中的程序入口地址。文件装入到4GB虚拟内存中的起始基地址 +相对于文件首地址偏移的程序入口地址,即imagebase + AddressOfEntryPoint,这个值才是真正运行在4GB虚拟内存中的程序入口地址。虽然该值在不同文件中有不同的值,但一般都是0x0040000开始,这是系统的默认ImageBase值。该值我们可以自定义修改,但要保证它可以执行

注意:程序入口在默认情况下一般都在.text节.code代码块当中,且OEP不是只能在.code代码块开始的位置,也可以从此块当中的任何合理位置,或者在其他节(如自定义.tttt节等)的任意合理位置,又或者在数据中。

6.BaseOfCode:代码开始的基址:即PE文件加载到内存后,所有代码的起始地址。编译器填写的,可以改,但不影响程序执行

7.BaseOfData:内存中所有数据开始的地址:编译器填的,程序运行用不到,想改就改

8. ImageBase: 内存镜像基址:该值可以看作模块之间的对齐粒度,也是程序运行装载到自己的虚拟4GB内存后的起始位置。imagebase一般都是0x00400000(具体原因参考上一篇对齐部分),这是系统的默认值,不能超过0x80000000,这是因为我们写的程序的数据只能在内存的2GB用户区中,不能占用2GB系统区。

模块的概念:一个.exe文件可能是由一堆PE文件组成的,每个pe文件都有自己的imagebase,他们共用一个4GB虚拟内存。这是因为.exe文件本身是一个PE文件,满足PE结构,但是.exe中可能还用到了很多.dll,每一个.dll也是一个PE文件,也满足PE结构,这些.dll有自己的功能和作用,当它们拼凑到一个.exe文件中时,.exe文件就有了完整的功能。这时又称.exe文件有很多模块构成,每一个.dll都是一个模块

pe文件(各个模块)不能从0开始的原因是因为内存保护,我们前面学过,free一个动态分配内存的指针后,一定要将指针指向NULL,那么当指针指向NULL后,这个指针指向的地址就是0x0,从0开始往后偏移一定范围的地址,操作系统都把他的地址上内存空了出来,那么此时指针访问这部分的数据,编译器会立马报错,也就是说限制了指针使其不能为所欲为的访问内存。

9.SectionAlignment:内存对齐粒度:可执行文件运行时装入4GB虚拟内存中的对齐粒度,一般为0x1000字节

10.DWORD FileAlignment:文件对齐粒度:可执行文件在硬盘的对齐粒度,一般为0x200字节,还有的是0x1000字节,和内存对齐粒度相等

举例说明内存对齐和文件对齐:(day27.1-PE结构概况中详细说明过)

11. DWORD  SizeOfImage:内存中整个PE文件的映射尺寸:即文件运行时在4GB虚拟内存中的整个文件数据大小。该值可以比实际的值大,但必须是SectionAlignment的整数倍

12. DWORD SizeOfHeaders:所有头+节表按照文件对齐后的大小:即DOS头 + 垃圾数据 + PE签名 + 标准PE头 + 可选PE头 + 节表,按照文件对齐后的大小。必须是FileAlignment的整数倍,否则加载会出错

举例:比如一个可执行文件的所有头和节表加起来大小为0x1800字节,但是因为要满足文件对齐粒度0x1000,示意图SizeOfHeaders的值应该为0x2000,

13. DWORD CheckSum:校验和:一些重要的系统文件、驱动文件等对此有要求,用来判断文件是否被修改(但是可以修改这个值)

校验方法举例:把PE文件中的所有数据中两个字节的值(十六进制)两两相加(如值1 2 3 4,相加:1+2,3+4),最后将所有两两相加的结果再求和得到一个数,存放到checksum表示的4字节内存中,内存可溢出

14. DWORD SizeOfStackReserve:初始化时保留的栈大小 (最大值)

15. DWORD SizeOfStackCommit:初始化时实际提交的栈大小

16. DWORD SizeOfHeapReserve:初始化时保留的堆大小 (最大值)

17. DWORD SizeOfHeapCommit:初始化时实际提交的堆大小

18. DWORD NumberOfRvaAndSizes:目录项数目“如果该值是0x10,表示下面的_IMAGE_DATA_DIRECTORY DataDirectory[16]结构体有16个,一个占8字节,这些结构体用于告诉我们编译器在编译文件时往exe文件中所添加的数据。具体如下图所示:

可执行文件的读取到装入内存过程

文件装入内存流程如图所示:

1.编译器生成.exePE文件

比如我们使用VC,编写程序以后按下F7,编译器会编译生成对应的.exe可执行文件,此时编译器计算生成PE文件的所有数据,比如imagebase或者OEP等全部字段信息,最后将.exe文件保存到硬盘中

2.文件数据读到内存(FileBuffer)

当文件数据读取到内存中时,其数据存储是完全照搬文件在硬盘上的数据。此时文件的格式并非windows运行格式,所以Windows操作系统还无法运行它

3.将文件装载到内存镜像(ImageBuffer)

将文件从FileBuffer装入ImageBuffer,即将文件从imagebase开始对齐拉伸成内存对齐形式的一块内存。这个过程就是将文件装入自己的4GB虚拟内存中,称为PE loader。我们称将文件拉伸装载后的内存为imageBuffer,即内存镜像。此时文件的格式满足windows运行格式。对于硬盘对齐粒度等于内存对齐粒度的文件不需要进行拉伸

此时文件在4GB虚拟内存中的起始地址,就是imagebase,一般为0x00400000,接着就可以通过imagebase + addressofentrypoint找到文件装载到内存后真正的程序入口地址;或者用imagebase加上一些偏移地址值就可以得到文件其他内容在运行时装入4GB内存后的地址

4.操作系统将虚拟地址转化成物理地址

FileBuffer和ImageBuffer提到的所有地址都是虚拟地址,操作系统最后会将这些虚拟地址转换为物理地址,这样才算真正的装入到真实内存中。

这篇关于PE结构(二)PE头字段说明的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/934462

相关文章

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

MySQL之InnoDB存储引擎中的索引用法及说明

《MySQL之InnoDB存储引擎中的索引用法及说明》:本文主要介绍MySQL之InnoDB存储引擎中的索引用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录1、背景2、准备3、正篇【1】存储用户记录的数据页【2】存储目录项记录的数据页【3】聚簇索引【4】二

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

MySQL中的索引结构和分类实战案例详解

《MySQL中的索引结构和分类实战案例详解》本文详解MySQL索引结构与分类,涵盖B树、B+树、哈希及全文索引,分析其原理与优劣势,并结合实战案例探讨创建、管理及优化技巧,助力提升查询性能,感兴趣的朋... 目录一、索引概述1.1 索引的定义与作用1.2 索引的基本原理二、索引结构详解2.1 B树索引2.2

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho

Maven中的profiles使用及说明

《Maven中的profiles使用及说明》:本文主要介绍Maven中的profiles使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录主要用途定义 Profiles示例:多环境配置激活 Profiles示例:资源过滤示例:依赖管理总结Maven 中的

Python循环结构全面解析

《Python循环结构全面解析》循环中的代码会执行特定的次数,或者是执行到特定条件成立时结束循环,或者是针对某一集合中的所有项目都执行一次,这篇文章给大家介绍Python循环结构解析,感兴趣的朋友跟随... 目录for-in循环while循环循环控制语句break语句continue语句else子句嵌套的循

Before和BeforeClass的区别及说明

《Before和BeforeClass的区别及说明》:本文主要介绍Before和BeforeClass的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Before和BeforeClass的区别一个简单的例子当运行这个测试类时总结Before和Befor

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以