当前位置: 首页 > article >正文

《Windows PE》4.2 绑定导入表

绑定导入表(Bound Import Table)是文件中的一个数据结构,用于存储已经绑定(即完成绑定导入)的外部函数的信息。

本节必须掌握的知识点:

        绑定导入表数据结构

        实例分析

4.2.1 绑定导入表数据结构

绑定导入是指在程序加载时,将程序所依赖的外部函数的地址与程序的IAT(Import Address Table,导入地址表)进行绑定的过程。这样,程序就可以直接通过IAT中的函数指针来调用这些外部函数,而无需再进行动态链接的过程。

绑定导入可以分为静态绑定和动态绑定两种方式:

●静态绑定:

在编译时,链接器将外部函数的地址直接嵌入到程序的IAT中。

静态绑定生成的可执行文件会包含被依赖函数的实际地址,因此在程序加载时无需进行额外的动态链接操作。

静态绑定的优点是加载速度快,无需运行时动态链接库的支持。但缺点是可执行文件的大小会增加,并且无法在运行时动态地加载新的函数或库。

●动态绑定:

在程序加载时,操作系统会根据IAT中的函数名称来查找并绑定外部函数的实际地址。

动态绑定通过操作系统的动态链接器(如Windows的LoadLibrary和GetProcAddress函数)来实现。

动态绑定的优点是可执行文件的大小较小,可以在运行时动态加载新的函数或库。但缺点是加载过程相对较慢,因为需要进行额外的符号解析和地址重定位操作。

绑定导入的具体实现方式取决于操作系统和编程语言。在Windows环境下,可以使用PE文件格式、IAT表和动态链接库(DLL)来实现绑定导入。在Linux环境下,可以使用ELF文件格式和动态链接器(ld.so)来实现绑定导入。

当可执行文件被绑定时,IAT 中的IMAGE_THUNK_DATA结构将被导入函数的实际地址覆盖。磁盘上的可执行文件具有其 IAT 中其他 DLL 中 API 的实际内存中地址。加载绑定的可执行文件时,Windows 加载程序可以绕过查找每个导入的 API 并将其写入 IAT 的步骤。正确的地址已经在那里了!

您可能对可执行绑定的安全性持怀疑态度。毕竟,如果绑定可执行文件并且它导入的 DLL 发生变化怎么办?比如kernel32.dll,在Windows 2000中其加载到进程空间的基地址为 0x77e60000,而在Windows XP SP3中其加载地址则是0x7c800000。发生这种情况时,IAT 中的所有地址都无效。在为PE加入绑定导入机制的时候,微软就已经考虑到了这个问题,所以假定PE加载前对IAT的修正都是正确的。那么PE的加载速度是加快的,即使绑定以后的EXE程序在其他的兼容系统中运行时,其地址出现错误,PE加载也有检测错误的机制。如果地址检测出错误, PE加载器会重新接管这项工作,加载时对IAT进行修正。

微软提供了一个绑定工具bind.exe程序,该程序可以把导入表中IAT表项IMAGE_ THUNK_DATA32的内容都静态替换成虚拟内存地址,然后在数据目录表的第12项指定的位 置声明这些更改。Windows在加载目标PE相关的动态链接库时,会首先检査这些地址是否正确合法,这些检查包括当前系统的DLL版本是否符合绑定导入结构中描述的版本号,如果不符合或者DLL需要被重新定位,加载器就会去遍历OriginalFirstThunk指向的数组(也就是 INT),计算新的地址。如果导入表是单桥结构,此时的遍历会失效,所以说单桥结构无法实 施静态绑定操作。

 注意

绑定导入是一种静态链接的方式,因此在程序运行期间无法更改绑定的外部函数地址。如果需要在运行时动态加载新的函数或库,可以考虑使用延迟加载或手动加载的方式。

本节我们讲述PE文件中的静态绑定导入,我们将在下一小节讲述延迟加载,在第十章详细讲述动态手动加载。

●绑定导入表描述符的结构如下:

typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {

    DWORD TimeDateStamp;    //时间戳

    WORD OffsetModuleName;// 指向DLL的名称

    WORD NumberOfModuleForwarderRefs;// ModuleForwarderRef 数目

    // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows

} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;

TimeDateStamp,一个 DWORD,其中包含导入的 DLL 的时间/日期戳。

OffsetModuleName,一个 WORD,其中包含指向具有导入 DLL 名称的字符串的偏移量。此字段是与第一个IMAGE_BOUND_IMPORT_DESCRIPTOR的偏移量(不是 RVA)。

NumberOfModuleForwarderRefs,一个 WORD,其中包含紧跟此结构的IMAGE_BOUND_FORWARDER_REF结构数。这些结构与IMAGE_BOUND_IMPORT_DESCRIPTOR相同,只是保留了最后一个 WORD(NumberOfModuleForwarderRefs)。

在简单的情况下,每个导入的DLL的IMAGE_BOUND_IMPORT_DESCRIPTORs将是一个简单的数组。但是,当绑定到转发到另一个DLL的API时,还必须检查转发的DLL的有效性。因此,IMAGE_BOUND_FORWARDER_REF结构与IMAGE_BOUND_IMPORT_DESCRIPTORs结构交织在一起。

假设您链接到HeapAlloc,它被转发到NTDLL中的RtlAllocateHeap。然后在可执行文件上运行BIND。在你的EXE中,你会有一个IMAGE_BOUND_IMPORT_DESCRIPTOR for KERNEL32.DLL,然后是一个IMAGE_BOUND_FORWARDER_REF for NTDLL.DLL。紧随其后的可能是您导入和绑定的其他dll的附加IMAGE_ BOUND_IMPORT_DESCRIPTORs。

绑定导入表中的每个元素都是一个被绑定的外部函数的地址。这些地址在程序加载时由操作系统或动态链接库进行填充,以便程序可以直接调用这些函数。

 注意

1.绑定导入表是可选的,不是所有的PE文件都会包含绑定导入表。只有在编译时进行了绑定导入操作,并且保留了绑定导入表的信息,才会在PE文件中存在绑定导入表。

2.绑定导入表的目的是为了提高程序的运行效率,避免了每次运行时都进行动态链接的开销。通过在程序加载时将外部函数的地址直接嵌入到绑定导入表中,可以加快程序的启动速度。

       3.同一操作系统的不同版本导入函数地址可能存在差异,这就意味着绑定导入函数的地址可能存在兼容问题的风险。如果绑定导入函数地址错误,操作系统PE加载器会启用导入表实现IAT表的重新绑定。

       4.绑定导入表通常会存在于32位PE文件中,64位PE文件并不包含绑定导入表。

4.2.2 实例分析

实验二十九:绑定导入表的定位及解析

       绑定导入表的RVA地址及大小信息位于数据目录项的第11项。我们以32位记事本程序为例:

       ●数据目录项:第11项。

将notepad32.exe拖入WinHex,并找到数据目录项,如下所示:

00000150   00 00 00 00 10 00 00 00  00 00 00 00 00 00 00 00   ................

00000160   04 76 00 00 C8 00 00 00  00 B0 00 00 20 7F 00 00   .v..?...?. ...

00000170   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000180   00 00 00 00 00 00 00 00  50 13 00 00 1C 00 00 00   ........P.......

00000190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

000001A0   00 00 00 00 00 00 00 00  A8 18 00 00 40 00 00 00   ........?..@...

000001B0   50 02 00 00 D0 00 00 00  00 10 00 00 48 03 00 00   P...?......H...

000001C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

000001D0   00 00 00 00 00 00 00 00  2E 74 65 78 74 00 00 00   .........text...

       数据目录项第11项中记录绑定导入表的RVA地址为:00000250H,大小为D0H。

●绑定导入表:位于文件偏移地址250H地址处。

      

00000250   A2 BD 02 48 58 00 00 00  B6 BD 02 48 65 00 00 00   ⒔.HX...督.He...

00000260   CA BD 02 48 71 00 00 00  6C BD 02 48 7E 00 00 00   式.Hq...l?H~...

00000270   6C BD 02 48 8B 00 00 00  89 BD 02 48 96 00 00 00   l?H?..壗.H?..

00000280   C6 BD 02 48 A3 00 01 00  C5 BD 02 48 B0 00 00 00   平.H?..沤.H?..

00000290   81 BD 02 48 BA 00 00 00  BD BD 02 48 C4 00 00 00   .?H?..浇.H?..

000002A0   00 00 00 00 00 00 00 00  63 6F 6D 64 6C 67 33 32   ........comdlg32

000002B0   2E 64 6C 6C 00 53 48 45  4C 4C 33 32 2E 64 6C 6C   .dll.SHELL32.dll

000002C0   00 57 49 4E 53 50 4F 4F  4C 2E 44 52 56 00 43 4F   .WINSPOOL.DRV.CO

000002D0   4D 43 54 4C 33 32 2E 64  6C 6C 00 6D 73 76 63 72   MCTL32.dll.msvcr

000002E0   74 2E 64 6C 6C 00 41 44  56 41 50 49 33 32 2E 64   t.dll.ADVAPI32.d

000002F0   6C 6C 00 4B 45 52 4E 45  4C 33 32 2E 64 6C 6C 00   ll.KERNEL32.dll.

