PE中的import table/IAT 分析

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

本文将通过一个实例说明PE结构中的import table及import address table(IAT).

 

在data directory中有两项:IMAGE_DIRECTORY_ENTRY_IMPORT(1)和IMAGE_DIRECTORY_ENTRY_IAT(12),IMAGE_DIRECTORY_ENTRY_IMPORT指向了该PE文件中所有的输入信息,而IMAGE_DIRECTORY_ENTRY_IAT指向了该PE文件中的导入地址表。现在不必关心这两个表的关系,在之后的分析中将渐渐明朗。

 

如果用C/C++程序来分析import table或IAT的结构不是很合适,因为CRT会插入太多的代码到最终的PE文件中,所以我们选择用汇编代码作为例子,好处就是所有的导入函数都是自己的代码中确确实实使用到的,一目了然。

 

我们使用现成的一个例子的分析:(摘自Win32ASM Programming 3rd Edition)

 

通过stud_pe(一个很好的学习PE的工具)我们清楚的看到在这个例子中我们使用了如下导入函数:

user32.dll:

DispatchMessageA

DrawTextA

EndPaint

GetClientRect

GetMessageA

DestroyWindow

PostQuitMessage

RegisterClassExA

ShowWindow

TranslateMessage

UpdateWindow

DefWindowProcA

CreateWindowExA

LoadCursorA

BeginPaint

 

kernel32.dll:

GetModuleHandleA

ExitProcess

RtlZeroMemory

 

现在我们从Data Directory开始分析。(以下截图均来自Stud_PE)

 

IMAGE_DIRECTORY_ENTRY_IMPORT:

 

IMAGE_DIRECTORY_ENTRY_IAT:

 

在继续之前我们需要了解两个东西:

1. Data Directory的每一项前四字节表示虚拟地址,后四字节表示大小,本文中不关心。不熟悉的读书可以查阅PE结构获取更多信息。

2. 这里的地址是虚拟地址,也就是载入内存以后的地址。我们如何在文件中也就是物理硬盘上找到对应的内容呢?其实通过Optional Header中的FileAlignment和SectionAlignment可以实现虚拟地址和物理地址的转化。有兴趣的读书可以查阅PE结构获取更多信息,本文将使用stud_pe提供的转化工具完成相应工作。

 

接下来开始我们逐步分析:

Import table的地址:00002090        通过stud_pe找到物理地址:00000690

IAT的地址:00002000                     通过stud_pe找到物理地址:00000600

 

那么00000690和00000600分别在文件的哪个section呢?通过查看该PE的section table我们发现所有的相关信息都存放在.rdata段了。说到这里简单提一下,data directory中指向的内容都是存在各个section中的,至于存在哪个section主要看section的读写属性,当然也可以另起一个section单独存放某个data entry的内容,比如.reloc就作为一个单独的段存放了所有的重定位信息。

 

为了方便,我们把整个.rdata段拿出来分析:

 

 

在继续之前,我们先大概了解一下import table的知识:

这个图再清晰不过了,不过要彻底搞明白还是要结合例子分析:

输入表是一个 IMAGE_IMPORT_DESCRIPTOR数据结构的数组,这个结构定义如下:

这个结构总共20个字节,最重要的有三个:OriginalFirstThunk、Name、FirstThunk。一个IMAGE_IMPORT_DESCRIPTOR包含了从一个dll中导入的信息,在这个例子中我们有两个dll:user32.dll和kernel32.dll,最后用一个全0的IMAGE_IMPORT_DESCRIPTOR表示结束。我们只提取我们关心的东西:

OriginalFirstThunk1: 000020DC  -->  000006DC(物理地址)

Name1:                   0000220E  -->  0000080E

FirstThunk1:            00002010  -->  00000610

 

OriginalFirstThunk2: 000020CC  -->  000006CC(物理地址)

Name2:                   0000224C  -->  0000084C

FirstThunk2:            00002000  -->  00000600

 

我们先从简单的开始分析:Name

(前面的地址如00000200不是真实的物理地址,在stud_pe中此地址表示到.rdata的起始地址的偏移,也就是00000600+00000200,因此我们看到user32.dll的物理地址其实是0000080E,也就是Name1的值。kernel32.dll同理)

 

再继续往下之前,我们先要回到之前PE的import table结构中另外两个结构的定义:

