PE文件结构详解(一)基本概念

PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名。那Windows是怎么区分可执行文件和非可执行文件的呢?我们调用LoadLibrary传递了一个文件名,系统是如何判断这个文件是一个合 法的动态库呢?这就涉及到PE文件结构了。

PE文件的结构一般来说如下图所示:从起始位置开始依次是DOS头,NT头,节表以及具体的节。

  • DOS头是用来兼容MS-DOS操作系统的,目的是当这个文件在MS-DOS上运行时提示一段文字,大部分情况下是:This program cannot be run in DOS mode.还有一个目的,就是指明NT头在文件中的位置。
  • NT头包含windows PE文件的主要信息,其中包括一个‘PE’字样的签名,PE文件头(IMAGE_FILE_HEADER)和PE可选头(IMAGE_OPTIONAL_HEADER32),头部的详细结构以及其具体意义在PE文件头文章中详细描述。
  • 节表:是PE文件后续节的描述,windows根据节表的描述加载每个节。
  • 节:每个节实际上是一个容器,可以包含代码、数据等等,每个节可以有独立的内存权限,比如代码节默认有读/执行权限,节的名字和数量可以自己定义,未必是上图中的三个。

当一个
PE文件被加载到内存中以后,我们称之为“映象”(image),一般来说,PE文件在硬盘上和在内存里是不完全一样的,被加载到内存以后其占用的虚拟地
址空间要比在硬盘上占用的空间大一些,这是因为各个节在硬盘上是连续的,而在内存中是按页对齐的,所以加载到内存以后节之间会出现一些“空洞”。

因为存在这种对齐,所以在PE结构内部,表示某个位置的地址采用了两种方式,针对在硬盘上存储文件中的地址,称为原始存储地址或物理地址表示距离文件头的偏移;另外一种是针对加载到内存以后映象中的地址,称为相对虚拟地址(RVA),表示相对内存映象头的偏移。

然而
CPU的某些指令是需要使用绝对地址的,比如取全局变量的地址,传递函数的地址编译以后的汇编指令中肯定需要用到绝对地址而不是相对映象头的偏移,因此
PE文件会建议操作系统将其加载到某个内存地址(这个叫基地址),编译器便根据这个地址求出代码中一些全局变量和函数的地址,并将这些地址用到对应的指令
中。例如在IDA里看上去是这个样子:

这种表示方式叫做虚拟地址(VA)。

也许有
人要问,既然有VA这么简单的表示方式为什么还要有前面的RVA呢?因为虽然PE文件为自己指定加载的基地址,但是windows有茫茫多的DLL,而且
每个软件也有自己的DLL,如果指定的地址已经被别的DLL占了怎么办?如果PE文件无法加载到预期的地址,那么系统会帮他重新选择一个合适的基地址将他
加载到此处,这时原有的VA就全部失效了,NT头保存了PE文件加载所需的信息,在不知道PE会加载到哪个基地址之前,VA是无效的,所以在PE文件头中
大部分是使用RVA来表示地址的,而在代码中是用VA表示全局变量和函数地址的。那又有人要问了,既然加载基址变了以后VA都失效了,那存在于代码中的那些VA怎么办呢?答案是:重定位。系统有自己的办法修正这些值,到后续重定位表的文章中会详细描述。既然有重定位,为什么NT头不能依靠重定位采用VA表示地址呢(十万个为什么)?因为不是所有的PE都有重定位,早期的EXE就是没有重定位的。

我们都知道PE文件可以导出函数让其他的PE文件使用,也可以从其他PE文件导入函数,这些是如何做到的?PE文件通过导出表指明自己导出那些函数,通过导入表指明需要从哪些模块导入哪些函数。导入和导出表的具体结构会在单独的文章中详细解释。

好了,看了这篇文章,相信大家应该对PE文件有个整体的了解了,以后的篇章,我会将整个PE文件常见部分进行“拆解“,敬请期待。

时间: 2024-10-19 19:30:51

