不使用 LoadLibrary() 函数来加载 Win32/64 DLL 动态链接库 已翻译 100%

oschina 投递于 2014/03/10 11:27 (共 5 段, 翻译完成于 04-02)
阅读 5536
收藏 2
3
加载中

前言

迟早会有不少人开始思考如何不使用 LoadLibrary() 来加载动态链接库。好吧,也许不是那么多人。。。与使用常规的 LoadLibrary() 调用来加载动态链接库的场景相比,它有几个优点,但在编写动态链接库(取决于它实现了什么)时会引入一些不便。尽管如此,如果你想了解幕后发生了什么,这个技巧能够作为指南为你提供很好的建议。我自己就用它来写 C/C++ 的动态链接库,而不是偏移量无关的汇编(一个游戏外挂中所用),那是后话了。

实现

加载动态链接库最重要的步骤是:

  1. 映射或者加载动态链接库到内存中。

  2. 通过动态链接库中的重定位表(如果存在的话)在动态链接库中重定位偏移量。

  3. 解析动态链接库的依赖,加载这个链接库所需的其它链接库,解析所需函数的偏移量。

  4. 使用参数 DLL_PROCESS_ATTACH 调用入口点(如果存在的话)。

yxrykds
yxrykds
翻译于 2014/03/10 14:03
2

我动手写代码实现上面的步骤,但很快发现有些地方不对:该加载的DLL并没有有效的HMODULE/HINSTANCE句柄,而很多windows的函数却需要一个句柄(例如,GetProcAddress(),CreateDialog(),等等)。实际上,一个模块的HINSTANCE 句柄就是加载的DOS/PE头在内存的地址。我尝试将这个地址传给函数但它不起作用,因为windows窗口会检查这个句柄是否是一个真的句柄而不只是内存的内容。这使得手动加载DLLs有一点难度!我不得不自己写一个GetProcAddress()函数,因为那些窗口不能与我的DLLs一起工作。后来,我发现我需要在DLL中使用对话框资源,和CreateDialog()函数也需要一个模块句柄来从DLL中获得对话框资源。因此我发明了我常用的FindResource()函数来手动加载DLLs和它可以被传递给CreateDialogIndirect()函数用来查找对话框资源。 你也可以使用它来加载其他类型的资源,如果你可以找到一个函数使该资源与FindResource()函数能配合使用,这就如同手动加载DLLs那样。据此提升你获得了DLL 手动加载器的代码和GetProceAddress()函数,但我在另一个提示里给出了资源的相关函数。

心如渊谷
心如渊谷
翻译于 2014/03/12 00:01
2

使用限制

  1. 被加载的 DLL 没有 HMODULE,所以使用起来很难,特别是与资源有关时。

  2. DllMain() 不接收 DLL_THREAD_ATTACH 与 DLL_THREAD_DETACH 通知。不过就 DLL_THREAD_ATTACH/DLL_THREAD_DETACH 而言,你可以这样来模拟它:创建一个小的 DLL,并使用常规的 LoadLibrary() 加载它,并且从这个被正常加载的 DLL 中的 DllMain() 处,调用已经被手动加载的那个 DLL 的入口点。

  3. 如果你的 DLL 引用了其它的 DLL,那么这些其它的 DLL 可以通过 WinAPI 的 LoadLibrary() 来加载。这实际上并非是缺陷,提及它,仅仅供你参考。实际上,手工加载如 kernel32.dll 等是没有必要的,大部分系统的 DLL 都可能工作失常或崩溃!

  4. 使用 SEH 的 DLL *也许会* 出错。DLL 包含 SEH 相关代码这件事本身不是问题,但在被加载的 DLL 中, __try 代码块无法捕捉到异常,这是因为 nt.dll!RtlIsValidHandler() 不接收我们手动加载的 DLL 中的内存区里的异常处理历程(因为这个内存区并非从 PE 文件映射而来)。只有在 DLL 的 __try 代码块触发异常的时候,这才成为一个问题(因为 Windows 无法执行这个 DLL 的异常处理句柄,那么就会触发另外一个异常,它跳出了这个 DLL 的异常处理句柄 - 结果通常是崩溃)。

  5. CRT 是否可以与手动加载的 DLL 一起工作,取决于几件事。它依赖于你实际使用的 CRT 版本号,及你从 CRT 所调用的函数。如果你仅仅使用几个简单的函数(如 printf),那么 CRT 也许会工作。我编写这个 DLL,使用了 /NODEFAULTLIB 链接选项,它表示你不能访问 CRT 函数,并且很可观的减少了 DLL 的文件大小(可能是 4K 的引用负荷)。另一方面,你必须使用纯  WinAPI 函数!这可能相当不方便,但你可以通过编写自己的迷你 CRT 来避免这个问题。在我的 C ++ 例子中,提供了一个这样的迷你 CRT ,不是为了无所不包,而是为了至少允许你使用最基本的 C++ 特性:自动初始化的静态变量,new / delete 操作符。顺便说一句,如果你准备使用这个代码,那么你应该理解这些问题中的绝大多数,并且你应该感激:不使用 CRT 来编写 C/C++ 的 DLL,比编写偏移无关的或可重定位的汇编补丁之类的那些东西,还是要方便太多了。

yxrykds
yxrykds
翻译于 2014/04/02 02:06
2

使用代码

用C/C++编写你自己的DLL。如果不想使用默认的CRT那么链接的时候就使用. /NODEFAULTLIB参数。这是一个写代码的好方法,这样你就可以使用这个参数在原来的LoadLibrary()和我提供的手动载入器之间切换(在我的样例中你可以通过切换到"Debug LoadLibrary"设置来切换)。这样你就可以轻松地测试BUG和崩溃是否是手动载入造成,另一个切换到原来的LoadLibrary()的好处是你可以只对DLL进行调试(因为调试器只能识别正常载入的模块)。

realZ
realZ
翻译于 2014/03/11 23:13
1

想使用我的手动加载器,就必须用到自定义函数GetProcAddress()去加载DLL.如果你想要使用对话框资源(或者一些其他类型的资源,但是对话框是使用最普遍的),由于需要正常、手动加载DLL工作: The inner working of FindResource() and LoadString() Win32 functions ,你能使用FindResource()函数 (和WinAPI函数:CreateDialogIndirect())。下载相关的附件(VC++2010),附件中包含一个加载和使用2个DLL的简单程序。一个DLL使用C开发,另一个使用C++开发。

漠天
漠天
翻译于 2014/03/10 18:11
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(1)

风暴突击者
风暴突击者
额,看不懂。
返回顶部
顶部