《C++ 黑客编程揭秘与防范(第2版)》——6.3 PE结构的3种地址

6.3 PE结构的3种地址

C++ 黑客编程揭秘与防范(第2版)
在上一章中用OD调试器调试程序时看到的地址与本章使用C32Asm以十六进制形式查看程序时的地址形式有所差异。程序在内存中与在文件中有着不同的地址形式,而且PE相关的地址不只有这两种形式。与PE结构相关的地址形式有3种,且这3种地址形式可以进行转换。

6.3.1 与PE结构相关的3种地址

与PE结构相关的3种地址是VA(虚拟地址)、RVA(相对虚拟地址)和FileOffset(文件偏移地址)。

VA(虚拟地址):PE文件映射到内存后的地址。

RVA(相对虚拟地址):内存地址相对于映射基地址的偏移地址。

FileOffset(文件偏移地址):相对PE文件在磁盘上的文件开头的偏移地址。

这3种地址都是和PE文件结构密切相关的,前面简单地引用过这几个地址,但是前面只是个概念。从了解节表开始,这3种地址的概念就非常重要了,否则后面的很多内容都将无法理解。

这3个概念之所以重要,是因为后面要不断地使用它们,而且三者之间的关系也很重要。每个地址之间的转换也很重要,尤其是VA和FileOffset的转换、RVA和FileOffset之间的转换。这两个转换不能说复杂,但是需要一定的公式。VA和RVA的转换就非常简单了。

PE文件在磁盘上和在内存中的结构是一样的。所不同的是,在磁盘上,文件是按照IMAGE_OPTIONAL_HEADER的FileAlignment值进行对齐的。而在内存中,映像文件是按照IMAGE_OPTIONAL_HEADER的SectionAlignment进行对齐的。这两个值前面已经介绍过了,这里再进行简单的回顾。FileAlignment是以磁盘上的扇区为单位的,也就是说,FileAlignment最小为512字节,十六进制的0x200字节。而SectionAlignment是以内存分页为单位来对齐的,通常Win32平台一个内存分页为4K,也就是十六进制的0x1000字节。一般情况下,FileAlignment的值会与SectionAlignment的值相同,这样磁盘文件和内存映像的结构是完全一样的。当FileAlig-nment的值和SectionAlignment的值不相同的时候,就存在一些细微的差异了,其主要区别在于,根据对齐的实际情况而多填充了很多0值。PE文件映射如图6-15所示。

图6-15 PE文件映射图

除了文件对齐与内存对齐的差异以外,文件的起始地址从0地址开始,用C32Asm的十六进制模式查看PE文件时起始位置是0x00000000。而在内存中,它的起始地址为IMAGE_OPTIONAL_HEADER结构体的ImageBase字段(该说法只针对EXE文件,DLL文件的映射地址不一定固定,但是绝对不会是0x00000000地址)。

6.3.2 3种地址的转换

当FileAlignment和SectionAlignment的值不相同时,磁盘文件与内存映像的同一节表数据在磁盘和内存中的偏移也不相同,这样两个偏移就发生了一个需要转换的问题。当知道某数据的RVA,想要在文件中读取同样的数据的时候,就必须将RVA转换为FileOffset。反之,也是同样的情况。

下面用一个例子来介绍如何进行转换。还记得前面为了分析PE文件结构而写的那个用MessageBox()输出“Hello World”的例子程序吗?用PEID打开它,查看它的节表情况,如图6-16所示。

图6-16 PEID显示的节表内容

从图6-16的标题栏可以看到,这里不叫“节表”,而叫“区段”。还有别的资料上称之为“区块”或“节区”,只是叫法不同,内容都是一样的。

