延迟加载的DLL在内存中相互加载距离太远

时间:2018-01-24 20:00:04

标签: c++ windows visual-studio dll loadlibrary

我有一个x64 Windows应用程序,有时会在DLL库中崩溃,我很遗憾无法控制它。这个库实际上是一大组相互依赖的DLL(大约40个)。崩溃并不总是在同一个地方,但这里是一个正在发生的事情的例子:

Exception thrown at 0x0000018393916B72 (foo.dll) in bar.exe: 0xC0000005: Access violation reading location 0x000001831C1F95A0.

在此位置,Visual Studio中的反汇编显示以下内容:

0000018393916B72  cmp         dword ptr [1831C1F95A0h],3  

这对我来说起初很奇怪,因为这意味着DLL将被硬编码以在错误的位置加载值。所以,我看了一下实际的指令内存,发现了以下内容:

83 3d 27 2a 8e 88 03

完全反汇编:

cmp    DWORD PTR [rip+0xffffffff888e2a27],0x3

这里发生的是在DLL中生成一条指令“将该指令后面的X字节的值与3进行比较”。此内存驻留在与此不同的DLL中,X是32位值。我假设在加载其他DLL时,X的值会被修补。

查看指令的实际字节,很明显它需要rip + 0x888e2a27,它确实指向此库中的一个DLL所在的位置,但遗憾的是这个32位值得到符号扩展并最终看起来在记忆中向后而不是向前。

最终,根本原因是这些DLL被加载到内存中的字节数超过0x7FFFFFFF,因此当它们尝试以32位偏移量相互引用时,它们无法这样做。我不确定为什么生成这些DLL的编译器选择使用RIP + 32位指令而不是64位绝对地址,但它似乎经常这样做。可能是节省尺寸的措施吗?

无论哪种方式,我认为解决此问题的最简单方法是强制LoadLibrary将这些DLL加载到彼此旁边,或者至少相对靠近彼此。我已经尝试了各种方法来做到这一点,但似乎Windows有目的地随机和不透明它决定加载DLL的位置。

有没有解决这个问题的方法?这是我的潜在解决方案,但我不知道如何做任何一个:

  1. 强制LoadLibrary在特定地址加载DLL,或使用某种选项强制这些DLL在内存中彼此靠近。

  2. 修改DLL本身以删除任何RIP +(32位值)指令,并将其替换为支持64位地址值的指令。

  3. 如果我可能让供应商重新编译DLL,请让他们使用特定的编译器标志来防止使用这些RIP +(32位值)指令。但是,我不知道这个编译器标志是什么。我假设他们使用了MSVC但我确实不确定。

1 个答案:

答案 0 :(得分:0)

无法保证这会起作用,但您可以尝试使用EDITBIN命令编辑有问题的DLL,以便将它们标记为不支持ASLR。

执行此操作的命令将是:

editbin /DYNAMICBASE:NO /HIGHENTROPYVA:NO foo.dll

Editbin随附msvc / windows sdk安装。

有关详细信息,请参阅: https://msdn.microsoft.com/en-us/library/d25ddyfc.aspx

如果启用了强制ASLR,则加载程序将从地址空间的底部开始搜索此类DLL的基址。所以也许这意味着他们最终彼此足够接近。

此行为在以下内容中解释:

https://blogs.technet.microsoft.com/srd/2017/11/21/clarifying-the-behavior-of-mandatory-aslr/

尝试的最后一点是尽可能早地加载DLL来执行应用程序,以便尽可能地释放地址空间。

祝你好运: - )。