延迟加载Dll的实现原理
一. 简介
延迟加载Dll是一个混合方式,它通过LoadLirary和GetProcAddress获得延迟加载函数的地址,然后直接转向对延迟加载函数的调用。
延迟加载不是操作系统的一个特征,它完全是通过链接器和运行库加入额外的代码和数据来实现的。同样地,无法在WINNT.H里找到关于延迟加载的更多参考,不过,可以在延迟装入数据(Delayload Data)和常规的输入数据之间看到一定的相似之处。
Datadirectory数组中的IMAGE_DIRECTORY_ENTRY_IMPORT条目指向延迟加载的数据。这是一个指向ImgDelayDescr结构数组的RVA,这个结构定义在VisualC++的DelayImp.h中,表1.1说明了它的内容。每一个被延迟加载的Dll都对应一个ImgDelayDescr结构。
表1.1 ImgDelayDescr 结构
| 大小 | 成员 | 描述 |
| DWORD | grAttrs | 这个结构的属性。目前唯一被定义的旗标是dlattrRva,表明这个结构中的字段应该被认为是RVA,而不是虚地址 |
| RVA | rvaDLLName | 指向一个被输入的DLL的名称的RVA。这个字符串被传递给LoadLibrary |
| RVA | rvaHmod | 指向一个HMODULE大小的内存位置的RVA。当延迟装入的DLL被装入内存后,它的模块句柄(hModule)被保存在这个地方 |
| RVA | rvaIAT | 指向这个Dll的输入地址表的RVA,它与常规的IAT的格式相同 |
| RVA | rvaINT | 指向这个DLL的输入名称表的RVA,它与常规的INT表格式相同 |
| RVA | rvaBoundIAT | 可选的绑定IAT的RVA,指向这个DLL的输入地址表的绑定拷贝,它与常规的IAT表的格式相同,目前,这个IAT的拷贝并不是实际的绑定,但是这个特征可能会加到绑定程序的未来版本中 |
| RVA | rvaUnloadIAT | 原始IAT的可选拷贝的RVA。它指向这个DLL的输入地址表的未绑定拷贝。它与常规的IAT表的格式相同,通常设为0 |
| DWORD | dwTimeStamp | 延迟装入的输入DLL的时间/日期戳,通常设为0 |
ImgDelayDescr结构的关键在于它包括了对应了DLL的IAT和INT的地址,这些表在格式上与常规的输入表是相同的,唯一的区别是他们有运行库代码而不是由操作系统进行写入和读出。当第一次从一个延迟装入的DLL中调用一个API函数时,运行库调用LoadLibrary(如果需要),然后是GetProcAddress,最后得到的地址被存在延迟加载的IAT表中。这样以后每次调用这个API时都会直接到这里来。
在Visual C++6.0 中有延迟装入数据最初的原型。ImgDelayDescr中所有包含地址的域均是虚拟地址,而不是RVA。换句话说,它们包含延迟装入数据所在位置的实际地址。这些域是双字,是x86上一个指针的大小。现在向IA-64的快速移植正在被支持。很显然4个字节不足以装下一个完整的地址,在这一点上,微软做了件正确的事情,已将包含地址的字段改为RVA。表1-10中已经使用修正的结构定义和名称。
关于在ImgDelayDescr结构中使用RVA还是虚拟地址,现在仍有争论,在这个结构中有一个字段是旗标值。当grAttrs字段被设为1时,结构成员被当做RVA。这是惟一与Visual Studio.net和64位编译器一起出现的选项。如果在grAttrs中的这个位关掉,ImgDelayDescr字段将是虚拟地址。

本文详细探讨了DLL延迟加载的实现原理,从Windows系统如何处理延迟加载的DLL请求,到应用程序如何利用这一特性优化性能,同时分析了延迟加载的优势与潜在问题。通过实例解析,帮助读者深入理解这一技术。
最低0.47元/天 解锁文章
1483

被折叠的 条评论
为什么被折叠?



