PE文件基础

参考项目:https://code.google.com/p/portable-executable-library/

1. RVA与VA,File Offset

参考:http://www.pediy.com/kssd/pediy10/61737.html

VA是内存中的真实的虚拟地址值;

RVA是VA相对于ImageBase的相对偏移;

而File Offset就是在PE映像文件中相对于文件开始处的偏移。

PE文件的布局与最终加载到内存中的布局是不完全相同的,主要是因为有一些对齐的要求。

在加载到内存时,粒度是section,也就是说一个section里面的内容会被加载到内存中连续的一片区域。

因此,在一个section内部,所有内容的RVA/VA/File Offset是遵循相同的函数关系的。

这个函数关系的信息,是保存在PE文件的section header结构中的。

PE内部的地址都是以RVA形式保存的,从RVA转换到VA很简单,只需要获取到PE加载到内存中的ImageBase。

虽然PE头部也会指定其“钟意”的基地址,但是很有可能最终没有被加载到这里。

从RVA到File Offset的转换就是与具体的section相关的了。

PE Bliss对于RVA到File Offset的转换代码如下:

//RVA to RAW file offset convertion (4gb max)
uint32_t pe_base::rva_to_file_offset(uint32_t rva) const
{
    //Maybe, RVA is inside PE headers
    if(rva < get_size_of_headers())
        return rva;
 
    const section& s = section_from_rva(rva);
    return s.get_pointer_to_raw_data() + rva - s.get_virtual_address();
}

因为一个section里的内容的file offset与rva之间的偏移关系是相同的,即

A.rva - A.offset = B.rva - B.offset

A.offset = B.rva + A.rva - B.offset

将B选取为这个section的头部,而A是该section中的任意一点,那么A.offset就可以通过上面的公式求出。

2. 导入表

导入表揭示了PE文件对于其他PE文件的依赖关系。

当一个PE文件需要使用其他PE文件定义的函数或者变量时,需要知道这些内容在内存中的具体加载地址。

这个信息是在链接过程中写入到PE文件中,要保证这个信息的正确,必须对依赖的那些PE文件的布局有了解,这也是为什么一个PE文件在链接时,需要指明它所依赖的其他PE文件的lib文件,因为lib文件就提供了这些相对位置的信息。

这些相对位置,其实就是函数或者变量在其他PE文件中的RVA。

3. 为什么PE文件中使用的地址都用RVA形式表示

因为PE真正关心的是当它自己被拉起来运行时,它需要的内容要到内存中的哪个位置去找,而不是要到文件中的哪个位置去找。因此它关心其实是VA,而一旦相关映像被加载到内存中了,那么它们的ImageBase也就确定了,所以知道了RVA,就知道了VA。

原文地址:https://www.cnblogs.com/long123king/p/3645176.html