动态链接库搜索顺序

同一动态链接库(DLL)的多个版本通常存在于作系统(OS)中的不同文件系统位置。 可以通过指定完整路径 来控制从中加载任何给定 DLL 的特定位置。 但是,如果不使用该方法,则系统将在加载时搜索 DLL,如本主题中所述。 DLL 加载程序 是加载 DLL 和/或解析对 DLL 的引用的作系统(OS)的一部分。

提示

有关 打包未打包 应用的定义,请参阅 打包应用的优缺点

影响搜索的因素

下面是本主题中讨论的一些特殊搜索因素,你可以将其视为 DLL 搜索顺序的一部分。 本主题后面的部分列出了某些应用类型的相应搜索顺序以及其他搜索位置的这些因素。 本部分只是为了介绍概念,并为其命名,我们将在本主题的后面部分参考它们。

  • DLL 重定向。 有关详细信息,请参阅 动态链接库重定向
  • API 集。 有关详细信息,请参阅 Windows API 集
  • 并行 (SxS) 清单重定向— 仅限桌面应用(而不是 UWP 应用)。 可以使用应用程序清单(也称为并行应用程序清单或融合清单)进行重定向。 有关详细信息,请参阅 清单
  • 加载模块列表。 系统可以检查是否已将具有相同模块名称的 DLL 加载到内存中(无论从哪个文件夹加载)。
  • 已知 DLL。 如果 DLL 位于运行应用程序的 Windows 版本的已知 DLL 列表中,则系统会使用已知 DLL 的副本(以及已知的 DLL 的依赖 DLL(如果有)。 有关当前系统上已知 DLL 的列表,请参阅注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

如果 DLL 具有依赖项,则系统会搜索依赖 DLL,就像仅使用其模块名称加载它们一样。 即使通过指定完整路径加载了第一个 DLL,也是如此。

打包应用的搜索顺序

当打包的应用通过调用 LoadPackagedLibrary 函数加载打包模块(特别是库模块(.dll 文件),DLL 必须位于进程的包依赖项图中。 有关详细信息,请参阅 LoadPackagedLibrary。 如果打包的应用以其他方式加载模块,并且未指定完整路径,则系统会在加载时搜索 DLL 及其依赖项,如本部分所述。

当系统搜索模块或其依赖项时,它始终使用打包应用的搜索顺序;即使依赖项未打包应用代码也是如此。

打包应用的标准搜索顺序

系统按以下顺序搜索:

  1. DLL 重定向。
  2. API 集。
  3. 桌面应用(而不是 UWP 应用)。 SxS 清单重定向。
  4. Loaded-module 列表。
  5. 已知 DLL。
  6. 进程的包依赖项关系图。 这是应用程序的包以及应用程序包清单 <Dependencies> 部分中指定为 <PackageDependency> 的任何依赖项。 依赖项按它们在清单中显示的顺序进行搜索。
  7. 调用进程从(可执行文件的文件夹)加载的文件夹。
  8. 系统文件夹(%SystemRoot%\system32)。

如果 DLL 具有依赖项,则系统会搜索依赖 DLL,就像只使用模块名称加载它们(即使第一个 DLL 是通过指定完整路径加载的)。

打包应用的备用搜索顺序

如果模块通过调用 LOAD_WITH_ALTERED_SEARCH_PATHLoadLibraryEx 函数来更改标准搜索顺序,则搜索顺序与标准搜索顺序相同,但在步骤 7 中,系统会搜索从(顶部加载模块的文件夹)而不是可执行文件的文件夹。

未打包应用的搜索顺序

当未打包的应用加载模块且未指定完整路径时,系统会在加载时搜索 DLL,如本部分所述。

重要

如果攻击者获得了搜索的其中一个目录的控制,则它可以在该文件夹中放置 DLL 的恶意副本。 有关帮助防止此类攻击的方法,请参阅 动态链接库安全

未打包应用的标准搜索顺序

系统使用的标准 DLL 搜索顺序取决于是否 启用安全 DLL 搜索模式

安全 DLL 搜索模式(默认启用)在搜索顺序中稍后移动用户的当前文件夹。 若要禁用安全的 DLL 搜索模式,请创建 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode 注册表值,并将其设置为 0。 调用 SetDllDirectory 函数可有效禁用安全的 DLL 搜索模式(指定文件夹位于搜索路径中),并更改本主题中所述的搜索顺序。

如果启用了安全 DLL 搜索模式,则搜索顺序如下所示:

  1. DLL 重定向。
  2. API 集。
  3. SxS 清单重定向。
  4. Loaded-module 列表。
  5. 已知 DLL。
  6. Windows 11 版本 21H2 (10.0;内部版本 22000)及更高版本。 进程的包依赖项关系图。 这是应用程序的包以及应用程序包清单 <Dependencies> 部分中指定为 <PackageDependency> 的任何依赖项。 依赖项按它们在清单中显示的顺序进行搜索。
  7. 从中加载应用程序的文件夹。
  8. 系统文件夹。 使用 GetSystemDirectory 函数检索此文件夹的路径。
  9. 16 位系统文件夹。 没有可获取此文件夹的路径的函数,但搜索该函数。
  10. Windows 文件夹。 使用 GetWindowsDirectory 函数获取此文件夹的路径。
  11. 当前文件夹。
  12. PATH 环境变量中列出的目录。 这不包括由 应用路径 注册表项指定的每个应用程序路径。 计算 DLL 搜索路径时,不使用 应用路径 密钥。

如果 禁用安全 DLL 搜索模式,则搜索顺序相同,不同之处在于 当前文件夹 从序列中的位置 11 移动到位置 8(步骤 7 之后)。应用程序从中加载的文件夹。

未打包应用的备用搜索顺序

若要更改系统使用的标准搜索顺序,可以使用 LOAD_WITH_ALTERED_SEARCH_PATH调用 LoadLibraryEx 函数。 还可以通过调用 SetDllDirectory 函数来更改标准搜索顺序。

注意

在当前进程开始之前,进程的标准搜索顺序也会受到调用父进程中 SetDllDirectory 函数的影响。

如果指定备用搜索策略,则其行为将继续,直到找到所有关联的可执行模块。 系统开始处理 DLL 初始化例程后,系统将还原为标准搜索策略。

如果调用指定 LOAD_WITH_ALTERED_SEARCH_PATHLoadLibraryEx 函数支持备用搜索顺序,lpFileName 参数指定绝对路径。

  • 标准搜索策略在调用应用程序的文件夹中(在初始步骤之后)开始。
  • LoadLibraryEx 指定的备用搜索策略,LOAD_WITH_ALTERED_SEARCH_PATH 开始(在加载 LoadLibraryEx 的可执行模块文件夹中的初始步骤后)。

这是他们唯一的区别。

如果启用了安全 DLL 搜索模式,则备用搜索顺序如下所示:

步骤 1-6 与标准搜索顺序相同。

  1. lpFileName指定的文件夹。
  2. 系统文件夹。 使用 GetSystemDirectory 函数检索此文件夹的路径。
  3. 16 位系统文件夹。 没有可获取此文件夹的路径的函数,但搜索该函数。
  4. Windows 文件夹。 使用 GetWindowsDirectory 函数获取此文件夹的路径。
  5. 当前文件夹。
  6. PATH 环境变量中列出的目录。 这不包括由 应用路径 注册表项指定的每个应用程序路径。 计算 DLL 搜索路径时,不使用 应用路径 密钥。

如果 禁用安全 DLL 搜索模式,则备用搜索顺序相同,但 当前文件夹 从序列中的位置 11 移动到位置 8(步骤 7 之后)。由 lpFileName指定的文件夹。

SetDllDirectory 函数支持备用搜索顺序(如果 lpPathName 参数指定路径)。 备用搜索顺序如下所示:

步骤 1-6 与标准搜索顺序相同。

  1. 从中加载应用程序的文件夹。
  2. SetDllDirectorylpPathName 参数指定的文件夹。
  3. 系统文件夹。
  4. 16 位系统文件夹。
  5. Windows 文件夹。
  6. PATH 环境变量中列出的目录。

如果 lpPathName 参数为空字符串,则调用将从搜索顺序中删除当前文件夹。

SetDllDirectory 有效禁用安全 DLL 搜索模式,而指定的文件夹位于搜索路径中。 若要根据 SafeDllSearchMode 注册表值还原安全 DLL 搜索模式,并将当前文件夹还原到搜索顺序,请调用 SetDllDirectorylpPathName 为 NULL。

使用LOAD_LIBRARY_SEARCH标志的搜索顺序

可以通过将一个或多个 LOAD_LIBRARY_SEARCH 标志与 LoadLibraryEx 函数一起使用来指定搜索顺序。 还可以将 LOAD_LIBRARY_SEARCH 标志与 SetDefaultDllDirectories 函数一起使用,为进程建立 DLL 搜索顺序。 可以使用 AddDllDirectorySetDllDirectory 函数为进程 DLL 搜索顺序指定其他目录。

所搜索的目录取决于 SetDefaultDllDirectoriesLoadLibraryEx指定的标志。 如果使用多个标志,则按以下顺序搜索相应的目录:

  1. LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR。 搜索包含 DLL 的文件夹。 此文件夹仅搜索要加载的 DLL 的依赖项。
  2. LOAD_LIBRARY_SEARCH_APPLICATION_DIR。 搜索应用程序文件夹。
  3. LOAD_LIBRARY_SEARCH_USER_DIRS。 搜索使用 AddDllDirectory 函数显式添加的路径或 SetDllDirectory 函数。 如果添加多个路径,则未指定搜索路径的顺序。
  4. LOAD_LIBRARY_SEARCH_SYSTEM32. 系统文件夹已搜索。

如果调用 LoadLibraryEx 且没有 LOAD_LIBRARY_SEARCH 标志,或者你为进程建立 DLL 搜索顺序,则系统将使用标准搜索顺序或备用搜索顺序搜索 DLL。