从图6-16中可以看到,节表的第一个节区的节名称为“.text”。通常情况下,第一个节表项都是代码区,入口点也通常落在这个节表项。在早期壳不流行时,通过判断入口点是否在第一个节区就可以判断该程序是否被病毒感。如今,由于壳的流行,这种判断方法就不可靠了。关键要看的是“R.偏移”,表明了该节区在文件中的起始位置。PE头部包括DOS头、PE头和节表,通常不会超过512字节,也就是说,不会超过0x200的大小。如果这个“R.偏移”为0x00001000,那么通常情况下可以确定该文件的磁盘对齐大小为0x1000(注意:这个测试程序是笔者自己写的,因此比较熟悉程序的PE结构。而且这也是一种经验的判断。严格来讲,还是要去查看IMAGE_OPTIONAL_HEADER的SectionAlignment和FileAlignment两个成员变量的值)。测试验证一下这个程序,看到“V.偏移”与“R.偏移”相同,则说明磁盘对齐与内存对齐是一样的,这样就没办法完成演示转换的工作了。不过,可以人为地修改文件对齐大小。也可以通过工具来修改文件对齐的大小。这里借助LordPE来修改其文件对齐大小。修改方法很简单,先将要修改的测试文件复制一份,以与修改后的文件做对比。打开LordPE,单击“重建PE”按钮,然后选择刚才复制的那个测试文件,如图6-17和图6-18所示。

图6-17 LordPE界面

图6-18 重建PE功能结果

PE重建功能中有压缩文件大小的功能,这里的压缩也就是修改磁盘文件的对齐值,避免过多地因对齐而进行补0,使其少占用磁盘空间。用PEID查看这个进行重建的PE文件的节表,如图6-19所示。

现在可以看到“V.偏移”与“R.偏移”的值不相同了,它们的对齐值也不相同了,大家可以自己验证一下FileAlignment和SectionAlignment的值是否相同。

图6-19 重建PE文件后的节表

现在有两个功能完全一样,而且PE结构也一样的两个文件了,唯一的不同就是其磁盘对齐大小不同。现在在这两个程序中分别寻找一个节表中的数据,学习不同地址之间的转换。

先用OD打开未进行重建PE结构的测试程序,找到反汇编中调用MessageBox()处要弹出对话框的两个字符串参数的地址,如图6-20和图6-21所示。

从图6-20和图6-21中可以看到,字符串“hello world !”的地址为0x00406030,字符串“hello”的地址为0x00406040。这两个地址都是虚拟地址,也就是VA。

将VA(虚拟地址)转换为RVA(相对虚拟地址)是很容易的,RVA(相对虚拟地址)为VA(虚拟地址)减去IMAGE_OPTIONAL_HEADER结构体中的ImageBase(映像文件的装载虚拟地址)字段的值,即RVA = VA – ImageBase = 0x00406030 – 0x00400000 = 0x0000 6030。由于IMAGE_OPTIONAL_HEADER中的SectionAlignment和FileAlignment的值相同,因此其FileOffset的值也为0x00006030。用C32Asm打开该文件查看文件偏移地址0x00006030处的内容,如图6-22所示。

图6-22 文件偏移0x00006030处的内容为“hello world!”字符串

从这个例子中可以看出,当SectionAlignment和FileAlignment相同时,同一节表项中数据的RVA(相对虚拟地址)和FileOffset(文件偏移地址)是相同的。RVA的值是用VA – ImageBase计算得到的。

再用OD打开“重建PE”后的测试程序,同样找到反汇编中调用MessageBox()函数使用的那个字符串“hello world !”,看其虚拟地址是多少。它的虚拟地址仍然是0x00406030。同样,用虚拟地址减去装载地址,相对虚拟地址的值仍然为0x00006030。不过用C32Asm打开该文件查看的话会有所不同。用C32Asm看一下0x00006030地址处的内容,如图6-23所示。

图6-23 文件偏移0x00006030处没有“hello world!”字符串

从图6-23中可以看到,用C32Asm打开该文件后,文件偏移0x00006030处并没有“hello world!”和“hello”字符串。这就是由文件对齐与内存对齐的差异所引起的。这时就要通过一些简单的计算把RVA转换为FileOffset。

把RVA转换为FileOffset的方法很简单,首先看一下当前的RVA或者是FileOffset属于哪个节。0x00006030这个RVA属于.data节。0x00006030这个RVA相对于该节的起始RVA地址0x00006000来说偏移0x30字节。再看.data节在文件中的起始位置为0x00004000,以.data节的文件起始偏移0x00004000加上0x30字节的值为0x00004030。用C32Asm看一下0x00004030地址处的内容,如图6-24所示。