我们看到IMAGE_THUNK_DATA是一个4字节的联合,在本例中其实就是一个IMAGE_IMPORT_BY_NAME的虚拟地址。如果导入函数不是用函数名导入的而是用序号导入的,情况就不一样了,具体是按名字导入还是按序号导入是看IMAGE_THUNK_DATA字段的最高位是否为1决定的。具体可查看PE文件结构。

 

接下来我们结合例子来看OriginalFirstThunk和FirstThunk两个字段表示的意义(以kernel32.dll为例):

OriginalFirstThunk: 000020CC  -->  000006CC 

                            内容如下:(28 22 00 00     1A 22 00 00     3C 22 00 00     00 00 00 00)

FirstThunk:  00002000  -->  00000600

                            内容如下:(28 22 00 00     1A 22 00 00     3C 22 00 00     00 00 00 00)

 

我们发现了两个地方:

1. 000006CC和00000600指向的内容是一样的,但是确存放了两个拷贝。

2. 00000600好熟悉啊。。。回头看看原来是data directory中IMAGE_DIRECTORY_ENTRY_IAT的地址!

 

OriginalFirstThunk和FirstThunk其实是指向了两个不同的表INT和IAT,但是这两个表其实内容是相同的。INT/IAT中的内容在本例中都是虚拟地址,至于该虚拟地址中的数据究竟表示了什么含义,就涉及IMAGE_IMPORT_BY_NAME这个结构体了。我们就取一个例子分析:1A 22 00 00  --> 0000081A(9B 00 45 78 69 74 50 72 6F 63 65 73 73 00)

Hint: 00 9B,这个是用来优化的,具体不展开,在本例中直接可以忽略。

Name: 45 78 69 74 50 72 6F 63 65 73 73 00 == ExitProcess

 

至于为什么需要用两个相同的表INT/IAT,原因比较复杂,简单的说是为了加快加载速度,能够进行链接时绑定。具体就不展开了,因为跟本文主题没有密切的联系,有兴趣的读者自己查阅相关资料。

 

对import table的分析基本全部完成了,现在我们再回头来看一下IAT的内容:

除了之前kernel32对应的FirstThunk,还有user32.dll对应的FirstThunk(00000610),也就是说IAT其实包含了该PE文件中所有导入的函数,以00 00 00 00作为一个dll的结尾。IAT对应的data directory的size告诉我们IAT的大小,在本例中50h表示这个IAT总共包含了80字节(20项)。

 

 

说到这里其实关于import table跟IAT相关的分析已经结束了,但是既然话题是import相关的,那我们也来分析一下在代码中调用一个输入函数是如何处理的:

比如:invoke ExitProcess,NULL

 

我们在OD中可以看到对应的机器/汇编指令:E8 5B 00 00 00/CALL 004011CA

004011CA这个地址是做什么用的呢?在OD中我们看到004011CA地址对应的指令如下:

JMP DWORD PTR DS:[402004]

那么402004是什么呢???噢!其实就是文件中对应的物理地址604!402004-400000(exe加载地址)== 2004 -->  604,也就是IAT表中的第二项,那么是1A 22 00 00么?不是!为什么叫IAT(image address table)?因为这个表的内容在加载时会用实在的导入函数地址填充。所以这个时候其实是真正的ExitProcess的地址了,我们可以用OD看到,运行时这个地址是76 0E 73 4E!

 

其实这个就是调用外部函数通常的做法,先用CALL指令转向一个跳转表:

而这个跳转表其实就是用JMP指令跳转到了IAT中的每一项。但是这个跳转表不是必需的,又是也可以直接用CALL DWORD PTR DS:[XXX]这样的语句实现外部函数的调用,XXX正是IAT中的某一项的地址。具体什么时候系统会创建这个跳转表是个疑问,暂时没有找到规律。

《Windows PE》4.1导入表 IMAGE_IMPORT_BY_NAME结构体用于描述PE文件中的导入地址表(Import Address TableIAT)或原始未绑定导入地址表(Original Unbound Import Address Table)中,通过名称进行导入的函数的信息。然后根据导入表FOA地址,在PE文件中找到导入表描述符的起始地址,循环遍历获取导入表描述符的OriginalFirstThunk字段中保存的RVA地址,将此RVA地址转换为FOA地址,即INT表IMAGE_THUNK_DATA的起始地址。 阅读详情

