程序员的自我修养(7)Windows平台IAT与INT表


前言:

IAT 和 INT这两个表是 Windows PE 文件格式中用于管理动态链接(导入函数)的核心数据结构,它们协同工作,共同实现了延迟绑定机制,下面介绍下它们的工作原理。

一、IAT(Import Address Table)与INT(Import Name Table)表

1、核心作用

  • IAT(Import Address Table):存储外部符号的实际地址

    • 作用:当程序加载到内存后,IAT 会存储所有外部函数 / 变量的实际内存地址(这些地址在编译时未知,需在程序加载或运行时由系统解析)。
    • 本质:一个指针数组,每个元素指向一个外部符号(函数或变量)的实际内存位置。程序调用外部函数时,会通过访问 IAT 中的指针间接跳转,从而实现 “地址无关”(代码无需修改即可在任意内存位置运行)。
    • 示例DynamicLinkDemo.exe 的 IAT 是一个指针数组,专门存储 utils.dlladd 函数和 global 变量的实际内存地址。当 utils.dll 被加载到内存地址 0x7ffabc00000 时,IAT 中会存储:
      • add 函数的实际地址:0x7ffabc00000 + 函数在 utils.dll 中的偏移(如 0x7ffabc01234
      • global 变量的实际地址:0x7ffabc00000 + 变量在 utils.dll 中的偏移(如 0x7ffabc05678
  • INT(Import Name Table):存储外部符号的标识信息

    • 作用:记录程序依赖的外部符号的名称或序号(静态信息,编译链接时确定),作为动态链接器解析地址的查询依据。
    • 本质:一个字符串或序号的列表,每个条目与 IAT 中的条目一一对应,用于告诉系统需要找哪个函数 / 变量。
    • 示例DynamicLinkDemo.exe 的 INT 是一个字符串列表,记录需要从 utils.dll 导入的符号名称(即 “add” 和 “global”),作为系统解析地址的查询依据。

2、查看IAT与INT表

下面将通过 dumpbin 工具查看示例中 DynamicLinkDemo.exe 的 IAT(导入地址表)和 INT(导入名称表),dumpbin /imports xxx输出按 依赖的dll分组(如 utils.dllKERNEL32.dll 等),每组对应该 dll 的 IAT 和 INT 信息。下面以 utils.dll 分组为例,详细解释输出中每个字段的含义。

dumpbin /imports DynamicLinkDemo.exe
Dump of file DynamicLinkDemo.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    utils.dll
                425130 Import Address Table    
                430D88 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                    1 global
                    0 add
  • Import Address Table:IAT在内存中的虚拟地址。程序加载到内存后,这个地址指向一个指针数组,每个指针存储对应符号的实际内存地址(如 add 函数和 global 变量的地址,由动态链接器在加载时填充)。
  • Import Name Table:INT在内存中的虚拟地址。该地址指向一个列表,存储需要从 utils.dll 导入的符号名称(或序号),是动态链接器解析地址的查询依据
  • 符号列表(add、global)DynamicLinkDemo.exe 需要从 utils.dll 导入的符号名称(函数或变量)。

3、计算IAT与INT表所在的段

步骤 1:获取 IAT/INT 的虚拟地址

utils.dll分组为例,执行下面的命令找到utils.dll 对应的IAT和INT虚拟地址:

dumpbin /imports DynamicLinkDemo.exe
utils.dll
		425130 Import Address Table    
		430D88 Import Name Table

步骤 2:查看 PE 头的段表信息

执行以下命令查看 DynamicLinkDemo.exe 的段表(Section Header):

dumpbin /headers DynamicLinkDemo.exe
// 下面只列出部分 Section Header 信息
SECTION HEADER #2
  .rdata name
    C32E virtual size
   25000 virtual address (00425000 to 0043132D)
    C400 size of raw data
   23E00 file pointer to raw data (00023E00 to 000301FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         Read Only

步骤 3:定位 IAT/INT 所在的段

通过对比 IAT/INT 的虚拟地址与段表中的 virtual address 和地址范围确定其所属段。

  • INT(Import Name Table)的位置

    • INT 虚拟地址0x430D88
    • 段表中 .rdata 段的地址范围是 0x00425000 to 0x0043132D,包含 INT 地址。
    • 结论:INT 通常位于 .data 段或 .rdata 段(取决于编译器,用于存储只读的符号名称字符串)。
  • IAT(Import Address Table)的位置

    • IAT 虚拟地址0x425130
    • 段表中 .rdata 段的地址范围是 0x00425000 to 0x0043132D,包含 IAT 地址。
    • 结论:IAT 通常位于 .data 段或 .rdata 段(取决于编译器)。

通过前面的分析可以确定utils.dll对应的IAT和INT表在.rdata段。但是,这个地方会有一个疑问,IAT 表的内容装载时会修改,为什么还会放到.rdata段?。

为什么 IAT 可能位于 .rdata 段?

.rdata 段的核心属性是 “只读数据”(Read-Only Data),但在某些情况下,编译器 / 链接器会将 IAT 放入.rdata 段,主要原因是:

  • 优化段结构:对于简单程序(如示例中的 DynamicLinkDemo.exe),导入表规模较小,链接器可能将 IAT、INT 等导入相关数据合并到 .rdata 段,减少段的数量(每个段会占用额外的内存页,合并可提高内存利用率)。
  • 地址空间布局:在 64 位程序或启用某些优化选项时,链接器可能调整段的划分,将 IAT 与其他只读数据(如字符串、常量)放在一起,统一管理只读内存区域。

若 IAT 在 .rdata 段,如何解释其 “可修改性”?

IAT 需要在程序加载时被动态链接器修改(填充实际地址),但 .rdata 段的默认属性是 “只读”,这看似矛盾,实际通过以下机制解决:

  • 加载时调整权限:操作系统加载程序时,会临时将 .rdata 段中 IAT 所在的内存页权限改为可读可写PAGE_READWRITE),允许动态链接器填充地址。
  • 填充后恢复权限:地址填充完成后,系统会将该内存页权限改回只读(PAGE_READONLY),确保运行时 IAT 不被意外修改,提高安全性。

4、工作原理

基于前面的示例,介绍IAT与INT的工作原理, IAT 和 INT 的协作贯穿程序的编译、加载、运行三个阶段,具体流程如下:

阶段 1:编译链接阶段——生成 IAT 和 INT 结构
  • utils.dll 导出符号
    • utils.cpp 中通过 __declspec(dllexport) 标记 addglobal,编译器会在 utils.dll 中生成导出表,记录这两个符号在 DLL 中的偏移量(如 add 偏移 0x1234global 偏移 0x5678)。
    • 同时生成 utils.lib(导入库),包含 utils.dll 的文件名和导出符号信息。
  • DynamicLinkDemo.exe 生成 IAT 和 INT
    • DynamicLinkDemo.exe 中通过 __declspec(dllimport) 声明 addglobal,编译器知道这两个符号来自外部 DLL。
    • 链接器(通过 utils.lib)在 DynamicLinkDemo.exe 中创建:
      • INT:存储符号名称 "add""global"(地址如 0x140002000)。
      • IAT:初始为占位符(如 0x00000000),等待后续填充实际地址(地址如 0x140003000)。
    • 链接器修改 DynamicLinkDemo.exe 的代码:
      • add(5, 3) 改为间接调用 call dword ptr [0x140003000](即通过 IAT 地址调用)。
      • cout << global 改为间接访问 mov eax, dword ptr [0x140003004](即通过 IAT 地址访问变量)。
阶段 2:程序加载阶段——动态链接器填充 IAT

DynamicLinkDemo.exe 运行时,系统的动态链接器(ntdll.dll)会完成以下工作:

  • 加载 DynamicLinkDemo.exe 并解析依赖
    • DynamicLinkDemo.exe 被加载到内存(如基地址 0x140000000),动态链接器读取其导入表,发现依赖 utils.dll
  • 加载 utils.dll 并获取符号地址
    • 动态链接器找到 utils.dll 并加载到内存(如基地址 0x7ffabc00000)。
    • 读取 utils.dll 的导出表,根据偏移量计算 addglobal 的实际地址:
      • add 实际地址 = 0x7ffabc00000 + 0x1234 = 0x7ffabc01234
      • global 实际地址 = 0x7ffabc00000 + 0x5678 = 0x7ffabc05678
  • 填充 IAT 表
    • 动态链接器将计算出的实际地址写入 DynamicLinkDemo.exe 的 IAT 表:
      • IAT 中 add 对应的位置(0x140003000)被填充为 0x7ffabc01234
      • IAT 中 global 对应的位置(0x140003004)被填充为 0x7ffabc05678
阶段 3:运行阶段——通过 IAT 访问 utils.dll 符号

DynamicLinkDemo.exe 执行时,通过已填充的 IAT 表实现对 utils.dll 的访问:

  • 调用 add(5, 3)
    • 执行指令 call dword ptr [0x140003000],CPU 读取 IAT 地址 0x140003000 中的值 0x7ffabc01234,跳转到该地址执行 add 函数,返回结果 8
  • 访问 global 变量
    • 执行指令 mov eax, dword ptr [0x140003004],CPU 读取 IAT 地址 0x140003004 中的值 0x7ffabc05678,从该地址读取 global 的值 100 并输出。

5、用例解析

通过分析实际编译后的用例,看下IAT与INT是如何工作的,如下:

步骤一:查看DynamicLinkDemo.exe 导入的符号

dumpbin /imports DynamicLinkDemo.exe
Dump of file DynamicLinkDemo.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    utils.dll
                425130 Import Address Table
                430D88 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                    1 global
                    0 add

从上面的输出可以看到,与utils.dll相关的IAT/INT表对应的虚拟地址分别是0x425130/0x430D88

步骤二:反汇编 DynamicLinkDemo.exe

dumpbin /DISASM /OUT:1.txt /SECTION:.text DynamicLinkDemo.exe

在这里插入图片描述

调用add函数及访问global变量的指令访问的地址分别是0x4251340x425130。IAT表的起始地址0x425130,可以看到前面两条指令直接访问的IAT表,通过IAT表间接访问其他模块的符号。

6、IAT 与 INT 的协作关系

环节INT 的作用(0x140002000IAT 的作用(0x140003000
编译时记录需要导入的符号名称("add""global"作为占位符,等待填充实际地址
加载时动态链接器通过名称查找 utils.dll 中的符号被填充为 addglobal 的实际内存地址
运行时不参与,已完成使命提供 main.exe 访问 utils.dll 的实际地址

7、核心价值

通过 IAT 和 INT 的配合,DynamicLinkDemo.exe 无需知道 utils.dll 的加载地址,即可正确调用其函数和访问变量,实现了:

  • 地址无关性utils.dll 可加载到任意内存地址,DynamicLinkDemo.exe 代码无需修改。
  • 模块化utils.dll 可独立更新,只要符号名称不变,DynamicLinkDemo.exe 无需重新编译。
  • 内存高效utils.dll 的代码段可被多个进程共享(如多个程序同时使用 utils.dll)。

这正是 Windows 动态链接机制的核心优势,而 IAT 和 INT 是实现这一机制的关键结构。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值