图6-24 0x00004030文件偏移处的内容

从图6-24中可以看出,该文件偏移处保存着“hello world !”字符串,也就是说,将RVA转换为FileOffset是正确的。通过LordPE工具来验证一下,如图6-25所示。


图6-25 用LordPE计算RVA为0x00006030的文件偏移

再来回顾一下这个过程。

某数据的文件偏移 = 该数据所在节的起始文件偏移 + (某数据的RVA –该数据所在节的起始RVA)。

除了上面的计算方法以外,还有一种计算方法,即用节的起始RVA值减去节的起始文件偏移值,得到一个差值,再用RVA减去这个得到的差值,就可以得到其所对应的FileOffset。读者可以使用例子程序进行手工计算,然后通过LordPE进行验证。

知道如何通过RVA转换为文件偏移,那么通过文件偏移转换为RVA的方法也就不难了。这3种地址相互的转换方法就介绍完了。读者如果没有理解,就可以反复地按照公式进行学习和计算。只要在头脑中建立关于磁盘文件和内存映像的结构,那么理解起来就不会太吃力。在后面的例子中,将会写一个类似LordPE中转换3种地址的程序,以帮助读者加强理解。

时间: 2024-09-18 07:43:52

《C++ 黑客编程揭秘与防范(第2版)》——6.3 PE结构的3种地址的相关文章

《C++ 黑客编程揭秘与防范(第2版)》—第6章6.4节PE相关编程实例

6.4 PE相关编程实例 C++ 黑客编程揭秘与防范(第2版) 前面讲的都是概念性的知识,本节主要编写一些关于PE文件结构的程序代码,以帮助读者加强对PE结构的了解. 6.4.1 PE查看器 写PE查看器并不是件复杂的事情,只要按照PE结构一步一步地解析就可以了.下面简单地解析其中几个字段内容,显示一下节表的信息,其余的内容只要稍作修改即可.PE查看器的界面如图6-26所示. PE查看器的界面按照图6-26所示的设置,不过这个可以按照个人的偏好进行布局设置.编写该PE查看器的步骤为打开文件并创建

《C++ 黑客编程揭秘与防范》—第1章1.1节编程语言和开发环境的选择

第1章 黑客编程入门 C++ 黑客编程揭秘与防范 你是否曾经在用别人开发的工具尝试"入侵",你是否希望开发出自己的黑器--相信很多人有着这种近似相同的经历.本章将简单介绍黑客编程及工具开发.如果你是初学编程,如果你从来没有接触过黑客软件的开发,如果你急于想了解黑客编程方面的知识--那么就请继续往下阅读. 1.1 编程语言和开发环境的选择 C++ 黑客编程揭秘与防范 初学者刚开始学习编程语言最头疼的问题就是如何选择编程语言及合适的开发环境,下面就来具体介绍一下. 有人认为学编程就是学编程

《C++ 黑客编程揭秘与防范》——1.1 编程语言和开发环境的选择

1.1 编程语言和开发环境的选择 C++ 黑客编程揭秘与防范 初学者刚开始学习编程语言最头疼的问题就是如何选择编程语言及合适的开发环境,下面就来具体介绍一下. 有人认为学编程就是学编程语言,而VC.VB这样的开发环境只是工具,不需要学.这个想法是错误的,因为开发环境提供了很多开发工具,如VC这个集成开发环境就提供了与之对应的PSDK.MFC等.除了语言以外,要开发特定的软件是需要开发包和开发工具支持的.况且,编程语言也是一种工具,用于和计算机进行交流的工具.所以我们既要学习编程语言,也要学习开发

《C++ 黑客编程揭秘与防范》——1.3 简单API的介绍

