LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)的缺陷

LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)的缺陷

作者:BlogUpdater |  时间:2022-05-05 |  浏览:94 |  评论已关闭 条评论

如果你认真瞧过LoadLibraryEx函数的文档,就会发现它有这样一个标志DONT_RESOLVE_DLL_REFERENCES。关于此标志,文档是这样描述的:

如果使用了此标志,并且加载的是一个DLL模块,系统在进程和线程初始化/退出的时候,不会调用DllMain,同时,系统也不会加载该模块引用的其他模块。
如果你系统仅仅是访问DLL模块中的数据或者是资源的话,则使用LOAD_LIBRARY_AS_DATAFILE会是一个更佳的选择。

在我看来,上面所建议的LOAD_LIBRARY_AS_DATAFILE还远远不够。
因为,DONT_RESOLVE_DLL_REFERENCES这个标志实际上有点类似于一个”定时炸弹”。

请再次认真阅读下关于这个标志的文档,试着深入地理解它会做什么以及它不会做什么。
模块被加载到内存的时候,系统不会调用它的初始化函数,并且其所有依赖的模块也不会被加载。结果就是,你无法执行此模块中的任何代码。(说得更准确地的话,就是如果你试图执行该模块中的代码,会导致程序崩溃,一万DLL模块还未初始化其自身,并且它的DLL导入表都没有得到解析)
但是,和LOAD_LIBRARY_AS_DATAFILE标志不同的是,被加载的DLL可以被GetModuleHandle所发现,并且可以使用GetProcAddress。

很明显,对于一个使用了标志进行加载的模块使用GetProcAddress是一个坏主意。因为上面我们提到过,你根本无法执行这个DLL中的任何代码。那从DLL中获取一个函数入口点又有什么意义呢?

而GetModuleHandle就会触发这个定时炸弹。

使用GetModuleHandle来查看一个DLL是否已经加载了是一个很常见的使用场景,如果加载了,则继续使用GetProcAddress来获取一个函数的地址并调用它。如果DLL使用的是DONT_RESOLVE_DLL_REFERENCES标志来加载的话,GetModuleHandle和GetProcAddress将会调用成功,但是执行函数的时候会导致程序崩溃。执行这种操作的代码不知道DLL是使用 DONT_RESOLVE_DLL_REFERENCES 加载的,它没有办法保护自己。

(请注意,这样做的代码无论如何都是不安全的,因为最初加载 DLL 的代码可能决定在另一个线程上执行 FreeLibrary,从而导致代码从第一个线程下面被撕掉。第二个问题可以通过使用 GetModuleHandleEx”修复”这个问题,可以通过指定它增加 DLL 引用计数,但这并不能解决第一个问题。)

即使你使用LoadLibrary加载DLL并将该句柄传递给GetProcAddress,程序仍然会崩溃,因为LoadLibrary注意到DLL已加载并且只是增加引用计数。

我们看看下面的例子:

如果你在没有命令行参数的情况下运行这个程序,那么一切都会正常运行:记事本会顺利启动。 但是,如果你传递一个命令行参数,这会启动”定时炸弹”,并且对 ShellExecuteA 的调用会导致程序崩溃,因为 shell32.dll 是在没有解析其 DLL 引用的情况下加载的。

换句话说,DONT_RESOLVE_DLL_REFERENCES 从根本上是有缺陷的,应该避免。 它继续存在只是为了向后兼容。

总结
太长不看版:任何情况下,请勿使用DONT_RESOLVE_DLL_REFERENCES,保护他人,也保护自己。

最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES) is fundamentally flawed》

最近我写了个东西
正如你们所知道的,拓扑梅尔智慧办公平台(Topomel Box)是一款绿色软件,主要面向经常使用电脑的朋友。它提供了各种提升办公效率的小功能,同时操作上尽可能地简单方便。
我想:你值得拥有。

评论已关闭。