SetDllDirectory 不级联,所以无法加载依赖DLL

SetDllDirectory does not cascade, so dependency DLLs cannot be loaded

我从一个目录执行一个 exe,比如 "C:/test"
DLL 位于目录 "C:/test/dlls" 中,因此,在此 exe 中,我调用:

SetDllDirectory("C:/test/dlls");

然后我打电话给

lib1 = LoadLibrary("lib1.dll)

ptrType pr = (ptrType) ::GetProcAddress(lib1, "test")

lib1.dll 需要目录 "C:/test/dlls" 中的其他 DLL,但是当我执行 pr(...) got from GetProcAddress 时,出现错误:

"The program can't start because lib2.dll is missing from your computer. Try reinstalling the program to fix this problem."

如果我将 lib2.dll 移动到 "C:/test",它就会被发现。 这意味着 SetDllDirectory() 仅对第一个 DLL 的加载有效。

有谁知道为什么以及如何解决它?

这个应该有效。 SetDllDirectory 执行“级联”,因此会影响相关 DLL 的加载方式。基本上,SetDllDirectory 允许您修改进程的 default search order for DLLs,对此所做的任何更改都会影响整个进程,包括可能加载到该进程中的任何 DLL。 Windows 在隐式加载子进程的依赖项时也使用此搜索顺序。

然而,SetDllDirectory 中存在致命缺陷,如 the documentation 中所述:

Each time the SetDllDirectory function is called, it replaces the directory specified in the previous SetDllDirectory call.

如果您进程中的某些其他代码正在调用 SetDllDirectory,那么它正在撤消您的第一个调用。如果 lib1.dll 中的代码在尝试加载 lib2.dll 之前执行此操作,则加载 lib2.dll 的尝试将失败。

当然,这是 lib1.dll 的不良行为。 DLL 为 guests in the application process and therefore should not go changing the carpet。但并不是所有的代码都表现良好,这使得这是一个脆弱的策略。

另一种可能出错的方式是,如果 lib1.dll 通过调用 LoadLibraryEx 加载 lib2.dll 并传递覆盖默认搜索顺序的许多标志之一,例如 LOAD_LIBRARY_SEARCH_APPLICATION_DIR.

最佳解决方案是将您依赖的所有 DLL 放在应用程序目录中。在Windows、the application's directory is the application bundle上,所以这是放置它们的最佳位置。用户永远不应该翻阅这个目录,所以“弄乱它”不是问题。它解决了各种问题,包括有人在当前目录中放置同名 DLL 的依赖注入攻击。 (您可以通过调用 SetDllDirectory 并传递一个空字符串以从搜索顺序中删除当前目录来防止此类攻击,但现在我们回到问题 #1。如果您家里有客人修改 DLL 搜索顺序,这不是解决安全问题的可靠方法。如果将 DLL 放在应用程序目录中,将在搜索当前目录之前 first 找到它们,在攻击向量被利用之前关闭它。)

如果您有非常好的理由将 DLL 放在不同的目录中,那么您的选择就有限了。

您不能使用 AddDllDirectory,因为上面引用的文档继续建议,因为您不控制 lib1.dll 中的代码,因此无法修改它以调用 LoadLibraryEx 带有 LOAD_LIBRARY_SEARCH_USER_DIRS 标志。

通过指定完整路径加载 lib1.dll 是行不通的,因为“如果 DLL 具有依赖项,系统会搜索依赖的 DLL,就好像它们仅使用模块名称加载一样。这是真的即使第一个 DLL 是通过指定完整路径加载的。"

这使您可以选择 using DLL redirection, or adding information about your dependencies in your application's manifest