有关在Windows中链接和加载(PE)程序的详细信息。
我正在寻找答案或教程,阐明Windows程序在组装后如何链接并加载到内存中。
特别是,我不确定以下几点:
程序组装完成后,某些指令可能会引用.DATA部分中的内存。当程序从某个任意地址开始加载到内存中时,如何翻译这些引用? RVA和相对内存引用是否会解决这些问题( BaseOfCode 和 BaseOfData PE-header的RVA字段)?
程序是否始终加载到 ImageBase 标题字段中指定的地址?如果加载的(DLL)模块指定相同的基础怎么办?
答案 0 :(得分:1)
首先,我要回答你的第二个问题: 不,一个模块(作为exe或dll)不是总是在基地址加载。这可能由于两个原因发生,要么已经加载了一些其他模块,并且没有空间将其加载到标头中包含的基址,或者因为ASLR(地址空间布局随机化)意味着模块在随机插槽中加载用于缓解利用目的。
解决第一个问题(与第二个问题有关): 引用存储器位置的方式可以是相对的或绝对的。通常跳转和函数调用是相对的(尽管它们可以是绝对的),它们说:“从当前指令指针中获取这么多字节”。无论模块的加载位置如何,相对跳转和调用都会起作用。
在处理数据时,它们通常是绝对引用,即“在此地址访问这些4字节数据”。并指定了完整的虚拟地址,而不是RVA而是VA。
如果模块未在其基地址加载,则绝对引用将全部被破坏,它们不再指向链接器假定它们应指向的正确位置。假设ImageBase为0x04000000,并且您在RVA 0x000000F4处有一个变量,VA将为0x040000F4。现在假设模块不是加载到它的BaseAddress,而是在0x05000000,所有内容都向前移动0x1000字节,所以你的变量的VA实际上是0x050000F4,但是访问数据的机器码仍然有旧地址硬编码,所以程序已经腐败了。为了解决这个问题,链接器存储在这些绝对引用所在的可执行文件中,因此可以通过向它们添加可执行文件被移位的数量来修复它们:增量偏移量,图像加载位置和图像库之间的差异包含在可执行文件的标头中。在这种情况下,它是0x1000。此过程称为 Base Relocation ,由操作系统在加载时执行:代码开始执行之前。
有时模块没有重定位,因此不能在其他地方加载,而是在其基地址加载。见How do I determine if an EXE (or DLL) participate in ASLR, i.e. is relocatable?
有关ASLR的更多信息:https://insights.sei.cmu.edu/cert/2014/02/differences-between-aslr-on-windows-and-linux.html
还有另一种方法可以在内存中移动可执行文件并使其正常运行。存在称为位置独立代码的东西。以这样的方式制作的代码,它将在内存中的任何地方运行,而不需要加载器执行基本重定位。 这在Linux共享库中非常常见,它完成了相对地处理数据(在距指令指针这一距离处访问此数据项)。
为此,在x64架构中存在RIP相对寻址,在x86中使用技巧来模拟它:获取指令指针的内容,然后通过向其添加常量偏移量来计算变量的VA 。 这里有很好的解释: https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html
我认为PIC代码在Windows中并不常见,Windows模块通常包含基本重定位来修复绝对地址,当它被加载到除了其首选基址之外的其他地方时,尽管我并不完全确定最后一段,所以带上一粒盐。
更多信息:
http://opensecuritytraining.info/LifeOfBinaries.html
How are windows DLL actually shared?(有点令人困惑,因为在提问时我没有很好地解释自己)。
我希望我帮助过:)