“ Microsoft便携式可执行文件和公用对象文件格式规范”的摘要
即使阅读了这篇文章,我还是听不懂。我有很多问题。任何人都可以用一种实用的方式来解释它。请遵循所声明的Object File
和Image File
的术语。
我只知道地址是
.data
和.text
节的地址(用于函数名)。 如果我所知道的有什么问题,请纠正我。
编辑:
阅读了弗朗西斯给出的答案后,我很清楚物理地址,VA和RVA是什么以及它们之间的关系。
所有变量和方法的RVA必须在重定位期间由链接器计算。因此,(方法/变量的RVA值)==(其与文件开头的偏移量)?必须是真的。但令人惊讶的是,事实并非如此。为什么这样?
我通过在
c:\WINDOWS\system32\kernel32.dll
上使用PEView进行了检查,发现:直到各节开始为止,
.text
是此dll中的第一节)。 .text
的开始到.data
,.rsrc
到最后一节的最后一个字节(.reloc
)的RVA和FileOffset是不同的。并且第一部分的第一个字节的RVA也总是显示为0x1000
我猜:
在第一个之前(此处为
.text
)部分实际未加载
进入VA空间的过程中,这些
字节的数据只是用来
找到并描述这些部分。
他们可以称为“元部分
数据”。
由于它们未加载到VA中
过程空间。的用法
术语RVA也没有意义,这是
这些字节为何使用
RVA == FileOffset
的原因。 进入VA空间。
.text
,.data
,.rsrc
,.reloc
的字节就是这样的字节。 0x00000
PEView软件正在启动它来自
0x1000
。 最佳答案
大多数Windows进程(* .exe)都以(用户模式)内存地址0x00400000加载,这就是我们所说的“虚拟地址”(VA)-因为它们仅对每个进程可见,并且将通过以下方式转换为不同的物理地址:操作系统(由内核/驱动程序层可见)。
例如,可能的物理内存地址(CPU可以看到):
0x00300000 on physical memory has process A's main
0x00500000 on physical memory has process B's main
操作系统可能有一个映射表:
process A's 0x00400000 (VA) = physical address 0x00300000
process B's 0x00400000 (VA) = physical address 0x00500000
然后,当您尝试在进程A中读取0x004000000时,您将获得位于物理内存0x00300000上的内容。
关于RVA,它只是为了简化重定位而设计的。加载可重定位模块(例如DLL)时,系统将尝试在进程存储空间中滑动它。因此,在文件布局中,它会放置“相对”地址以帮助计算。
例如,DLL C可能具有以下地址:
RVA 0x00001000 DLL C's main entry
在基地址为0x10000000的进程A中加载时,C的主条目变为
VA = 0x10000000 + 0x00001000 = 0x10001000
(if process A's VA 0x10000000 mapped to physical address was 0x30000000, then
C's main entry will be 0x30001000 for physical address).
在基地址为0x32000000的进程B中加载时,C的主条目变为
VA = 0x32000000 + 0x00001000 = 0x32001000
(if process B's VA 0x32000000 mapped to physical address was 0x50000000, then
C's main entry will be 0x50001000 for physical address).
通常,镜像文件中的RVA在加载到内存时是相对于进程基址的,但是某些RVA可能相对于镜像或目标文件中的“节”的起始地址(您必须检查PE格式规范以获取详细信息)。不管哪种,RVA都是相对于“某些”基础VA的。
总而言之,
(编辑)关于爪子的新问题:
方法/变量的RVA值并不总是相对于文件开头的偏移量。它们通常是相对于某些VA的,可能是默认的加载基地址或部分基VA-这就是为什么我说您必须检查PE format spec以获得详细信息的原因。
您的工具PEView试图显示每个字节的RVA以加载基地址。由于部分从不同的起点开始,所以在交叉部分时RVA可能会有所不同。
关于您的猜测,它们非常接近正确答案:
# Name VirtSize RVA PhysSize Offset
1 .text 000C44C1 00001000 000C4600 00000800
2 .data 00000FEC 000C6000 00000E00 000C4E00
3 .rsrc 00000520 000C7000 00000600 000C5C00
4 .reloc 0000B098 000C8000 0000B200 000C6200
有一个不可见的“0 header RVA = 0000,SIZE = 1000”,它迫使.text从RVA 1000开始。这些段在加载到内存(即VA)时应该是连续的,因此它们的RVA是连续的。但是,由于内存是按页面分配的,因此它将是页面大小的倍数(4096 = 0x1000字节)。这就是为什么#2部分从1000 + C5000 = C6000(C5000来自C44C1)开始的原因。
为了提供内存映射,这些部分仍必须按一定的大小对齐(文件对齐大小-由链接器决定。在上面的示例中为0x200 = 512字节),该大小控制PhysSize字段。偏移表示“偏移到物理PE文件的开头”。
因此, header 占用文件的0x800字节(映射到内存时为0x1000字节),这是第1节的偏移量。然后通过对齐其数据(c44c1个字节),我们得到physsize C4600。 C4600 + 800 = C4E00,恰好是第二部分的偏移量。
好的,这与整个PE加载有关,因此可能有点难以理解...
(编辑)让我再次做一个新的简单总结。
关于assembly - VA(虚拟地址)和RVA(相对虚拟地址),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2170843/