相关推荐

PE之代码解析导入表

前几个基本PE文件流程掌握以后,咱们使用之前的流程来进行导入表常见的信息获取。咱们接着往下走以pImportDescriptor->Name不为空为条件,逐个遍历每一个导入描述符:先把当前导入描述符里Name字段的虚拟地址(RVA),用RvaToOffset函数转成文件偏移,再加上buf的起始位置,就能拿到对应的DLL名字(szDllName),接着打印出这个DLL的名称、时间戳、转发链、Name字段的虚拟地址、FirstThunk值,还有OriginalFirstThunk(OFTs)的数值。

Strategic__qq3610256651的博客 547

PE文件结构详解(四)PE导入表

也许大家注意到过,在IMAGE_DATA_DIRECTORY中,有几项的名字都和导入表有关系,其中包括:IMAGE_DIRECTORY_ENTRY_IMPORTIMAGE_DIRECTORY_ENTRY_BOUND_IMPORTIMAGE_DIRECTORY_ENTRY_IATIMAGE_DIRECTORY_ENTRY_DELAY_IMPORT这几个导入都是用来干什么的,他们之间又是什么关系呢?听我慢慢道来。

evil.eagle的专栏 3万+

脱壳进阶篇——IAT修复与解密

IAT修复 这种jmp明显有问题,下断点跟进去看一下。 复制IAT中的第一条和最后一条,指定范围去获取输入表,准确完美。 数据窗口跟随内存地址,这种77和76开头的典型就是API函数地址,使用长型地址指针即可观看系统API函数名字。 ...

摔不死的笨鸟的博客 3410

4.ring0-遍历IAT(特例NTOS)

原NTOS的IAT只能通过IMAGE_DIRECTORY_ENTRY_IAT(12)来获得,因为NTOS加载完后,INIT方式加载,所以IMAGE_DIRECTORY_ENTRY_IMPORT对应的区域被释放了! 坑爹啊, 可以用windbg很直观的看到: X86: x64: 其他的IAT遍历代码如下: NTSTATUS EnumIATTable(ULO

hgy413的专栏 1802

PE文件结构—Data Diretoris,Sections,Section Header

今天将具体学习一下数据目录表和区域表的相关内容,这两部分中有很多内容在文件被执行时起关键作用。下面我们将具体分析一下数据目录表和区域表。

QXinHan的博客 1093

PE文件格式--------------导入表和IAT

pe文件导入表 1)提取导入表:在数据目录的中,索引为1的位置; 导入表起始RVA地址:IMAGE_NT_HEADER.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress 导入表大小:IMAGE_NT_HEADER.OptionalHeader.DataDirectory[IMAGE_DIRECT...

weixin_30692143的博客 407

PE文件导入表解析(x64)

本文详细解析了PE文件加载过程中导入表处理的流程。首先,加载器通过PE头定位导入数据目录,获取导入表RVA和大小。随后遍历导入描述符数组,对每个DLL执行加载操作:1)读取DLL名称并加载到内存;2)解析导入函数,通过OriginalFirstThunk获取函数名或序号;3)计算函数绝对地址并写入IAT表。文中以VCRUNTIME140.dll为例,展示了从INT获取函数名到最终填充IAT地址的完整过程,并验证了内存中IAT表项与符号地址的一致性。整个流程确保了程序能正确调用外部DLL函数。

本科网安在读 672

PE文件中的IAT和IET

PE文件中的IAT和IET IAT IAT中文名叫导入地址表 在IAT中存放的是导入的函数的RVA数组,每个元素对应一个函数的地址(RVA) 通过这个RVA即可找到导入函数的位置 所以说我们必须先知道IAT在哪 1.找到可选头结构体的最后一个成员 可选头结构体的最后一个成员是DataDirectory 它是一个结构体数组 typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE M

learn112的博客 1283

IAT 三连之什么是 IAT

IAT 的全称是 ImportAddress Table。在可执行文件中使用其他 DLL 可执行文件的代码或数据,称为导入或者输入,当 PE 文件载入内存时,windows 加载器会定位所有导入的函数或数据将定位到的内容填写至可执行文件的某个位置供其使用,而这个操作是需要借助导入表来完成的。 导入表中存放了程序所有使用的 DLL 模块名称及导入的函数名称或函数序号。 本文所用文件及源代码下载地址: https://pan.baidu.com/s/1o9360AI 学习 IAT 有什么用? 在脱

