PE文件结构分析

  • 基本概念
    • 整体结构
    • 基地址
    • 相对虚拟地址
    • 文件偏移地址
    • DOS头
    • NT头(内容多)
    • PE区段分析
    • PE文件的输入输出表
    • 重定位表

基本概念
  • 认识PE文件
    PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)。

EXE和DLL文件之间的区别完全是语义上的,他们使用完全相同的PE格式。唯一的区别就是用一个字段标识出这个文件是EXE还是DLL。还有许多DLL的扩展,如OCX控件和控制面板程序(.CPL文件)等都是DLL,它们有一样的实体。

64位的Windows只是对PE格式做了一些简单的修饰,新格式叫PE32+。没有新的结构加进去,其余的改变只是简单地将以前的32位字段扩展成64位。对于C++代码,Windows文件头的配置使其拥有不明显的区别。

整体结构

PE结构一般来说:从起始位置开始依次是DOS头、NT头、节表以及具体的节。

基地址

程序加载进内存的起始地址。
当PE文件通过Windows加载器被装入内存后,内存中的版本被称作模块(Module)。映射文件的起始地址被称为模块句柄(hModule),可以通过模块句柄访问内存中其他的数据结构。这个初始内存地址也称为基地址(ImageBase)。我们可以通过Pchunter工具查看程序基地址。

相对虚拟地址

又称作RVA,它是一个“相对”地址,或称为“偏移量”。存中的一个简单的相对于PE文件装入地址的偏移位置。顺便说一下,在PE用语里,实际的内存地址被称作虚拟地址(Virtual Address,简称VA),另外也可以把虚拟地址想象为加上首选装入地址的RVA。不要忘了前面提到的装入地址等同于模块句柄。它们之间的关系如下:

虚拟地址(VA)=基地址(ImageBase)+相对虚拟地址(RVA)

文件偏移地址

当PE文件储存在磁盘上时, 某个数据的位置相对于文件头的偏移 量,称为文件偏移地址(File Offset) 或物理地址(RAW Offset)。文件偏 移地址从PE文件的第一个字节开始计 数,起始值为0。用十六进制工具 (例如WinHex、C32等)打开文件 所显示的地址就是文件偏移地址。

DOS头

每一个PE文件都是以一个DOS程序开始的,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行DOS stub(DOS块)。DOS stub其实就是一个有效的EXE,如果OS是不支持PE文件的,那么它将显示为一个错误提示

对于DOS头信息我们只需要关注两个字段即可:e_magic和e_lfanew,e_magic字段(一个字大小)需要被设置为值5A4Dh,这个值有个#define,名为IMAGE_DOS_SIGNATURE,ASCII表示法里,它的ASCII值为“MZ”,是MS-DOS的最初创建者之一Mark Zbikowski字母的缩写。e_lfanew字段是真正PE文件头(NT头)的相对偏移,其指出真正PE头的文件偏移位置,它占用4个字节,位于文件开始偏移3Ch字节中。这里是 0x000000D0,也就代表偏移0xd0处是NT头的开始位置

NT头(内容多)

有三个成员:PE签名、PE文件头、PE可选头

1、NT头结构信息-PE签名
在一个有效的PE文件里,Signature字段被设置为00004550h,ASCII码字符是“PE00”,#define IMAGE_NT_SIGNATURE定义了这个值。这个值也通常被用来判断是否为标准的PE文件。(通常是跟MZ头一起判断)

2、NT头结构信息-PE文件头
IMAGE_FILE_HEADER(PE文件头)结构包含了PE文件的一些基本信息,最重要的是其中一个域指出了IMAGE_OPTIONAL_HEADER的大小。下面介绍IMAGE_FILE_HEADER结构的各个字段以及对这些字段的额外说明,这个结构也能在COFF格式的OBJ文件的最开始处找到,因此也称为COFF File Header。

3、NT头结构信息-PE可选头
(IMAGE_OPTIONAL_HEADER)是一个可选的结构,但实际上IMAGE_FILE_HEADER结构不足以定义PE文件属性,因此可选映像头中定义了更多的数据,完全不必考虑两个结构区别在哪里,两者连起来就是一个完整的“PE文件头结构”。使用Stud_PE查看文件的可选头结构如下

我们只对这些字段做解释分析。

