PE文件格式详解(五)――Improt Table(引入表)

这节即将学习的Import Table和下节的Export Table关系密切,两者联合起来就可以解决我们开始提出的问题。在说明Import Table和Export Table的作用之前先让我们明白编译器是如何处理我们调用外部库函数的。在PE 文件中,当你调用另一模块中的函数(例如USER32.DLL 中的GetMessage),编译器制造出来的CALL 指令并不会把控制权直接传给DLL 中的函数,而是传给一个JMP DWORD PTR [XXXXXXXX] 指令,后者也位于.text 中。JMP 指令跳到一个地址去,此地址储存在.idata 的一个DWORD之中。这个DWORD 内含该函数的真正地址(函数进入点),如图1 所示


                                                    图1

    那么,这样做有什么好处呢?试想一下,如果CALL指令后面跟的直接就是DLL中的函数地址,那么加载器就需要修补每一个调用DLL 的指令。而现在PE 载入器需要做的,就只是把DLL 函数的真实地址放到.idata 的那个DWORD 之中,根本就没有程序代码需要修补。嗯,现在比较清除了,加载器首先要知道所加载的程序调用了哪些DLL的哪些函数,然后找出这些函数的地址,把他们添入到.idata 的那些DWORD 之中。那么加载器如何知道所加载的程序调用了哪些DLL的哪些函数,这就是Import Table的作用;加载器又是如何找出这些函数的地址呢,这又是Export Table的作用。现在两者的作用都很清除了,剩下的关键问题就是PE加载其如何利用这两个东东来完成上面的任务,完成了这个任务也就解决了我们开始提出的问题。这节我们先讨论前半部分,也就是加载器如何利用Import Table找出所加载的程序调用了哪些DLL的哪些函数。

    首先,最基本的是加载器如何找到Import Table呢?这就要利用前面我们提到的数据目录(Data Directory),它是Option Header结构中的最后一个域。Data Directory 是一个 IMAGE_DATA_DIRECTORY 结构数组,共有16个成员,每个成员包含了一个重要数据结构的信息(RVA 和 大小),并且这些重要数据结构的信息在IMAGE_DATA_DIRECTORY 结构数组中的位置是固定的,这样加载器就很容易找到需要的信息了。这就好比你有一个书架每一层都放着不同种类的书籍,并且它们始终固定,即:第一层始终放小说,第二层始终放散文…等等,当你需要散文的时候你就可以毫不犹豫的去拿第二层上的书就可以了。下面的表就是IMAGE_DATA_DIRECTORY 结构数组的布置情况:(第0,1,12三项是和我们这两节解决问题有关系的;第5项在最后一节中用到;第9项在线程中用到)。

Member

Info inside

0

Export symbols

1

Import symbols

2

Resources

3

Exception

4

Security

5

Base relocation

6

Debug

7

Copyright string

8

Unknown

9

Thread local storage (TLS)

10

Load configuration

11

Bound Import

12

Import Address Table (IAT)

13

Delay Import

14

COM descriptor

     如果您还记得节表可以看作是PE文件各节的根目录的话,也可以认为 Data Directory 是存储在这些节里的逻辑元素的根目录。明确点,Data Directory 包含了PE文件中各重要数据结构的位置和尺寸信息。Data Directory的每个成员都是 IMAGE_DATA_DIRECTORY 结构类型的,其定义如下所示:

IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress dd ?
isize dd ?
IMAGE_DATA_DIRECTORY ENDS

VirtualAddress 实际上是数据结构的相对虚拟地址(RVA)。比如,如果该结构是关于import symbols的,该域就包含指向IMAGE_IMPORT_DESCRIPTOR 数组的RVA。 isize 含有VirtualAddress所指向数据结构的字节数。

待续....

你可能感兴趣的:(数据结构,image,table,dll,import,Descriptor)