00000300   4E 54 44 4C 4C 2E 44 4C  4C 00 47 44 49 33 32 2E   NTDLL.DLL.GDI32.

00000310   64 6C 6C 00 55 53 45 52  33 32 2E 64 6C 6C 00 00   dll.USER32.dll..

      

 

总结

       Notepad32.exe中共有10个_IMAGE_BOUND_IMPORT_DESCRIPTOR绑定导入表描述符结构,对应10个DLL模块名。

       每个绑定导入表描述符的前4个字节为时间戳。

       OffsetModuleName字段指向DLL模块名。以第一个绑定导入表描述符为例,RVA地址为0058H,FOA地址=0058H+250H=2A8H,即comdlg32.dll。

       NumberOfModuleForwarderRefs字段为ModuleForwarderRef (模块转发器)数目。以284H地址处的A3 00 01 00为例,A3指向2F3H地址处的KERNEL32.dll,模块转发器数量为1,即紧随其后的B0 00 00 00,指向300H地址处的NTDLL.DLL。

       接下来我们再观察一下IAT导入函数地址表。IAT表的RVA地址为数据目录项的第12项,RVA值为00001000H,大小为348H。位于.text节区的起始位置,即文件偏移地址00000400H地址处。

       ●节表:绑定导入表位于.text节区400H文件偏移地址处。

000001D0   00 00 00 00 00 00 00 00  2E 74 65 78 74 00 00 00   .........text...

000001E0   48 77 00 00 00 10 00 00  00 78 00 00 00 04 00 00   Hw.......x......

000001F0   00 00 00 00 00 00 00 00  00 00 00 00 20 00 00 60   ............ ..`

00000200   2E 64 61 74 61 00 00 00  A8 1B 00 00 00 90 00 00   .data...?......

00000210   00 08 00 00 00 7C 00 00  00 00 00 00 00 00 00 00   .....|..........

00000220   00 00 00 00 40 00 00 C0  2E 72 73 72 63 00 00 00   ....@..?rsrc...

00000230   20 7F 00 00 00 B0 00 00  00 80 00 00 00 84 00 00    ....?..€...?.

00000240   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

       ●IAT导入函数地址表:大小384H,以被填充导入函数的实际VA地址。

00000400   EF 6F DA 77 17 6C DA 77  25 BA DC 77 05 BD DC 77   飋趙.l趙%很w.杰w

00000410   AB 7A DA 77 42 78 DA 77  57 D7 DA 77 00 00 00 00   珃趙Bx趙W宗w....

00000420   70 D2 18 77 00 00 00 00  19 DC F0 77 05 4A F2 77   p?w.....莛w.J騱

00000430   A9 DE F0 77 5F 6E EF 77  56 F4 F0 77 9D 7F EF 77   ┺饂_n飛V麴w..飛

00000440   28 BE EF 77 25 4B F2 77  BB A5 EF 77 AC 7E EF 77   (撅w%K騱互飛瑍飛

00000450   95 56 F2 77 22 FB F1 77  C1 61 EF 77 B3 83 EF 77   昖騱"w羇飛硟飛

00000460   69 5A EF 77 8F 93 EF 77  FA 6B EF 77 B9 7D EF 77   iZ飛.擄w鷎飛箎飛

00000470   DB 5E EF 77 B7 D4 EF 77  05 3A F0 77 AE 3A F0 77   踍飛吩飛.:饂?饂

00000480   10 94 EF 77 70 5B EF 77  00 00 00 00 B8 97 80 7C   .旓wp[飛....笚€|


http://www.kler.cn/news/335814.html

相关文章:

  • Pytest 使用Pycharm右键直接运行测试脚本正常,控制台命令pytest运行收集不到用例无法正常测试 no tests ran in 0.01s
  • Python知识点:在Python环境中,如何使用Transformers进行预训练语言模型应用
  • 目标检测 DETR(2020)
  • 【Linux】信号知识三把斧——信号的产生、保存和处理
  • Vue - 路由用法
  • 基于 springboot vue中学生日常行为评分管理系统设计与实现
  • Python 进阶部分详细整理
  • 第五十九周周报 IAGNN
  • AI学习指南深度学习篇-Python实践
  • Linux之实战命令27:apropos应用实例(六十一)
  • SQL第12课——联结表
  • dockertop提示Failed to fetch extensions
  • SQL专项练习第四天
  • 数据结构 ——— 单链表oj题:链表中倒数第K个节点
  • idea2024设置中文
  • 什么是大语言模型的输入和输出限制
  • 昊辰创投注资众见科技,共筑智能眼镜未来:新一轮融资加速技术创新与市场扩张
  • SQL进阶技巧:统计各时段观看直播的人数
  • vim/vi常用命令大全
  • 为什么营业执照显示经营异常