PE文件:延迟导入表

0x00 作用

延迟导入表从它的名字上,我们可以知道他是一种加载比较延迟的导入表,不是在一开始就被加载的,而是等到要被使用的时候,才会被延迟加载(动态加载相关链接库并修正函数的虚拟地址,实现对函数的调用)。
目的:
1.提高程序的加载速度。
2.提高兼容性:有的函数在老的Windows版本中并不存在,但是在新的Windows版本中存在,而我们想要写一个Dll来实现一个动态的判断,即可以根据操作系统的版本来决定动态库是否要被加载,则可以使用延时导入技术。

延时导入的局限性:
1.一个导出了全局变量的的Dll是无法被延时加载的。
2.Kernel32.dll是不能被延时加载的,因为要加载好该模块才能调用LoadLibrary和GetProcAddress。
3.不应该在DllMain的入口函数中调用一个延时载入的函数,因为这样可能引起程序奔溃(可能此时的Dll并没有被加载上)。

0x01 结构

数据目录的第十四个成员。

typedef struct ImgDelayDescr {  
    DWORD           grAttrs;           // attributes  
    RVA             rvaDLLName;     // RVA to dll name  
    RVA             rvaHmod;            // RVA of module handle  
    RVA             rvaIAT;               // RVA of the IAT  
    RVA             rvaINT;              // RVA of the INT  
    RVA             rvaBoundIAT;    // RVA of the optional bound IAT  
    RVA             rvaUnloadIAT;   // RVA of optional copy of original IAT  
    DWORD           dwTimeStamp;    // 0 if not bound,  
                                    // O.W. date/time stamp of DLL bound to (Old BIND)  
} ImgDelayDescr, * PImgDelayDescr;  
typedef const ImgDelayDescr *   PCImgDelayDescr;  

grAttrs: 用来区分版本,1是新版本,0是旧版本,旧版本中后续的rvaxxxxxx域使用的都是指针,而新版本中都用RVA,我们只讨论新版本。
rvaDLLName:一个RVA,指向导入DLL的名字。
rvaHmod: 一个RVA,指向导入DLL的模块基地址,这个基地址在DLL真正被导入前是NULL,导入后才是实际的基地址。
rvaIAT: 一个RVA,表示导入函数表,实际上指向IAT,在DLL加载前,IAT里存放的是一小段代码的地址,加载后才是真正的导入函数地址。
rvaINT: 一个RVA,指向导入函数的名字表。 rvaUnloadIAT:延迟导入函数卸载表。
dwTimeStamp:延迟导入DLL的时间戳

0x02 设置方式

延迟导入表的设置是通过VS设置来实现的,比较简单。

【DLL的延迟加载】:
1.需要DLL,MyLib.lib导入库及MyLib.h 进行【隐式加载】的全步骤
2.属性->配置属性->链接器->输入->延迟加载的Dll-> 添加MyDll.dll
(注意/DelayLoad:MyDll.dll这个开关不能用#pragma comment(linker, “/DelayLoad:MyDll.dll”)来设置。
3.属性->配置属性->链接器->输入->附加依赖项-> 添加DelayImp.lib
//也可以用#include <delayimp.h>和#pragma comment(lib, “Delayimp.lib”)
//这个开关告诉链接器将delayimp中的__delayLoadHelper2函数嵌入到我们的可执行文件中。
4.如果需要自动卸载Dll,则需在可选 属性->配置属性->链接器->高级->卸载延迟加载的DLL->是 (/DELAY:UNLOAD);
//此时只能调用__FUnloadDelayLoadedDll2(PCSTR szDll)函数,而不能调用FreeLibrary
//并且传入的参数不包含路径,且名称与延迟加载的Dll中配置的参数必须保持一致,如果不打算卸载,就可以不指定/DELAY:UNLOAD

装载延迟导入表:实际使用的函数是实际上使用的函数是:__delayLoadHelper2
在这里插入图片描述
**卸载延迟导入表:**实际使用的函数是FUnloadDelayLoadedDLL()。
在这里插入图片描述

注意:
延迟导入的加载只发生在函数第一次被调用的时候,之后IAT就填充为正确函数地址,不会再走__delayLoadHelper了。
延迟导入一次只会导入一个函数,而不是一次导入整个模块的所有函数。

发布了19 篇原创文章 · 获赞 21 · 访问量 984

猜你喜欢

转载自blog.csdn.net/weixin_43742894/article/details/105252674