hl1293348082的专栏 913

IAT结构分析

IAT结构分析 IAT介绍 解决兼容问题(不同机器的dll版本不同,地址自然不同),操作系统就必须提供一些措施来确保可以在其他版本的Windows操作系统,以及DLL版本下也能正常运行。 这时IAT(Import Address Table:输入函数地址表)就应运而生了。 IAT定位 手工寻找 利用PEview找到程序的NT头的可选头内有个import table此处记录了其RVA。 根据结构体...

playmaker的博客 808

解析导入表和IAT

一、导入表的结构 导入表的结构看起来复杂,其实只是套娃,不要被它吓到了。 导入表的定义如下: typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbo

hambaga的博客 2028

IAT表的详解(转)

IAT表详解 IAT的全称是Import Address TableIAT表是执行程序或者dll为了实现动态加载和重定位函数地址,用到的一个导入函数地址表。这里面记录了每个导入函数的名字和所在的dll名称,在pe加载的时候系统会加载这些dll到用户的地址空间然后把函数地址覆盖这个表里的函数地址,然后重构所有用到这个表的代码,让其调用直接指向实际函数地址(PE是否覆盖不确定,驱动会这么做),...

1593

PE结构:导入表中的双桥结构

导入表的知识曾经整理过,网址如下;现再对其中的双桥结构进行整理。 《PE文件:导入表》:https://blog.csdn.net/weixin_43742894/article/details/105155489 导入表结构: struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; //指向INT 桥1 } D

weixin_43742894的博客 1414

PE结构->【输入表】Import【下】

输入表结构回顾一下,在 PE文件头的 IMAGE_OPTIONAL_HEADER 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表的。而输入表是以一个 IMAGE_IMPORT_DESCRIPTOR(简称IID) 的数组开始。 每个被 PE文件链接进来的 DLL文件都分别对应一个 IID数组结构。在这个 IID数组中,并没有指出有多少个项(就是没有明确指明有多少个链接

u011513939的博客 451

《逆向工程核心原理》读书笔记——第13章 PE文件格式

第13章 PE文件格式13.1 介绍13.2、PE文件格式13.2.1基本结构13.2.2 VA&RVA13.3 PE头13.3.1 DOS头13.3.2 DOS存根13.3.3 NT头13.3.4 NT头中的文件头1.Machine2.NumberOfSections3.SizeOfOptionalHeader4.Characteristics13.3.5 NT头中的可选头1.Magic2.AddressOfEntryPoint3.ImageBase4.SectionAlignment, File

Onlyone_1314的博客 2727

PE结构:导入/导出表

PE结构:导入/导出表 导入导出表是PE文件中重要的两张资源表,这两张表会在PE文件执行是被加载进入内存,RVA在映像可选头部的数据目录表中可以查询。 一般会被放在.rdata(资源数据段)中 EXPORT 导出表 DLL链接库文件中的函数有两种导出方式:按名字与按序号 导出表格式 EXPORT { ​ DWORD Export flags; //保留,必须为0 ​ DWORD Time/Data_Stamp //导出表创建的时间戳 ​ WORD MajorVersion //主版本号,可

Arshion的博客 1138

PE import/export address table

#include <stdlib.h> #include <stdio.h> #include <windows.h> // http://hatriot.github.io/blog/2017/09/19/abusing-delay-load-dll/ // https://blog.csdn.net/adam001521/arti...

ai82036620的博客 389

第四讲,数据目录表之导入表,以及IAT

    一丶IAT(地址表) 首先我们思考一个问题,程序加载的时候会调用API,比如我们以前写的标准PE 那么他到底是怎么去调用的? 它会Call 下边的Jmp位置 而Jmp位置则是对一个全局变量取内容. 看下全局变量内容是什么. 我们跟过去看下 75 98 FD AE 可以看出,这个位置保存了一张表格,这张表格保存的是MessageBoxA的函数地址. ...

weixin_30924087的博客 316
上一篇: OBJ分析代码
下一篇: delay load深入分析
panda1987
博客等级 码龄18年 37粉丝 25原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值