用OD调试器调试程序时看到的地址和用C32Asm看十六进制形式的程序时看到的地址形式是不一样的。程序在内存中和在文件中有不同的地址形式,PE相关的地址不限于这两种形式。PE结构相关的地址形式有3种,这3种地址形式可以相互转换。1、PE结构相关的三类地址PE结构相关的三类地址分别是VA(虚拟地址)、RVA(相对虚拟地址)和FileOffset(文件偏移地址)。VA(VirtualAddress):PE文件映射到内存后的地址。RVA(RelativeVirtualAddress):内存地址相对于映射基地址的偏移地址。FileOffset(文件偏移地址):磁盘上相对于PE文件开头的偏移地址。PE文件在磁??盘上的结构与在内存中的结构相同。不同之处在于,在磁盘上,文件根据IMAGE_OPTIONAL_HEADER的FileAlignment值对齐。在内存中,图像文件根据IMAGE_OPTIONAL_HEADER的SectionAlignment进行对齐。FileAlignment是以磁盘上的扇区为准,也就是说FileAlignment最小为512字节,十六进制为0x200字节。SectionAlignment以内存页为单位进行对齐。通常,Win32平台上一个内存页为4KB,十六进制为0x1000字节。一般来说,FileAlignment的值会和SectionAlignment的值一样,这样磁盘文件的结构和内存映像就完全一样了。当FileAlignment的值与SectionAlignment的值不同时,会有一些细微的差别。主要区别是根据对齐的实际情况填充了很多0值。PE文件映射如图1所示。图1PE文件映射除了文件对齐和内存对齐的区别外,文件的起始地址是从地址0开始的。在C32Asm十六进制模式下查看PE文件时,起始地址位置是0x00000000。在内存中,它的起始地址是IMAGE_OPTIONAL_HEADER结构的ImageBase字段(这个语句只针对EXE文件,DLL文件的映射地址不一定是固定的,但肯定不会是0x00000000地址)。2.三种地址转换当FileAlignment和SectionAlignment的取值不同时,同一个节表数据在磁盘文件和内存映像中的偏移量在磁盘和内存中也不同,所以两者之一offsets需要转换的问题。当知道某个数据的RVA,想读取文件中的相同数据时,必须将RVA转换为FileOffset。反之亦然。下面举例介绍如何转换。一个使用MessageBox()输出“HelloWorld”的示例程序,用PEID打开,查看其节表,如图2所示。从标题栏可以看到图2PEID所示节表的内容图2的,不叫“节表”,叫“节”。还有其他的材料叫做“块”或“节”,只是名称不同,内容是一样的。从图2可以看出,节表第一个节区的节名是“.text”。通常,第一个节表项是代码区,入口点通常落在这个节表项中。在早期的shell还没有普及的时候,可以通过判断入口点是否在第一段来判断程序是否感染了病毒。在贝壳普及的今天,这种判断方法已经不靠谱了。关键要看的是“R.Offset”,它表示该节在文件中的起始位置。PE头包括DOS头、PE头和节表,通常不超过512字节,也就是说不超过0x200的大小。如果这个“R.offset”为0x00001000,通常可以确定文件的磁盘对齐大小为0x1000。测试验证这个程序,看到“V.offset”和“R.offset”是一样的,也就是说磁盘对齐和内存对齐是一样的,所以没办法完成demo转换。但是,文件对齐大小是可以人为修改的。也可以通过工具修改文件对齐的大小。在这里,LordPE用于修改其文件对齐大小。修改方法很简单,先复制要修改的测试文件,与修改后的文件进行比较。打开LordPE,点击“RebuildPE”按钮,然后选择刚刚复制的测试文件,如图3、图4所示。图3LordPE界面图4重建PE功能结果PE重建功能具有压缩文件大小的功能。这里的压缩是修改磁盘文件的对齐值,避免因为对齐导致过多的补零,从而占用更少的磁盘空间。.使用PEID查看重构后的PE文件的sectiontable,如图5所示。图5PE文件重构后的sectiontable现在可以看到“V.Offset”和“R.Offset”的值"不同,它们的对齐值也不同。您可以自行验证FileAlignment和SectionAlignment。值是否相同。现在有两个功能完全一样,PE结构完全一样的文件。唯一的区别是它们的磁盘对齐大小不同。现在分别在这两个程序中查找一个节表中的数据,学习不同地址之间的转换。先用OD打开测试程序,不重建PE结构,在反汇编中找到调用messagebox()的两个字符串参数的地址,弹出对话框,如图6、图7所示。图6MessageBox()函数中使用的字符串的地址图7数据窗口显示的两个字符串的地址从图6和图7可以看出,字符串“helloworld!”的地址是0x00406030,字符串“hello”的地址是0x00406040。这两个地址都是虚拟地址或VA。VA(虚拟地址)转换为RVA(相对虚拟地址)很容易,RVA(相对虚拟地址)是VA(虚拟地址)减去IMAGE_OPTIONAL_HEADER结构中ImageBase(加载图像文件的虚拟地址)字段的值,即RVA=VA–ImageBase=0x00406030–0x00400000=0x00006030。由于IMAGE_OPTIONAL_HEADER中的SectionAlignment和FileAlignment值相同,所以其FileOffset的值也为0x00006030。用C32Asm打开文件,查看文件偏移地址0x00006030处的内容,如图8所示。图8文件偏移量0x00006030处的内容是字符串“helloworld!”从这个例子可以看出,当SectionAlignment和FileAlignment相同时,同一sectionentryshiftaddress中数据的RVA(相对虚拟地址)和FileOffset(文件偏移量)相同。RVA的值是使用VA-ImageBase计算的。然后用OD打开“rebuildingPE”后的测试程序,同样找到字符串“helloworld!”用于在反汇编中调用MessageBox()函数,查看其虚拟地址是什么。它的虚拟地址仍然是0x00406030。同理,虚拟地址减去加载地址,相对虚拟地址的值还是0x00006030。但是如果用C32Asm打开文件查看就不一样了。使用C32Asm查看地址0x00006030处的内容,如图9所示。图9没有“helloworld!”stringatfileoffset0x00006030从图9可以看出,用C32Asm打开文件后,没有“helloworld!”和文件偏移量0x00006030处的“hello”字符串。这是由于文件对齐和内存对齐的不同造成的。这时候就需要通过一些简单的计算,将RVA转化为FileOffset。RVA转FileOffset的方法很简单。首先看当前的RVA或者FileOffset属于哪个section。0x00006030此RVA属于.data部分。0x00006030的RVA相对于该部分的起始RVA地址0x00006000偏移0x30字节。再看看。.data段在文件中的起始位置为0x00004000,.data段中文件起始偏移量0x00004000加上0x30字节的值为0x00004030。使用C32Asm查看地址0x00004030处的内容,如图10所示。图10Contentsatfileoffset0x00004030从图10可以看出,字符串“helloworld!”存储在这个文件偏移量处,也就是说,将RVA转换为FileOffset是正确的。使用LordPE工具进行验证,如图11所示。图11使用LordPE计算RVA为0x00006030的文件的偏移量我们再回顾一下这个过程。某条数据的文件偏移量=该数据所在节的起始文件偏移量+(某条数据的RVA-该数据所在节的起始RVA)。除了上面的计算方法,还有一种计算方法,就是用section的起始RVA值减去section的起始文件偏移值得到差值,再减去RVA得到的差值,就可以了获取其对应的FileOffset。可以使用示例程序执行手动计算,然后由LordPE进行验证。如果你知道如何将RVA转换为文件偏移量,那么将文件偏移量转换为RVA的方法就不难了。介绍了这三个地址之间的转换方法。不懂的可以根据公式反复学习、计算。只要在脑海中建立起磁盘文件和内存映像的结构,就不会太难理解。