(1)Magic:是一个标记字,说明文件是ROM映像(0107h),还是普通可执行的映像(010Bh),一般是010Bh,如是PE32+,则是020Bh。
(2)MajorLinkerVersion:链接程序的主版本号。
(3)MinorLinkerVersion:链接程序的次版本号。
(4)SizeOfCode:所有带有IMAGE_SCN_CNT_CODE属性区块的总共大小(只入不舍),这个值是向上对齐某一个值的整数倍。例如,本例是200h,即对齐的是一个磁盘扇区字节数(200h)的整数倍。通常情况下,多数文件只有一个Code块,所以这个字段和.text块的大小匹配。
(5)SizeOfInitializedData:已初始化数据块的大小,即在编译时所构成的块的大小(不包括代码段)。但这个数据并不太准确。

(6)SizeOfUninitializedData:未初始化数据块的大小,装载程序要在虚拟地址空间中为这些数据约定空间。这些块在磁盘文件中不占空间,就像“UninitializedData”这一术语所暗示的一样,这些块在程序开始运行时没有指定值。未初始化数据通常在.bss块中。
(7)AddressOfEntryPoint:程序执行入口RVA。通常我们说的程序入口点或者OEP就是它。
(8)BaseOfCode:代码段的起始RVA。
(9)BaseOfData:数据段的起始RVA。数据段通常是在内存的末尾,即PE文件头和Code Section之后。可是,这个域的值对于不同版本的微软链接器是不一致的,在64位可执行文件中是不出现的。
(10)ImageBase:文件在内存中的首选装入地址(基地址)。如果有可能(也就是说,目前如果没有其他占据这块地址,它是正确对齐的并且是一个合法的地址,等等),加载器试图在这个地址装入PE文件。如果可执行文件是在这个地址装入的,那么加载器将跳过应用基址重定位的步骤。

(11)SectionAlignment:当被装入内存时的区块对齐大小。每个区块被装入的地址必定是本字段指定数值的整数倍。默认的对齐尺寸是目标CPU的页尺寸。对于运行在Windows 9x/Me下的用户模式可执行文件,最小的对齐尺寸是一页1000h(4KB)。这个字段可以通过链接器的/ALIGN开关来设置。在IA-64上,是按8KB来排列的。
(12)FileAlignment:磁盘上PE文件内的区块对齐大小,组成块的原始数据必须保证从本字段的倍数地址开始。对于x86可执行文件,这个值通常是200h或1000h,这是为了保证块总是从磁盘的扇区开始,这个字段的功能等价于NE格式文件中的段/资源对齐因子。用不同版本的微软链接器默认值会改变。这个值必须是2的幂,其最小值为200h,并且如果SectionAlignment小于CPU的页尺寸,这个域必须与SectionAlignment匹配。链接器开关/OPT:WIN98设置x86可执行文件的文件对齐为1000h,/OPT:NOWIN98设置对齐为200h。
(13)MajorOperatingSystemVersion:要求操作系统的最低版本号的主版本号。随着这么多版本的Windows的到来,这个字段明显地变得不切题了。
(14)MinorOperatingSystemVersion:要求操作系统的最低版本号的次版本号。

(15)MajorImageVersion:该可执行文件的主版本号,由程序员定义。它不被系统使用并可以设置为0,可以通过链接器的/VERSION开关设置它。
(16)MinorImageVersion:该可执行文件的次版本号,由程序员定义。
(17)MajorSubsystemVersion:要求最低子系统版本的主版本号。这个值与下一个字段一起,通常被设置为4,可以通过链接器开关/SUBSYSTEM来设置。
(18)MinorSubsystemVersion:要求最低子系统版本的次版本号。
(19)Win32VersionValue:另一个从来不用的字段,通常被设置为0。
(20)SizeOfImage:映像装入内存后的总尺寸。也就是在内存中所占的大小。它指装入文件从Image Base到最后一个块的大小。最后一个块根据其大小往上取整。
(21)SizeOfHeaders:是MS-DOS头部、PE头部、区块表的组合尺寸。所有这些项目都出现在PE文件中任何代码或数据区块之前。域值四舍五入至文件对齐的倍数。

