您的位置:首页 > 其它

理解PE文件相对虚拟地址(RVA)到文件偏移的转换

2007-03-12 15:47 495 查看
关于PE文件格式的详细描述在网络上可以找到一大堆,最近有空,我也来研究一把。读了很多参考资料,应该说都讲得非常清晰,尤其是看雪学院的iamgufeng翻译的那篇文章,读来受益非浅。
根据我这个菜鸟的阅读感受与实践来说来看,我觉得根据RVA正确换算出到数据相对文件的偏移这个细节这些文章都没有做过多的说明,而这是我唯一化比较多时间来思考的地方。下面我就说说我对此的理解,以和大家交流。
所谓PE文件的相对虚拟地址(Relative Virual Address,RVA):是PE文件中的数据、模块等运行在内存中的实际地址相对PE文件装载到内存的基地址之间的距离。举例说明,如果PE文件装入虚拟地址(VA)空间的400000h处,且进程从虚址401000h开始执行,我们可以说进程执行起始地址在RVA 1000h。
引用看雪学院《加密与解密II》PE文件格式小节中的一段话:“…对于PE文件,首先应该知道,磁盘中的执行文件与其被Windows装载程序装入后的Module看起来非常相似。Windows装载程序把磁盘文件编程实际执行代码的中作相当简单,装载程序使用文件内存映象机制将磁盘文件映射到虚拟地址空间。打个比方,PE文件象一个活动地房子,装载时只需将某个块放在某个地方,在把它和其它部分拧好即可(比如说,把它和DLL连结在一起)。加载PE文件格式的DLL同样的简单。一旦Module被装入,在Windows中就同其它已经装入的文件一样了。”我是这样理解这段话的:它说明PE文件中的节等模块加载到内存时,节的数据布局和文件中的内存布局基本保持不变。所以可以根据这个数据尺寸相对不变的特点来由RVA正确换算出到数据相对文件的偏移。即,每个节(section)中的数据的起始位置相对节的起始位置是不变的,不管节是在文件中还是被加载到内存中
我们知道,PE文件中的节是存放数据的主要地方,每个节中的数据的RVA相对于节的起始RVA是相对,比如在定位导入/导出表相对文件的偏址时,我们可以获得导入/导出表所在的节(模块),以及节的文件偏移和导入/导出表的RVA,则可以据此推算出导出表的文件偏移。原理如下图:




由图示可知:
数据相对偏移h=数据RVA - 节RVA
数据相对偏移h=数据的文件偏移 – 节的文件偏移
根据上面两个等式可以得到数据在文件中的偏移量的算法为:
数据的文件偏移=(数据RVA - 节RVA) + 节的文件偏移
则要获取数据,将缓冲区的起始地址直接加上数据在文件中的偏移即可获得数据的存放地址以供相关函数读取。
由于这个操作使用非常频繁,可以将它写为函数以供调用。我这里使用一个宏操作来实现,如下:
#define addr_in_file(virtualaddr) ((char*)pFileBuffer + /
(virtualaddr-pSectHead[i].VirtualAddress+pSectHead[i].PointerToRawData))
其中,virtualaddr是要转化的RVA
pFileBuffer是文件缓冲区的起始地址,打开文件后将整个内容都读到缓冲区。如下:
fp=fopen(argv[1],"rb");

fseek(fp,0,SEEK_SET);
fread(pFileBuffer,nFileLen,1,fp);

pSectHead节头的数组地址,pSectHead[i]表示virtualaddr所在的节的地址
宏的结果就是virtualadd对应的数据在文件缓冲区中的存放地址。
注意这里的宏中有参数i,它的取值是变化的,如:获取导入表在那个节中的代码如下:
if (pDataDir[1].VirtualAddress) //存在导入表
{
for(i=0;i<pPeHead->FileHeader.NumberOfSections;i++)
{
if((pDataDir[1].VirtualAddress>=pSectHead[i].VirtualAddress)
&&(pDataDir[1].VirtualAddress<(pSectHead[i].VirtualAddress+pSectHead[i].SizeOfRawData)))
break;
}
//

然后再使用宏获取相关信息:

pImpDir=(IMAGE_IMPORT_DESCRIPTOR*)addr_in_file(pDataDir[1].VirtualAddress);

printf(" import: dll name=%s/n",(char*)addr_in_file(pImpDir->Name));

其实对于初学者来说,把相对虚拟地址到文件偏移的转换关系理解了,再参考MSDN或其它相关文档,就可以很容易的写出自己的PE文件解析器了。
限于格式的原因,我就不列出完整地源代码了,需要交流的可以找我索取源代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: