WINDOWS+PE权威指南读书笔记(6)

本文详细解析了PE文件的导入表(Import Table)和导入函数地址表(IAT),包括它们的结构、作用及在内存中的变化。通过实例分析了如何通过双桥结构定位导入函数,并阐述了在程序加载到内存后,IAT如何转变为函数的入口地址。内容涵盖了导入表的查找方法和动态链接库的函数导入过程。
摘要由CSDN通过智能技术生成

目录

导入函数地址表:(IAT)

导入表双桥结构应用举例:


导入函数地址表:(IAT)

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

导入函数地址表是一个双字的数组,每个双字代表的是一个导入函数的VA,该地址称为导入函数地址(Import Address,IA)。用户程序通过无条件跳转指令JMP跳转到 VA 指定处.

由于 IAT中定义了不止一个链接库的函数,为了区分这些从不同链接库引入的函数,规定所有引入函数按照链接库分类 ; 相同链接库的函数地址排列在一起,最后以一个双字的0结束。

导入表和 IAT 是有紧密联系的,通过桥 2即可定位到 IAT。在内存中,桥 1 可以让你找到调用的函数名称或函数的索引编号(“值 - 名称”),桥 2 却可以帮助你找到该函数指令代码在内存空间的地址。

当 PE 被加载进虚拟地址空间(内存)以后,IAT 的内容会被操作系统更改为函数的 VA。这个修改最终会导致通向“值 - 名称”描述的桥 2 发生断裂。

当桥 2 发生断裂以后,如果没有桥 1 作为参照〈因为桥 1 和桥 2 维护了两个一 一对应的函数 RVA,就是“值-名称”对应函数地址),我们就无法重新找到该地址到底是调用了哪个函数。这就是为什么会在导入表数据结构中存在两个桥的原因,也是为什么单桥导人表结构中无法实施绑定的原因 。

导入表双桥结构应用举例:

构造调用同一个 DLL 文件的多个函数的导入表:

代码很简单,首先调用函数 FindWindow 获取任务栏的句柄 〈行24) 存储到 hTray 变量中。然后调用 ShowWindow (行 26) 将任务栏窗口隐藏起来,最后通过函数 EnableWindow 将任务栏设置为禁止操作。编译链接生成最终要分析的 PE 文件 LockTray.exe。

LockTray 的导入表分析:

use32.dll动态链接库的导入函数分析

程序中一共使用了 user32.dll 的三个API 函数,依次为 : EnableWindow、FindWindow和 ShowWindow,下面具体分析导入表构造:

>> 5C 20 00 00

桥 1(导入表的第一个双字),最高位为 0,表示这是一个RVA,表示函数是以字符串类型的函数名导入的。从文件偏移 0x0000065C 处取出的值应该是一组 IMAGE_THUNK_DATA,一直找到双字 0为止。取出的这些 IMAGE_THUNK_DATA 结构依次是:

前三个 RVA 分别指向了 IMAGE_IMPORT_BY_NAME 结构,代表调用的三个函数字符串文件偏移地址:

下面分析的是桥2,上面分析的是桥1,但是桥2分析方法同桥1,桥2在618+10 =628h处:

>> 08 20 00 00

桥 2(导入表的第五个双字),最高位为 0,表示这是一个RVA,表示函数是以字符串类型的函数名导入的。从文件偏移 0x00000608h 处取出的值应该是一组 IMAGE_THUNK_DATA,一直找到双字 0为止。取出的这些 IMAGE_THUNK_DATA 结构依次是:

注意:

桥2有比桥1明确的IAT地址范围,IAT地址在600h~618h处,所以桥2的 IMAGE_THUNK_DATA 就在这个IAT范围里找。

>> 8A 20 00 00

>> 7C 20 00 00

>> 6C 20 00 00

>> 00 00 00 00

前三个 RVA 分别指向了 IMAGE_IMPORT_BY_NAME 结构,代表调用的三个函数字符串文件偏移地址:

kernel32.dll动态链接库的导入函数分析

找出导入表范围内(618h~ 654h)kernel32.dll的桥1,桥2。其它分析方法同user32.dll:

用一个图来表示 LockTray.exe 的导入表结构:

文件中的 LockTray 导入表,在文件中时桥2和桥1指向的都一样都是指向函数名,所以这里没有画出来桥2的函数指针表:

装入内存后的 LockTray.exe 的导入表,这里桥2指向的函数入口地址暂未有具体分析:

从现在掌握的对导入表的知识来看,定位导入函数地址表的方法有两种:

1:第一种方法是从导入表的最后一个导入表项 IMAGE_IMPORT_DESCRIPTOR 结构中的字段 IMAGE_IMPORT_DESCRIPTOR.FirstThunk 的桥2定位 IAT。

2:第二种方法是通过PE头数据结构的数据目录项IMAGE_DATA_DIRECTORY的第 13 个数据项的描述直接定位 IAT。(具体可看导入表定位目录)

3:诚如结构里所描述的那样,每个动态链接库都维护了自己的 IAT 内容,而且不同链接库维护的这些内容可以是不连续的,这在后面还会讲到。

4:当程序加载到内存以后,导入表部分发生变化的值正是 IMAGE_IMPORT_DESCRIPTOR结构中的 FirstThunk(桥2) 字段指向的函数指针表内容。这些内容已经不是指向函数名的指针了,而是指向了虚拟内存中该函数的可执行代码的地址! 所以其含义也由原来的函数指针更改为函数的入口地址。现在看来,所有的这些值最终都指向了同一片连续的区域,从而形成了我们常说的 IAT。

从另一个侧面分析了导入表在内存中的数据组织:(2000h是IAT,2010h是导入表)

如图所示:(IAT地址在内存中变成函数入口地址了)

(1) 是user32.dll 动态链接库。

(2) 是 kernel32.dll 动态链接库。

①是指向引入 user32 函数名的指针数组,由导入表范围内红框中的两个动态链接库对应的桥1指向。

②是指向引入 kernel32 函数名的指针数组,由与桥1相隔5个字节的蓝框中的桥2指向。

① '是指向引入 user32 国数入口地址的指针数组。

② '是指向引入 kernel32 函数入口地址的指针数组 。

①'+②'组成了标准的IAT。如果在内存中,这里的地址会被修改成相关函数的绝对地址,程序只要跳转到这里就可以执行相关函数了。

图中涉及与导入表有关的所有数据,但是狭义上的导入表却只是几个 IMAGE_IMPORTDESCRIPTOR 结构而已,因为在数据目录中记录的导入表长度只计算了这些结构的大小,其他的数据并没有包含在内! 这一点一定要清楚。上图的导人表长度只有 3ch 个字节,但与导入表相关的数据却长达 92h 个字节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值