(22)CheckSum:映像的校验和。IMAGEHLP.DLL中的CheckSumMappedFile函数可以计算这个值。一般的EXE文件可以是0,但一些内核模式的驱动程序和系统DLL必须有一个检验和。当链接器的/RELEASE开关被使用时,校验和被置于文件中。
(23)Subsystem:一个标明可执行文件所期望的子系统(用户界面类型)的枚举值。这个值只对EXE是重要的
(24)DllCharacteristics:DllMain()函数何时被调用,默认为0。
(25)SizeOfStackReserve:在EXE文件里,为线程保留的堆栈大小。它一开始只提交其中一部分,只有在必要时,才提交剩下的部分。
(26)SizeOfStackCommit:在EXE文件里,一开始即被委派给堆栈的内存数量。默认值是4KB。
(27)SizeOfHeapReserve:在EXE文件里,为进程的默认堆保留的内存。默认值是1MB,但是在当前版本的Windows里,堆值在用户不干涉的情况下就能增长超过这个值。

(28)SizeOfHeapCommit:在EXE文件里,委派给堆的内存大小。默认值是4KB。
(29)LoaderFlags:与调试有关,默认为0。
(30)NumberOfRvaAndSizes:数据目录的项数。这个字段从最早的Windows NT发布以来一直是16。
(31)DataDirectory[16]:数据目录表,由数个相同的IMAGE_DATA_DIRECTORY结构组成,指向输出表、输入表、资源块等数据。

PE区段分析

区段概念:在PE文件头与原始数据之间存在一个区块表(sectio Table),区块表包含每个块在映像中的信息,分别指向不同的区块实体。

区段表:紧跟着NT头后的是区块表它是一个IMAGE_SECTION_HEADER结构数组。每个IMAGE_SECTION_HEADER结构包含了它所关联区块的信息,如位置、长度、属性;该数组的数目由IMAGE_NT_HEADERS.FileHeader.NumberOfSections指出。

  • 区段表分析
    IMAGE_SECTION_HEADER结构各个字段的解释如下:

(1)Name:区段名
(2)VirtualSize:指出实际的、被使用的区块大小。
(3)VirtualAddress:区段装载到内存中的RVA。
(4)SizeOfRawData:区段在磁盘文件中所占的大小。
(5)PointerToRawData:区段在磁盘文件中的偏移。
(6)PointerToRelocations:这部分在EXE文件中无意义。
(7)PointerToLinenumbers:行号表在文件中的偏移值。这是文件的调试信息。
(8)NumberOfRelocations:这部分在EXE文件中无意义。
(9)NumberOfLinenumbers:区段在行号表中的行号数目。
(10)Characteristics:块属性。该字段是一组指出块属性(如代码/数据/可读/可写等)的标志。

一些其他的注意事项:

1.区段名是可以被随意修改的,所以默认的区段名知识给我们一些参考,很多恶意软件或者加壳之后的软件都会去修改区区段名称等信息。

2.区块的大小是要对齐的,有两种对齐值,一种用于磁盘文件内,另一种用于内存中。PE文件头指出了这两个值,他们可以不同。

3.恶意代码或者壳进场会在区段上下文章,所以掌握区段的一些操作知识对分析恶意代码以及脱壳会有很大的帮助。

PE文件的输入输出表

输入表(IT、导入表):

1.可执行文件使用来自于其他DLL的代码或数据时,成为输入。当PE文件装入时,Windows加载器的工作之一就是定位所有被输入的函数和数据,并且让整座被装入的文件可以使用那些地址。这个过程是通过PE文件的输入表来完成的,输入表中保存的是函数名和驻留的DLL名等动态链接所需的信息。

2.PE文件头的可选映像头中数据目录表的第二成员指向输入表。输入表以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始。每个被PE文件隐式链接进来的DLL都有一个LLD,在这个数组中,没有字段指出该结构数组的项数,但它的最后一个单元是NULL,可以由此计算出该数组的项数。例如,某个PE文件从两个DLL文件中引入函数,就存在两个LLD结构来描述这些DLL文件,并在两个DLL结构的最后一个内容全为0的LLD结构作为结束。

输出表:

当创建一个DLL时,实际上创建了一组能让EXE或其他DLL调用的一组函数,此时PE装载器根据DLL文件中输出信息修正被执行文件的IAT。当一个DLL函数能被EXE或另一个DLL文件使用时,那就是被输出了。输出的信息就保存在了输出表中,DLL文件通过输出表想系统提供输出函数名,序号和入口地址等信息。
EXE文件一般不存在输出表,而大部分DLL文件中存在输出表

