PE结构——导入表

独行   ·   发表于 2020-11-03 11:58:25   ·   技术文章

前言

该文章第一部分表述导入表的概念,第二部分讲解导入表的结构,第三部分举例在PE文件当中找到导入表的内容。
注意:该文章不涉及延迟加载导入表等内容


第一部分

在讲解导入表之前,我们先想一想,在我们使用高级语言编写程序的时候是不是有个导入的操作。E.g Python 当中可以使用import 来导入其他库来使用库当中的函数。那么导入表是不是也是这样一个作用呢?答案是肯定的。导入表当中的数据就是指定了PE文件调用外来函数(这里外来函数是指不在本程序当中定义的函数)的数目,这些外来函数在哪些动态链接库当中等等。Windows 加载器在运行PE时会通过导入库将动态链接库一并加载到进程的地址空间当中。


第二部分

2.1;IMAGE_IMPORT_DESCRIPTOR

导入表是数据目录中注册的数据类型。描述信息位于数据目录的第2个目录项
导入表的当中每20个字节为一组数据结构,该数据结构名称为IMAGE_IMPORT_DESCRIPTOR(导入表描述符)详细内容如下图:

关键字“union” 表示Characteristics 和 OriginalFirstThunk 任意一个元素。这个结构体一共5个元素。每个元素的数据类型“dd” 占用4个字节。(注释:元素Name1保存着导入动态链接库的名称)其中我们需要关注的是第一个元素OriginalFirstThunk 和 第5个元素FirstThunk 。它们指向了另外一个数据结构。其中元素OriginalFirstThunk 指向的数组称之为INT。元素FirstThunk 指向的数据结构称之为IAT。

2.2;INT

数组INT当中的每一项均是一个结构,该结构名称为IMAGE_THUNK_DATA结构。如下图:

IMAGE_THUNK_DATA是一个双字,之后用双字“0”作为结束标志。

其中IMAGE_THUNK_DATA当中的双字指向了另外一个结构体IMAGE_IMPORT_BY_NAME(即IMAGE_THUNK_DATA保存着通往IMAGE_IMPORT_BY_NAME的RVA)

IMAGE_IMPORT_BY_NAME结构体定义如下:

2.3;IAT

IAT (函数地址表)同样也是数据目录当中注册的数据类型,描述信息保存在数据目录项中第13个目录项。
IAT的结构体为IMAGE_DATA_DIRECTORT。

2.4;导入表,INT,IAT之间的关系


导入表当中第一个元素和第五个元素保存着通往INT和IAT 的偏移地址。在INT 以及IAT 当中保存在导入的动态链接库的信息。

第三部分

接下来,我们用一个PE程序来查找一个PE文件的导入表的具体位置。

3.1;通过DOS头确定PE头的位置

通过DOS MZ头的最后一个元素来定位PE头的位置:
注释:在硬盘文件当中,PE文件的地址都是从0开始计算的。

3.2;定位PE扩展头的数据目录项位置

导入表信息注册在PE扩展头当中的数据目录项的第二个位置当中。数据目录项的文件偏移地址为78h。所以在文件中数据目录项的地址为b0h + 78h = 128h。

3.3;RVA转换为FOA

3.3.1;查看文件偏移

3.3.2;计算导入表在文件当中的位置
FOA = 0000002010h – 0000002000h + 0000000600h = 610h
注释:计算方式参考文章:http://www.nvnv.xyz/newsinfo/804196.html

3.4;在硬盘文件当中查看导入表内容

现在我们知道了导入表的地址,那么根据导入表描述符的定义,我们知道一个导入表当中的结构体占用了20个字节。导入表详细如下图:

3.5;重复3.2~3.4的内容,找到IAT内容

3.6;查看IMAGE_IMPORT_DESCRIPTOR第一个元素

IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk,该元素有两种不同的解释:
1;双字最高位为0,表示导入符号是一个数值,该数值是一个RVA。
2;双字最高位为1,表示导入符号是名称。
如图,我们查看到我们案例当中的最高位为0,那么该数值是一个RVA。

3.7;定位INT文件位置

我们回顾一下INT是一个数组,其中元素大小为双字,以双字0为结束符。通过以知的RVA计算出FOA。FOA=654h详细如下图

3.8;定位IMAGE_IMPORT_BY_NAME

INT当中每个双字元素,且保存着IMAGE_IMPORT_BY_NAME RVA地址。同样计算出FOA=65Ch。
IMAGE_IMPORT_BY_NAME.Hint

IMAGE_IMPORT_BY_NAME.Name1 大小不确定,表示函数名字符串名称 。以“\0”作为字符串结束标志。

局部总结:截至目前,我们已经知道了。系统通过PE文件当中的PE扩展头最后一个元素数据目录项当中的第二个元素保存着导入表的RVA和数据库大小的信息。通过该导入表RVA可以定位到导入表的内容。导入表由多个导入表描述符IMAGE_IMPORT_DESCRIPTOR组成。每个导入表描述符由20个字节组成,最后由20个0字节作为结束符号。导入表描述符的第一个元素保存着一个RVA地址。该地址是指向了结构体IMAGE_THUNK_DATA。IMAGE_THUNK_DATA结构体本质上是一个双字的结构。该结构保存着结构体IMAGE_IMPORT_BY_NAME。这个结构体当中保存着需要导入函数的编号以及函数名称。

3.9;查看IMAGE_IMPORT_DESCRIPTOR.FirstThunk

IMAGE_IMPORT_DESCRIPTOR.FirstThunk 是结构体当中最后一个元素,偏移地址为10h。详细位置如下图:

该元素是指定了IAT这个链表,它的值00002008h 表示RVA地址,计算出FOA地址608h。那么现在我们在IAT内容当中寻找该地址保存的内容是什么。如下图:很明显在IAT链表对应位置处保存的RVA地址为205Ch。计算FOA=65Ch。这个文件偏移地址当中对应的结构体为IMAGE_IMPORT_BY_NAME。 参考:3.8;定位IMAGE_IMPORT_BY_NAME。

局部总结:INT与IAT两个链表,在硬盘文件的保存状态中,指定的最终都是结构体IMAGE_IMPORT_BY_NAME。这样的状态就是俗称的双桥结构

3.10;加载到内存中的IAT表

由上面的研究可以知道,INT,以及IAT表最终指向的内容都是相同的,那么为什么这么做呢?事实上当PE文件加载进入内存当中运行的时候,IAT被PE加载器修改为导入函数的地址。那么,INT 当中保存着调用函数的名称或函数的索引编号。IAT保存函数指令代码的内存空间的地址。

用户名金币积分时间理由
veek 50.00 0 2020-11-03 16:04:14 一个受益终生的帖子~~

打赏我,让我更有动力~

1 条回复   |  直到 2020-11-11 | 1260 次浏览

urfyyyy
发表于 2020-11-11

我很欣慰,学院里居然有几个喜欢二进制的

评论列表

  • 加载数据中...

编写评论内容
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.