dll 高级技术中--函数转发器、KNOWDLL、DLL重定向知识收集。

4、函数转发器

      函数转发器(function forwarder)是DLL输出段中的一个条目,用来将一个函数调用转发到另一个DLL中的另一个函数。例如:如果用Visual C++的Dumpbin工具来查看Kernel32.dll那么我们会看到类似下面的输出。

c:/Windows/System32>dumpbin -exports Kernel32.dll

 

 710  2C5          RtlFillMemory (forwarded to NTDLL.RtlFillMemory)

 711  2C6          RtlMoveMemory (forwarded to NTDLL.RtlMoveMemory)

 712  2C7          RtlUnwind (forwarded to NTDLL.RtlUnwind)

 713  2C8          RtlZeroMemory (forwarded to NTDLL.RtlZeroMemory)

这个输出显示了4个被转发的函数,如果应用程序调用了RtlFillMemory,那么我们的可执行文件会被动的链接到Kernel32.dll。当可执行文件运行的时候,加载程序会载入Kernel32.dll并发现被转发的函数实际上是在NTDLL.dll中,然后他会将NTDLL.dll模块也一并载入。当可执行文件调用RtlFillMemory的时候,它实际上调用的是NTDLL.dll中的RtlFillMemory函数。

      如果我们调用RtlFillMemory ,那么GerProcAddress会先在Kernel32.dll的导出段中查找,并发现RtlFillMemory是一个转发器函数,于是它会递归调用GetProcAddress,在NTDLL的导出段中查找RtlFillMemory 函数。

      我们也可以在自己的DLL模块中使用函数转发器,最简单的方法是使用pragma指示符,如下所示:

#pragma comment(linker,"/export:someFunc=DllWork.someOtherFunc")

这个pragma告诉链接器,正在编译的DLL应该导出一个名为someFunc的函数,但实际上实现someFunc的是另一个名为someOtherFunc的函数,该函数被包含在另一个DllWork.dll的模块中。我们必须为每个想要转发的函数单独创建一行pragma。

5、已知的DLL

      系统对操作系统提供的某些DLL进行了特殊处理,这些DLL被称为已知的DLL(known DLL)。除了操作系统在载入它们的时候总是在同一个目录中查找之外,它们与其它的DLL并没有什么不同。在注册表中有这么一个注册表项:

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/KnownDLLs

      我们可以看到,这个注册表项包含了一组值名,这些值名是一些DLL的名称。每个值名的数据正好等于值名加上.dll扩展名。(但这并不是必须的,稍候就会看到)。当LoadLibrary或LoadLibraryEx被调用的时候,函数首先会检查我们传入的DLL的名字是否包含了.dll扩展名。如果没有包含,那么函数会用正常的搜索顺序来搜索这个DLL。

如果我们指定了.dll扩展名,那么这两个函数会先将扩展名去掉,然后再在KnownDLLs注册表中搜索,看其中是否有与之相符的值名。如果没有值名与之相符,那么函数会使用正常的搜索规则。但是,如果找到了与之相符的值名,那么系统会查看与值名相对于的数据,并试图用该数据来载入DLL。系统还会从注册表项的DllDirectory值所表示的目录中开始搜索DLL。在Windows XP中DllDirectory的默认值为%SystemRoot%/system32

      为了举例说明这一过程,假设我们在KnownDLLs注册表项中添加了下列值:

value name:SomeLib

value data:SomeOtherLib.dll

调用LoadLibrary("SomeLib");的时候,系统会用正常的搜索规则来对这个DLL进行定位.

但是,如果调用LoadLibrary("SomeLib.dll");的时候,系统会在knownDllsz注册表中发现有一个与之相符的名称。因此系统试图载入的DLL是SomeOtherLib.dll而不是SomeLib.dll。它首先会在%SystemRoot%/system32目录中查找SomeOtherLib.dll。如果在这个目录中找到了该文件,那么系统就会将它载入,如果系统未能在这个目录中找到该文件,那么LoadLibrary会失败并返回NULL,这时调用GetLastError将返回ERROR_FILE_NOT_FOUND。

6、DLL重定向

      最初开发Windows的时候,内存和磁盘空间都非常宝贵,因此为了节约这些宝贵的资源,Windows的设计目标是尽可能的共享资源。出于这样的考虑,Microsoft建议将多个应用程序所共享的模块放在Windows的系统目录中,这使得系统能方便的定位和共享文件。C/C++运行库以及MFC就是很好的例子。

      随着时间的推移,这成为了一个严重的问题,这是因为安装程序可能会用老版本的文件覆盖这个目录中的文件,或用不完全兼容的新版本的文件覆盖这个目录中的文件,从而妨碍用户的其它应用程序的正常运行。今天,硬盘不仅容量大而且价格便宜,内存也够用而且相对来说价格比以前要便宜很多。因此,Microsoft现在强烈建议开发人员将应用程序的文件放到自己的目录中,并且绝对不要碰Windows系统目录中的任何东西。这样既可以防止我们的应用程序妨碍其它应用程序,也可以避免其它应用程序妨碍我们的应用程序。

      为了帮助开发人员,Microsoft从Windows 2000开始新增了一项DLL重定向特性。这项特性强制操作系统的加载程序 首先从应用程序的目录中载入模块。只有加载程序无法找到要找的文件时,才会在其它目录中搜索。

      为了强制加载程序总是先检查应用程序的目录,我们所要做的就是将一个文件放到应用程序的目录中。这个文件的内容无关紧要,但它的文件名必须是AppName.local。举个例子,如果我们有一个名为SuperApp.exe的可执行文件,那么重定向文件的名称必须是SuperApp.exe.local。

      LoadLibrary(Ex)在内部做了修改,来检查这个文件存在与否。如果应用程序的目录中存在这个文件,那么系统会载入这个目录中的模块。如果应用程序的目录中不存在这个文件,那么LoadLibrary的工作方式与以往相同。注意:除了创建一个.local文件,我们还可以创建一个名为.locald的文件夹。在这种情况下,我们可以将自己的DLL保存在这个文件夹中,让Windows能够轻易找到他们。

      注意:为了安全性的缘故,Windows vista中这项特性在默认情况下是关闭的----因为它可能会使系统从应用程序的文件夹中载入伪造的系统DLL,而不是从Windows的系统文件夹中载入真正的系统DLL。为了打开这项特性,我们必须在HKLM/SoftWare/Microsoft/WindowsNT/CurrentVersion/Image File Execution Options注册表项中增加一个条目DWORD DevOverrideEnable,并将它的值设为1。

dll 高级技术中--函数转发器、KNOWDLL、DLL重定向知识收集。

上一篇:reStructuredText


下一篇:系统配置常量,域名根目录方法