PE文件结构详解(一)基本概念的相关文章

PE文件结构详解(二)可执行文件头

在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格式,重要的信息都包含在头部,顺着头部的信息,可以引导系统解析整个文件.所以,我们先来认识一下PE文件的头部格式.还记得上篇里的那个图吗? DOS头和NT头就是PE文件中两个重要的文件头. 一.DOS头 DOS头的作用是兼容MS-DOS 操作系统中的可执行文件,对于32位PE文件来说,DOS所起的作用

PE文件结构详解(六)重定位

前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里,IE的iexplorer.exe导入了Kernel32.dll的GetCommandLineA函数,可以看到这是个间接call,00401004这个地址的内存里保存了目的地址, 根据图中显示的符号信息可知,00401004这个地址是存在于iexplorer.exe模块中的,实际上也就是一项IAT的

PE文件结构详解(四)PE导入表

PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPORT,即导入表. 也许大家注意到过,在IMAGE_DATA_DIRECTORY中,有几项的名字都和导入表有关系,其中包括:IMAGE_DIRECTORY_ENTRY_IMPORT,IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,IMAGE_DIRECTORY_ENTRY_IAT

PE文件结构详解(三)PE导出表

上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,DataDirectory中的每一项都可以用这个函数获取,函数原型如下: PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size); Base:模

PE文件结构详解(五)延迟导入表

PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因为有些导入函数可能使用的频率比较低,或者在某些特定的场 合才会用到,而有些函数可能要在程序运行一段时间后才会用到,这些函数可以等到他实际使用的时候再去加载对应的DLL,而没必要再程序一装载就初始化好. 这个机制听起来很诱人,因为他可以加快启动速度,我们应该如何利用这项机制呢?VC有一个选项,可以让我

PE文件结构详解

1.定位标准PE头 DOS Stub长度不固定,所以DOS头不是一个固定大小的数据结构.DOS头位于PE的起始位置,通过DOS头去定位后面标准PE头的位置就是通过字段e_lfanew. e_lfanew字段的值是一个相对偏移量,绝对定位时需要加上DOS MZ头的基地址. 也就是PE头的绝对位置是: PE_start = DOS MZ 基地址+IMAGE_DOS_HEADER.e_lfanew 2.PE文件结构 在32位系统下,最重要的部分是PE头和PE数据区. 32位系统下的PE文件被划分为:D

Andriod APK 文件结构详解

APK文件结构 APK文件实际是一个zip压缩包,可以通过解压缩工具解开. 下面是用解压缩工具解开的APK包的结构: |-- AndroidManifest.xml |-- META-INF | |-- CERT.RSA | |-- CERT.SF | `-- MANIFEST.MF |-- classes.dex |-- res | |-- drawable | | `-- icon.png | `-- layout | `-- main.xml `-- resources.arsc Mani

PE文件格式详解(四)

PE文件格式详解(四) 0x00 前言 上一篇介绍了区块表的信息,以及如何在hexwrokshop找到区块表.接下来,我们继续深入了解区块,并且学会文件偏移和虚拟地址转换的知识. 0x01 区块对齐值 首先我们要知道啥事区块对齐?为啥要区块对齐?这个问题其实困扰了我很久,只能怪我操作系统没学好...我现在的理解是由于内存和磁盘存在分页的问题所以使得不同区块一般要放到不同的分页中,当然也可以多个区块合并以节省空间,但是对于不能合并的区块如代码和数据块就不得不放在不同分页上了.学过操作系统的都知道不

Android入门——Fragment详解之基本概念与用法(一)

引言 Android在3.0中引入了Fragments的概念,其目的是用在大屏幕设备上–例如平板电脑上,支持更加动态和灵活的UI设计.平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互.Fragment允许这样的一种设计,而不需要你亲自来管理 Viewhierarchy的复杂变化. 通过将Activity的布局分散到Fragment中, 你可以在运行时修改Activity的外观,并在由Activity管理的back stack中保存那些变化. 一.F