PE导入表总结

导入表

导入表是为实现代码重用而设置的。通过分析导入表数据,可以获得诸如PE文件的指令调用了多少外来的函数,以及这些外来函数都存在于哪些动态链接库里等信息。Windows加载器在运行PE时会将导入表中声明的动态链接库一并加载到进程的地址空间,并修正指令代码中调用的函数地址。数据目录中一共有四种类型的数据与导入表数据有关:

导入表

导入函数地址表

绑定导入表

延迟加载导入表

1、导入表概念

当程序调用了动态链接库的相关函数,在进行编译和链接的时候,编译程序和链接程序就会将调用的相关信息写入最终生成的PE文件中,告诉操作系统这些函数的执行指令字节码从哪里能够获取。这些信息就是导入表所描述的内容。

 

2、PE中的导入表

导入表是数据目录中注册的数据类型之一,其描述信息位于数据目录的第2个目录项中。IAT也是数据目录中注册的数据类型之一,其描述信息位于数据目录的第13个目录项中。

2.1、导入表描述符IAMGE_IMPORT_DESCRIPTOR

导入表数据的起始是一组导入表描述符结构。每组20个字节,最后一组为全0结构,表示导入表描述已经结束。可以通过导入表起始地址和这个空结构计算出导入表中引用的动态链接库的个数。

导入表的每一组都是一个结构,称为导入表描述符IMAGE_IMPORT_DESCRIPTOR,该结构的具体定义如下:

IMAGE_IMPORT_DESCRIPTOR STRUCT{

union

Characteristics dd ?

+0h OriginalFirstThunk dd ? ;桥1

ends

+4h TimeDateStamp dd ? ;时间戳

+8h ForwarderChain dd ? ;链表的前一个结构

+Ch Name1 dd ? ;指向链接库名字的指针

+10h FirstThunk dd ? ;桥2

}IMAGE_IMPORT_DESCRIPTOR ENDS

1】OriginalFirstThunk:双字。是一个指向另外一个数据结构的通路。该字段指向一个包含一系列结构的数组。

指向的数组中的每一个结构定义了一个导入函数的信息,最后以一个内容全为0的结构作为结束。指向的数组中每一项为一个结构,此结构的名称是IMAGE_THUNK_DATA。该结构实际上是一个双字。

2】TimeDateStamp:双字。时间戳,多为0。如果导入表项被绑定,那么绑定后的这个时间戳就被设置为对应DLL文件的时间戳。操作系统在加载时,可以通过这个时间戳来判断绑定信息是否过时。

3】ForwarderChain:双字。链表的前一个结构。

4】Name1:双字。Name1是一个RVA,它指向该结构所对应的DLL文件的名称,而这个名称是以“\0”结尾的Ansi字符串。

5】FirstThunk:双字。与OriginalFirstThunk相同,它指向链表定义了针对Name1这个动态链接库引入的所有导入函数,简称桥2。

2.2、导入函数地址表

PE文件中所有的导入函数jmp指令操作数的集合,组成了另外一个数据结构,这个结构就是导入函数地址表(Import Address Table,IAT)。该地址表是数据目录的第13个数据目录项。

导入函数地址表是一个双字的数组,每个双字代表的是一个导入函数的VA,该地址称为导入函数地址(Import Address,IA)。用户程序通过无条件跳转指令达到VA处,便可运行引入函数的指令。由于IAT中定义了不止一个链接库的函数,为了区分这些从不同链接库引入的函数,规定所有引入函数按照链接库分类:相同链接库的函数地址排列在一起,最后以一个双字的0结束。

导入表和IAT是有紧密联系的,通过桥2即可定位到IAT。在内存中,桥1可以让你找到函数名称或函数的索引编号,桥2却可以帮你找到该函数指令代码在内存空间的地址。当PE被加载进虚拟地址空间后,IAT的内容会被操作系统更改为函数的VA。这个修改最终会导致通向“值-名称”描述的桥2发生断裂。

2.3、绑定导入