1.3 简单API的介绍 C++ 黑客编程揭秘与防范 下面介绍一些在黑客编程中会用到的API函数,尽量排一点简单易用的函数,用简单的几行代码来完成一定的功能,希望大家能在这里体会到编程乐趣,不至于被大段的代码影响了自己前进的心情. 1.3.1 复制自身程序到Windows目录和系统目录下 一般的病毒木马都有这种类似的功能,完成这个功能其实并不复杂,我们来拆解思考一下实现这段代码的步骤. 复制是一个拷贝的过程.既然是拷贝,就要知道拷贝的原位置和目的位置.也就是整个过程其实分3步,首先要得到自身程序

《C++ 黑客编程揭秘与防范》——1.2 应用程序的调试

1.2 应用程序的调试 C++ 黑客编程揭秘与防范 在开发程序的过程中,除了编码以外还需要对程序进行调试,当编写的程序出现问题后,就要对程序进行调试.调试不是仅使用一个printf()或MessageBox()进行简单的输出来观察某个函数的返回值(虽然在调试的时候的确是对返回值观察较多),也不是对某个变量.某一时间的具体值的输出.调试是有专业的调试分析工具的,VC6不但提供代码编辑.代码编译.编译连接等功能,还提供了一个非常好用的调试工具.在编写完代码后,如果程序输出的结果是未知的,或者是没有预

《C++ 黑客编程揭秘与防范(第2版)》——6.1 PE文件结构

6.1 PE文件结构 C++ 黑客编程揭秘与防范(第2版) PE(Portable Executable),即可移植的执行体.在Windows平台(包括Win 9x.Win NT.Win CE--)下,所有的可执行文件(包括EXE文件.DLL文件.SYS文件.OCX文件.COM文件--)均使用PE文件结构.这些使用PE文件结构的可执行文件也称为PE文件. 普通的程序员也许没有必要掌握PE文件结构,因为其大多是开发服务性.决策性.辅助性的软件,比如MIS.HIS.CRM等软件.但是对于学习黑客编程

《C++ 黑客编程揭秘与防范(第2版)》—第6章6.3节PE结构的3种地址

6.3 PE结构的3种地址 C++ 黑客编程揭秘与防范(第2版) 在上一章中用OD调试器调试程序时看到的地址与本章使用C32Asm以十六进制形式查看程序时的地址形式有所差异.程序在内存中与在文件中有着不同的地址形式,而且PE相关的地址不只有这两种形式.与PE结构相关的地址形式有3种,且这3种地址形式可以进行转换. 6.3.1 与PE结构相关的3种地址 与PE结构相关的3种地址是VA(虚拟地址).RVA(相对虚拟地址)和FileOffset(文件偏移地址). VA(虚拟地址):PE文件映射到内存后

《C++ 黑客编程揭秘与防范(第2版)》——第6章 加密与解密

第6章 加密与解密 C++ 黑客编程揭秘与防范(第2版) 本章介绍的是关于加密与解密的知识,但是从整篇的内容上来看很难找到具体的加密与解密的知识.本章主要介绍PE结构.调试API函数等相关的内容.加密与解密,简单来说,主要就是逆向与调试.这些知识在前面的章节已经介绍过了,而掌握本章的知识以后会提高逆向与调试的能力. PE结构是Windows下可执行文件的标准结构,可执行文件的装载.内存分布.执行等都依赖于PE结构,而在逆向分析软件时,为了有目的.更高效地了解程序,必须掌握PE结构.要掌握反病毒.

《C++ 黑客编程揭秘与防范(第2版)》——6.2 详解PE文件结构

6.2 详解PE文件结构 C++ 黑客编程揭秘与防范(第2版) PSDK的头文件Winnt.h包含了PE文件结构的定义格式.PE头文件分为32位和64位版本.64位的PE结构是对32位的PE结构做了扩展,这里主要讨论32位的PE文件结构.对于64位的PE文件结构,读者可以自行查阅资料进行学习. 6.2.1 DOS头部详解IMAGE_DOS_HEADER 对于一个PE文件来说,最开始的位置就是一个DOS程序.DOS程序包含了一个DOS头部和一个DOS程序体.DOS头部是用来装载DOS程序的,DOS