Windows 机密已知的 DLL 平衡行为

Raymond Chen

Windows “已知的 DLL”(非正式名称)的功能指由内核的模块加载器进行特殊处理的 DLL(动态链接库)列表。当加载器发现具有指向已知的 DLL 的加载时动态链接的程序时,就会立即使用已知的副本,而忽略通常情况下应用于模块加载的搜索算法。有人可能会将其视为一种安全功能(尽管无可否认是相当弱的安全功能),但实际上,该功能的作用并非是保障安全。实际上,已知的 DLL 确实与性能密切相关。

已知的 DLL 功能结构已随时间的推移发生了改变。在某些版本的 Windows® 中,内核使用已知 DLL 列表作为起点,然后查看这些已知 DLL 链接到的所有 DLL,接着查看这些 DLL 链接到的所有 DLL,依此类推。此过程构建了所有依赖关系的传递闭包,并且将其全部视为已知。在其他版本的 Windows 中,并未构建此传递闭包;而是仅将明确列为已知的 DLL 视为已知。在某些版本的 Windows 中,已知 DLL 在系统开始时已通过内核预加载;在其他版本中,不会发生预加载,并且内核仅使用列表来避免搜索 DLL 路径。

不仅仅是已知的 DLL 列表的解释在更改版本,KnownDLLs 注册表项的内容也随之更改。Windows 性能团队不仅更改了已知 DLL 的列表,还基于他们对应用程序如何使用 Windows 的理解更改了列表转换为一组 DLL 所依据的规则。这在设计方面很典型,它是一场权衡游戏。在系统启动时预加载已知的 DLL,可允许使用这些 DLL 的应用程序更快地启动,但这是有代价的。启动系统所花费的时间会加长,而且内存使用量会增加,因为无论是否实际使用了这些 DLL,它们都会保留在内存中。这是一个以其他组件为代价来提高某些组件的性能的游戏。达成适当的平衡是一项艰难的任务,但是正如您所见,随着使用模式的发展,它正在不断优化。

使用已知 DLL 的一个结果(可能不太明显)是已知的 DLL 优先于本地重定向 DLL(在本专栏的 2007 年 1 月版本中,我们介绍了本地重定向的 DLL)。如果考虑一下,您可能会发现这实际上不是突发事件,而是意料之中的事。毕竟,已知 DLL 列表的目的是要绕过搜索路径从而加快 DLL 加载。如果内核必须检查本地重定向的 DLL,那只会减慢进程。您可能认为仅将一个目录添加到搜索路径不会是大问题,但是当该目录可能指向跨越全球一半的网络服务器时,即使添加一个目录也可能消耗巨大的性能。

不管您是否相信,我们已经发现了依赖特定已知 DLL 的程序。在应用程序目录中,有一个包含名为 Version.dll 的文件的程序。常规情况下,Version.dll 的专用副本将覆盖系统目录中的副本;但在 Windows XP 中,已将 Version.dll 列为已知的 DLL。这意味着将忽略应用程序目录中的副本,而使用系统目录中的版本。

在 Windows Vista® 中,Version.dll 不再被列为已知的 DLL,可能是由于性能专家确定应用程序使用它并不足以节省成本。因此,在 Windows Vista 中停止了使用此程序,因为该应用程序实际上依赖于忽略 DLL 的内核,而该程序又将 DLL 安装在自己的应用程序目录中。此程序故意将 DLL 安装到搜索路径上,然后依赖 Windows 忽略它!当然,如果希望 Windows 忽略某个文件,更有效的机制是事先不安装该文件。意外的是,编写该程序的公司提出了一个 bug,声称他们发现了安全漏洞。

Raymond Chen的网站“The Old New Thing”和同名书籍 (Addison-Wesley, 2007) 讨论了 Windows 的历史和 Win32 编程。他醉心于工作,对生活细节毫不在意。

© 2008 Microsoft Corporation 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.