重新定义DLL(或提供适当的默认加载地址)是值得的吗?

Mar*_* Ba 27 c c++ windows dll

重新绑定DLL意味着修复DLL,这是首选的加载地址是Loader实际上能够加载DLL的加载地址.

这可以通过诸如Rebase.exe或通过为所有(自己的)dll指定默认加载地址等工具来实现,以便它们"适合"您的可执行进程.

以这种方式管理DLL基地址的重点是加速应用程序负载.(或者我理解.)

现在的问题是:值得麻烦吗?

我有Richter/Nazarre的Windows via C/C++一书,他们强烈建议[a]确保加载地址全部匹配,以便Loader不必重新加载加载的DLL.

但是,如果这样可以将应用程序加载时间加快到任何显着的数量,他们就无法争辩.

此外,对于ASLR,它似乎有任何价值,因为负载地址无论如何都是随机的.

关于这个的利弊是否有任何确凿的事实

[a]:在我的WvC++/5th ed中,它位于第568ff页的标题为Rebasing Modules and Binding Modules的部分.在第20章,DLL高级技巧.

Han*_*ant 12

修补可重定位的地址并不是什么大问题,它以内存速度(微秒)运行.更大的问题是包含此代码的页面现在需要由页面文件而不是DLL文件备份.换句话说,当包含代码的页面未映射时,需要将它们写入页面文件而不是仅丢弃.

这样做的成本并不容易,特别是在具有大量RAM的现代机器上.它只计算机器开始负载时有很多进程争夺内存.和分页文件的碎片.

但显然,变基是一种非常便宜的优化.在Debug + Windows + Modules窗口中很容易看到,在重新命名的DLL上有一个明亮的图标."地址"列为您提供了一个很好的提示,即基本地址是一个不错的选择.在它们之间留出足够的空间,这样您就不必在程序增长时不断调整它.

  • 这种情况有很大不同,就是在终端服务器环境中运行应用程序.如果应用了修正,则模块使用的内存无法跨会话有效共享.如果没有应用修正,则可以共享模块使用的内存.当许多会话运行相同的应用程序时(在终端服务器环境中很常见),不共享模块会对内存产生重大影响.老实说,我不知道ALSR在终端服务器环境中做了什么. (7认同)

Mar*_* Ba 7

我想自己提供一个答案,尽管Hans Passant和其他人的答案已经很好地描述了这些权衡.

在我们的应用程序中最近摆弄DLL基地址后,我将在此给出我的结论:

我认为,除非你能证明不是这样,否则为DLL提供非默认的基地址是徒劳的.这包括重新定义我的DLL.

  • 对于我控制的DLL,给定平均应用程序,每个DLL只会被加载到内存中一次,因此页面文件上的负载应该是最小的.(但请参阅Michal Burr在关于终端服务器环境的另一个答案中的评论.)

  • 如果为DLL提供了固定的基址(没有重新定位),它实际上会增加地址空间碎片,因为这些地址迟早不再匹配.在我们的应用程序中,我们给了所有DLL一个固定的基地址(出于其他遗留原因,而不是因为地址空间碎片)而没有使用rebase.exe,这显着增加了我们的地址空间碎片,因为你真的无法手动获得这个权限.

  • 重新定位(通过rebase.exe)并不便宜.这是构建过程中的另一个步骤,必须进行维护和检查,因此必须有一些好处.

  • 一个大型应用程序将始终加载一些DLL,其中基地址不匹配,因为一些钩子DLL(AV)和因为你没有rebase第三方DLL(或至少我不会).

  • 如果您使用RAM磁盘作为分页文件,如果加载的DLL被分页,您实际上可能会更好:-)

总而言之,我认为除了像系统DLL这样的特殊情况之外,重新定位不值得.


我想添加一个我在Old New Thing上发现的历史片段:Windows 95如何修改DLL?-

当需要重新定义DLL时,Windows 95只会记录DLL的新基址,但不会做太多其他事情.真正的工作发生在DLL的页面最终被交换时.原始页面被交换掉磁盘,然后修复程序被动态应用到原始页面,从而重新定位它.然后将修复后的页面映射到进程的地址空间,并允许程序继续.

看看这个过程是如何完成的(阅读整篇文章),我个人怀疑"变形是邪恶的"部分可以追溯到Win9x和低内存条件的旧时代.


看,现在Old Old Thing上有一件非历史作品:

现在确保我的所有DLL都具有非冲突的基址是多么重要?

回到白天,你被告知要做的事情之一是重新绑定你的DLL,以便它们都具有非重叠的地址范围,从而避免了运行时重定位的成本.这现在仍然很重要吗?

...

在存在ASLR的情况下,重新定义DLL无效,因为ASLR 无论如何都会忽略您的基址,并将DLL重定位到其伪随机选择的位置.

...

结论:为了以防万一,它不会因为退货而受到损害,但要明白,这种回报将极为罕见.使用已/DYNAMICBASE启用(并且有/HIGHENTROPYVA良好的措施)构建您的DLL, 并让ASLR完成确保不发生基址冲突的工作.这将涵盖几乎所有的现实场景.如果您碰巧遇到ASLR不可用的极少数情况之一,那么您的程序仍然有效.由于搬迁罚款,它可能会运行一点慢.

... ASLR实际上比避免手动变基更能避免冲突,因为ASLR可以将系统视为一个整体,而手动变基需要您知道加载到流程中的所有DLL,并协调多个供应商的基地址通常是不可能的.

  • 关于Old New Thing的一篇不太具历史意义的文章:[现在确保我所有的DLL都有非冲突的基地址有多重要?](https://blogs.msdn.microsoft.com/oldnewthing/20170120-00/?p = 95225). (2认同)