注意:输出表(Export Table)中的主要成分是一个表格,内含函数名称、输出序数等。序数是指定DLL中某个函数的16位数字,在所指向的DLL里是独一无二的。在此不提倡仅仅通过序数引出函数这种方法,这会带来DLL维护上的问题。一旦DLL升级或修改,调用该DLL的程序将无法工作。

重定位表

当链接器生成一个PE文件时,它假设这个文 件执行时会被装载到默认的基地址处,并且把 code和data的相关地址都写入PE文件中。如果 装入时按默认的值作为基地址装入,则不需要重 定位。但如果可执行文件被装载到虚拟内存的另 一个地址,链接器所登记的那个地址就是错误的 ,这时就需要用重定位表来调整。在PE文件中 ,它往往单独分为一块,用“.reloc”表示。

只要了解怎么去使用工具查看与修改资源即可。

64位PE文件

1、64位的Windows只是对PE格式做了一些简单的修饰,新格式叫PE32+。没有新的结构加进去,其余的改变只是简单地将以前的32位字段扩展成64位。对于C++代码,Windows文件头的配置使其拥有不明显的区别。

2、PE文件中的数据结构一般都有32位和64位之分,如IMAGE_NT_HEADERS32、IMAGE_NT_HEADER64等。除了在64位版本中的一些扩展域以外,这些结构几乎总是一样的。在winnt.h都有#defines,它可以选择适当的32位或64位结构并给它们起成与大小无关的别名(在前面的例子中,可以写成IMAGE_NT_HEADERS)。结构选择依赖于用户正在编译的模式(尤其_WIN64是否被定义)。

希望大家可以有所收获!!!

[re入门]PE文件结构相关推荐

  1. PE文件结构 - 数据目录表学习

    数据目录表,是一个结构体数组.数组里的每个元素对应一个数据表.通常有16个. 数组每个元素都是一个结构体,结构体如下 typedef struct _IMAGE_DATA_DIRECTORY {    ...

  2. PE文件结构(五岁以下儿童)基地搬迁

    PE文件结构(五岁以下儿童) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 基址重定位 链接器生成一个PE文件时,它会如果程序被装入时使用的默认ImageBase基地址(VC默认 ...

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

    前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...

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

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/12718845 PE文件结构详解(四)PE导入表讲了一般的 ...

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

    PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...

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

    上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...

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

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/11903197 在PE文件结构详解(一)基本概念里,解释了 ...

  8. 【转】PE文件结构详解--(完整版)

    (一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...

  9. 再探.NET的PE文件结构(安全篇)

    一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软 ...

最新文章

  1. dtm文件生成等高线 lisp_南方cass如何用图面高程点生成等高线
  2. shiro 的 @RequiresPermissions 注解使用
  3. TQ210——常见问题
  4. 添加列属性_css多列属性
  5. 高铁车厢女童突然抽搐 护士为防其咬舌将手指放口中
  6. 【转】Java 单例模式详解
  7. 牛客 2021年度训练联盟热身训练赛第二场 D题Soccer Standings
  8. Python 数据结构与算法——归并排序
  9. 变量、属性、函数、方法总结
  10. 高德地图JS-API (超简单Get新技能√)
  11. Spring Security 原理
  12. GWAS: 网页版的基因型填充(genotype imputation)
  13. json数据条件查询,json数据sql查询中文乱码
  14. C# 数据操作系列 - 13 SugarSql初探
  15. Java web实现百度地图导航
  16. python正则表达式编译_用Python编译正则表达式
  17. Novavax向美国提交新冠疫苗紧急使用授权申请;西门子医疗发布最新业绩 | 医药健闻...
  18. win11下安装Cuda和Cudnn,保姆及教程!!!
  19. 〔摘录转载〕字体彩蛋
  20. matlab 降维工具

热门文章

  1. PID算法原理及模板讲解
  2. fiddler抓包工具:生成证书
  3. Illustrator 2021 (AI 2021)下载 功能介绍
  4. phpmyadmin 免登陆
  5. nRF52833-QIAA-R nordic无线收发芯片
  6. UE4Material_材质属性(1)
  7. stm32ad测量范围_用STM32的AD测电压,范围是0~3.3V,但是输入电压可能高于3.3,怎么保护STM32?...
  8. TensorFlow识别复杂验证码以及搭建生产环境(1)—— 收集训练集
  9. UNIX痛恨者手册[转贴自 FreeBSDChina]
  10. 【学习整理】静态路由如何配置