文章目录
前言:
IAT 和 INT这两个表是 Windows PE 文件格式中用于管理动态链接(导入函数)的核心数据结构,它们协同工作,共同实现了延迟绑定机制,下面介绍下它们的工作原理。
一、IAT(Import Address Table)与INT(Import Name Table)表
1、核心作用
-
IAT(Import Address Table):存储外部符号的实际地址
- 作用:当程序加载到内存后,IAT 会存储所有外部函数 / 变量的实际内存地址(这些地址在编译时未知,需在程序加载或运行时由系统解析)。
- 本质:一个指针数组,每个元素指向一个外部符号(函数或变量)的实际内存位置。程序调用外部函数时,会通过访问 IAT 中的指针间接跳转,从而实现 “地址无关”(代码无需修改即可在任意内存位置运行)。
- 示例:
DynamicLinkDemo.exe的 IAT 是一个指针数组,专门存储utils.dll中add函数和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.dll、KERNEL32.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段的地址范围是0x00425000to0x0043132D,包含 INT 地址。 - 结论:INT 通常位于 .data 段或 .rdata 段(取决于编译器,用于存储只读的符号名称字符串)。
- INT 虚拟地址:
-
IAT(Import Address Table)的位置
- IAT 虚拟地址:
0x425130 - 段表中
.rdata段的地址范围是0x00425000to0x0043132D,包含 IAT 地址。 - 结论:IAT 通常位于 .data 段或 .rdata 段(取决于编译器)。
- IAT 虚拟地址:
通过前面的分析可以确定
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)标记add和global,编译器会在utils.dll中生成导出表,记录这两个符号在 DLL 中的偏移量(如add偏移0x1234,global偏移0x5678)。- 同时生成
utils.lib(导入库),包含utils.dll的文件名和导出符号信息。
DynamicLinkDemo.exe生成 IAT 和 INTDynamicLinkDemo.exe中通过__declspec(dllimport)声明add和global,编译器知道这两个符号来自外部 DLL。- 链接器(通过
utils.lib)在DynamicLinkDemo.exe中创建:- INT:存储符号名称
"add"和"global"(地址如0x140002000)。 - IAT:初始为占位符(如
0x00000000),等待后续填充实际地址(地址如0x140003000)。
- INT:存储符号名称
- 链接器修改
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的导出表,根据偏移量计算add和global的实际地址:add实际地址 =0x7ffabc00000 + 0x1234 = 0x7ffabc01234global实际地址 =0x7ffabc00000 + 0x5678 = 0x7ffabc05678
- 动态链接器找到
- 填充 IAT 表
- 动态链接器将计算出的实际地址写入
DynamicLinkDemo.exe的 IAT 表:- IAT 中
add对应的位置(0x140003000)被填充为0x7ffabc01234。 - IAT 中
global对应的位置(0x140003004)被填充为0x7ffabc05678。
- IAT 中
- 动态链接器将计算出的实际地址写入
阶段 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变量的指令访问的地址分别是0x425134、0x425130。IAT表的起始地址0x425130,可以看到前面两条指令直接访问的IAT表,通过IAT表间接访问其他模块的符号。
6、IAT 与 INT 的协作关系
| 环节 | INT 的作用(0x140002000) | IAT 的作用(0x140003000) |
|---|---|---|
| 编译时 | 记录需要导入的符号名称("add"、"global") | 作为占位符,等待填充实际地址 |
| 加载时 | 动态链接器通过名称查找 utils.dll 中的符号 | 被填充为 add 和 global 的实际内存地址 |
| 运行时 | 不参与,已完成使命 | 提供 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 是实现这一机制的关键结构。
Windows平台IAT与INT表&spm=1001.2101.3001.5002&articleId=153619779&d=1&t=3&u=75e1cac0560d4db1920b3372c4589e02)
1255

被折叠的 条评论
为什么被折叠?