绑定导入是一种提高PE加载速度的技术。它只能起辅助性的作用,它的存在与否只影响加载过程,并不影响PE的最终加载结果和运行结果。

2.3.1、绑定导入机制

双桥结构的导入表中,桥2是指向IAT的,Windows加载程序负责IAT中地址的修正工作。如果一个PE中导入的函数比较多,那么这部分工作就会占用一些时间,PE加载速度就会变慢。绑定导入的目的就是把由Windows加载程序负责的IAT地址修正工作提前到加载前进行,要么由用户手工完成,要么由专门的程序来完成;然后,通过在PE文件中声明绑定导入数据,以便告诉操作系统加载器说这部分工作不需要你做了。

但是在不同的操作系统中,动态链接库的基地址通常是不一样的。比如kernel32.dll,在Windows 2000中其加载到进程空间的基地址为0x77e60000,而在Windows XP SP3中其加载地址则是0x7c800000。同一动态链接库加载后处于不同的基地址,直接导致了同一个导入函数在不同操作系统中其导入地址VA是不一样的。

在为PE加入绑定导入机制的时候,微软就已经考虑到了这个问题,所以假定PE加载前对IAT的修正都是正确的。那么PE的加载速度是加快的,即使绑定以后的EXE程序在其他的兼容系统中运行时,其地址出现错误,PE加载也有检测错误的机制。如果地址检测出错误,PE加载器会重新接管这项工作,加载时对IAT进行修正。

微软提供bind.exe程序,该程序可以把导入表中IAT表项IMAGE_THUNK_DATA32的内容都静态替换成虚拟内存地址,然后在数据目录表的第12项指定的位置会声明这些更改。Windows在加载目标PE相关的动态链接库时,会首先检查这些地址是否符合绑定导入结构中描述的版本号,如果不符合或者DLL需要重定位,加载器就会去遍历OriginalFirstThunk指向的数组(也就是INT),计算新的地址。如果导入表是单桥结构,遍历则会失效。

2.3.2、绑定导入数据结构

绑定导入数据由一系列的绑定导入描述符IMAGE_BOUND_IMPORT_DESCRIPTOR的结构组成。每一个结构对应一个导入的动态链接库,它描述了导入动态链接库的版本信息(通过时间戳来定义):

IMAGE_BOUND_IMPORT_DESCRIPTOR STRUCT

+0h TimeDateStamp DWORD ? ;时间戳

+4h OffsetModuleName WORD ? ;指向DLL名称

+6h NumberOfModuleForwarderRefs WORD ? ;数目

}IMAGE_BOUND_IMPORT_DESCRIPTOR ENDS

结构中各字段的详细节是如下:

1】TimeDateStamp:该字段的值必须与要引用的DLL的文件头IMAGE_FILE_HEADER.TimeDateStamp字段值吻合,否则就会促使加载器去重新计算新IAT,这种情况一般发生在DLL版本不同时或者DLL映像被重定位时。

2】OffsetModuleName:以第一个IMAGE_BOUND_IMPORT_DESCRIPTOR作为基址,DLL名称字符串(ASCII且以“\0”结束)的偏移。

3】NumberOfModuleForWarderRefs:描述了紧接在此结构后的IMAGE_BOUND_FORWARDER_REF数组的元素个数。

IMAGE_BOUND_FORWARDER_REF STRUCT{

+0h TimeDateStamp DWORD ? ;时间戳

+4h OffsetModuleName WORD ? ;指向DLL名称

+6h Reserved WORD ? ;预留

}

出于不同的目的(代码更新、结构调整或实施补丁等),动态链接库中的某些函数的实现代码会被转移到别的动态链接库中。但为了提供向前的兼容,这些动态链接库还保留了该函数的定义。也就是说,一个导入函数涉及对多动态库的调用,数据结构IMAGE_BOUND_FORWARDER_REF就是因此产生的。它将引入函数涉及的所有动态链接库都列举出来。

 

参考资料:《Windows PE权威指南》

转载于:https://www.cnblogs.com/xingzherufeng/p/8337002.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值