这里没有人回答你的问题。原因是 IMAGE_DIRECTORY_ENTRY_IAT 几乎没有记录。
我研究了他们使用这个目录的 ReactOS 的代码,以了解它是如何工作的。然后我编写了自己的代码来证实我的理论。这是我的结果。
我将以 Windows XP SP3 的 32 位 Calc.exe 为例进行说明。
当你列出 Calc.exe 的所有目录时,你会得到:
0 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_EXPORT
1 VirtAddr: 00012B80 Size: 0000008C IMAGE_DIRECTORY_ENTRY_IMPORT
2 VirtAddr: 00016000 Size: 00008A5C IMAGE_DIRECTORY_ENTRY_RESOURCE
3 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_EXCEPTION
4 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_SECURITY
5 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_BASERELOC
6 VirtAddr: 00001240 Size: 0000001C IMAGE_DIRECTORY_ENTRY_DEBUG
7 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_ARCHITECTURE
8 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_GLOBALPTR
9 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_TLS
10 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
11 VirtAddr: 00000260 Size: 00000080 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
12 VirtAddr: 00001000 Size: 00000228 IMAGE_DIRECTORY_ENTRY_IAT
13 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
14 VirtAddr: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
etc..
你看到有3个目录与import相关:
IMAGE_DIRECTORY_ENTRY_IAT
IMAGE_DIRECTORY_ENTRY_IMPORT
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
正如您在上面看到的,IAT 目录从偏移量 0x1000 开始,有 0x228 个字节。
它仅由指向导入的 DLL 的函数指针组成。
每个指针在 32 位进程中有 4 个字节,因此有 (0x228 = 552) / 4 = 138 个条目。我写了一个循环来列出它们:
Addr 01001000 --> function 77DA22EA
Addr 01001004 --> function 77DA23D7
Addr 01001008 --> function 77DA189A
Addr 0100100C --> function 00000000
Addr 01001010 --> function 77C41E2E
Addr 01001014 --> function 77C41D83
Addr 01001018 --> function 77C41EFF
Addr 0100101C --> function 00000000
Addr 01001020 --> function 77E59F93
Addr 01001024 --> function 77E605D8
Addr 01001028 --> function 77E5A5FD
Addr 0100102C --> function 77E7A9AD
Addr 01001030 --> function 77E536A3
Addr 01001034 --> function 77E53803
Addr 01001038 --> function 77E4E341
Addr 0100103C --> function 77E58D60
Addr 01001040 --> function 77E41BE6
Addr 01001044 --> function 77E52A2B
Addr 01001048 --> function 77E4177A
Addr 0100104C --> function 77E4C879
Addr 01001050 --> function 77E51B14
Addr 01001054 --> function 77E530C1
Addr 01001058 --> function 77E5AC37
Addr 0100105C --> function 77E54A69
etc...
这是所谓的导入地址表,模块中的代码在其中查找对外部函数的调用。如果您的代码调用 GetLastError(),它会在此处查看该函数在 Kernel32.dll 中的位置。
此列表中的 00000000 标志着另一个 DLL 正在跟随。仅此列表是没有用的,因为您不知道这些地址的含义。
重要提示:每个可执行文件都有一个 IAT。但并非每个可执行文件都使用 IMAGE_DIRECTORY_ENTRY_IAT 公开它。
如果可执行文件还有一个 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 目录,则 IAT 带有预加载的值。绑定目录由 IMAGE_BOUND_IMPORT_DESCRIPTOR 和 IMAGE_BOUND_FORWARDER_REF 条目链组成。这是内容:
Bound DLL: SHELL32.dll Timestamp: 3B842039
Bound DLL: msvcrt.dll Timestamp: 3B842039
Bound DLL: ADVAPI32.dll Timestamp: 3B842038
Bound DLL: KERNEL32.dll Timestamp: 3B842038
Bound DLL: GDI32.dll Timestamp: 3B842039
Bound DLL: USER32.dll Timestamp: 3B842038
Windows 加载程序检查编译 Calc.exe 时此目录中的时间戳是否与磁盘上 DLL 的时间戳相同。在这种特定情况下,不需要调用 GetProcAddress() 来解析导入,并且可以使用 IAT 中预加载的入口点。这是为了速度优化。
一些 DLL 将调用转发到另一个 DLL。例如 Kernel32.dll 有一些直接进入 NtDll.dll 的调用。在这种情况下,有一个转发器条目 (IMAGE_BOUND_FORWARDER_REF),它还允许检查转发的 DLL 的时间戳。
如果 DLL 已加载到另一个基地址,则必须将增量添加到 IAT 中的地址。但是不知道图片中原始基地址存放在哪里?当微软在 2004 年引入 ASLR(地址空间布局随机化)时,它成为所有 DLL 加载到随机基地址的规则。
在我的情况下,预加载的 IAT 完全没用,因为我在 XP 上的 Calc.exe 来自 2004 年,而导入的 DLL 来自 2008 年。所以必须重新解析所有条目。一旦您安装了更新系统上某些 DLL 的 Windows 更新,绑定的导入将不再起作用。
并且 IMAGE_DIRECTORY_ENTRY_IMPORT 指向 IAT 中完全相同的地址(通过 IMAGE_IMPORT_DESCRIPTOR->FirstThunk)。我打印了预加载的 IAT 和用 GetProcAddress() 的结果覆盖它们的值:
LoadLibrary(ADVAPI32.dll) --> HMODULE 77DA0000, Timestamp 4802BE8C
IAT 01001000 'RegOpenKeyExA' --> Value 77DA22EA updated 77DA7842
IAT 01001004 'RegQueryValueExA' --> Value 77DA23D7 updated 77DA7AAB
IAT 01001008 'RegCloseKey' --> Value 77DA189A updated 77DA6C17
LoadLibrary(GDI32.dll) --> HMODULE 77EF0000, Timestamp 4802BE8A
IAT 01001010 'SetBkColor' --> Value 77C41E2E updated 77EF5E29
IAT 01001014 'SetTextColor' --> Value 77C41D83 updated 77EF5D77
IAT 01001018 'SetBkMode' --> Value 77C41EFF updated 77EF5EDB
etc...
正如您所见,绑定导入在所有这些情况下都毫无用处。新值(右侧)不仅仅是与预加载值的恒定偏移量。它们指向不同的 DLL。由于 Advapi32.dll 与编译 Calc.exe 时不再相同,因此必须重新解析它们。
在 Windows 7 的 Calc.exe 中,我发现了相同的方案。但在 Windows 10 上就不同了。似乎他们在 DLL 中存储了偏移量而不是入口点地址?
总结:如果您编写自己的 DLL 加载程序,您可以完全忽略 IMAGE_DIRECTORY_ENTRY_IAT 和 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT。通过不调用 GetProcAddress() 进行的微小速度优化在当今快速 CPU 上可以忽略。