导入表解析,IAT表解析【滴水逆向三期53笔记】

我们再上一章节简要介绍了IAT表,我们知道如果程序调用dll中的函数时,必须通过IAT表来找到函数,我们基本了解了IAT表之后,我们今天来讲解一下导入表,通过本章节的学习,我们可以了解导入表,也能对IAT表有一个更深入的了解。

一.导入表是什么?

我们可以将导入表比作餐厅的菜单,而dll中函数的地址,就可以被比作为菜单上的菜品。
也就是说,导入表记录了dll中的函数名称和地址,exe在调用dll中的函数的时候,就可以通过导入表来找到函数。这里注意区分导入表和IAT表,IAT表是exe在调用dll中的函数时,通过IAT表中记录的地址来找到函数,而导入表记录了dll的名称,还有dll中函数的地址和名称(也有函数的导出序号)。
那么我们来看看导入表的结构:

typedef struct_IMAGE_IMPORRT_DESCRIPTOR{
	union{
	DWORD Characteristics;
	DWORD OriginaFirstThunk;//指向IMAGE_THUNK_DATA(输入名称表)结构数组的RVA
	}
	DWORD TimeDateStamp;//时间戳(当可执行文件不与被输入的DLL进行绑定时,此字段为0)
	DWORD ForwarderChain;//第一个被转向的API索引
	DWORD Name;//指向被输入的DLL的名称字符串的第一个字符RVA
	DWORD FirstThunk;//指向输入地址表(IAT表)的RVA,IAT是一个IMAGE_THUNK_DATA结构的数组
}IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPOTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

解析:
该导入表结构中:
OriginaFIrstThunk中存储的是一个地址,指向IMAGE_THUNK_DATA(导入名称表)该表中记录了导入函数的名称,也被叫做INT表,该表的结束标志和很多表一样:最后一张表的数据全为0。
Name中存储的是指向该dll文件名的RVA
FirstThunk中存储的是该文件的IAT(import address table)表的RVA,因为导入表和IAT表都和调用的dll文件中的函数有密切关系,所以他俩总是在一起,该表的结束标志和很多表一样:最后一张表的数据全为0。
找到IAT表有两种方式:一种是通过数据目录的第13个项目的VirtualAddress找到,另一种方式是通过导入表的FirstTrunk找到
我们知道一个程序可能调用了很多dll文件,所以导入表也不只一张,VirtualAddress(数据目录中的第二个元素为导入表) 指向多个一样的导入表结构。

二.导入表详解

我们想要了解导入表,必须通过程序运行前和程序运行中两种状态来了解,也就是程序未被加载前和程序和调用的dll都被加载到独立4GB空间两种状态下:
我们给出图来帮助我们理解:
程序未被加载前:
程序运行前
我们可以很清楚地观察到,在程序未被加载前,INT(导入名称表)和IAT(导入地址表)中存储的地址都指向同一个地方:该处存储了函数名称字符串或函数导出序号,我们在上一章中也在程序二进制文件中观察到存储了MessageBoxA(上一章节存储了MessageBoxA是因为我写的程序调用了MessageBox函数),那么我们如何判断INT(导入名称表)中的元素到底是函数导出名称还是导出序号呢?
这里给出判断INT表中某一项方法:
在INT(导入名称)表中,若该项最高位为1,那么去除最高位,剩下的就是该函数的导出序号
若该项最高位为0,那么这一项就是RVA,指向IMAGE_TRUNK_BY_NAME。

typedef struct _IMAGE_THUNK_DATA32 {						
    union {						
        PBYTE  ForwarderString;						
        PDWORD Function;						
        DWORD Ordinal;						 //序号
        PIMAGE_IMPORT_BY_NAME  AddressOfData;//指向IMAGE_IMPORT_BY_NAME
    } u1;						
} IMAGE_THUNK_DATA32;						
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;						
						
						
typedef struct _IMAGE_IMPORT_BY_NAME {						
    WORD    Hint;						//可能为空,编译器决定 如果不为空 是函数在导出表中的索引
    BYTE    Name[1];				    //函数名称,以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;		

IMAGE_IMPORT_BY_NAME 中的 Name 并不只有 1 个字节,而是一直遍历到元素为 ‘\0’ 为止,OriginalFirstThunk 和 FirstThunk 的遍历 具体详见下图(这俩的遍历都一样的,其实都是遍历的 IMAGE_THUNK_DATA)。
IMAGE_TRUNK_DATA

程序和调用的dll被加载后:
我们来思考一个问题:既然INT表和IAT表都指向同一个地方,那么我们为什么需要两张表呢?
我们再上一章简单解析了IAT表,我们知道程序在调用dll中的函数时,要通过IAT表来间接寻址,如果只用一张表的话,在程序被加载后,IAT表中的项目被改为函数地址,那么程序不就不知道函数名称了吗?这就是为什么要有INT表和IAT表的原因。
程序加载后
我们可以观察到:在程序和调用的dll被加载后,IAT表中的项目不再指向函数名称,通过上一章节的简析,我们知道它指向了函数被加载后的真正地址,当程序调用函数时,通过IAT表就可以找到函数真正的位置了。
IAT 表在文件加载完成后系统会调用 GetProcAddress() 函数,就是做的我们前面写过的根据导出表函数序号或函数名字找到函数地址的功能。系统会循环 INT 表,根据表内的名字或序号调用 GetProcAddr() 得到地址,依次添加到 IAT 表中。
注意 IMAGE_IMPORT_BY_NAME 中的 Hint 不是导出序号,而是当前这个函数在导出表函数地址表中的索引。但基本没用,所以不一定是准确的,可以全部为0。而Name并不是一字节,而是以’\0’结尾的不定长字符串。

三.打印一个程序的导入表的信息(作业思路)

这里先给出基本思路(这里是打印程序未被加载前的导入表):
1.通过数据目录的第二项的VirtualAddress转换为FOA在文件中找到导入表
2.输出dll名(导入表中Name字段转换为FOA在文件中找到)
3.遍历OriginalFirstTrunk(OriginalFirstTrunk字段转换为FOA在文件中找到),打印导出序号或导出名称(需要转换为FOA在文件中找到)
4.遍历FirstTrunk(导入表中FirstTrunk字段转换为FOA在文件中找到)
5.找到下一张导入表,循环2,3,4,直到某张表的导入表内数据全为零结束

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
第1讲:2015-01-12(进制01) 第2讲:2015-01-13(进制02) 第3讲:2015-01-14(数据宽度-逻辑运算03) 第4讲:2015-01-15(通用寄存器-内存读写04) 第5讲:2015-01-16(内存寻址-堆栈05) 第6讲:2015-01-19(EFLAGS寄存器06) 第7讲:2015-01-20(JCC) 第8讲:2015-01-21(堆栈图) 第8讲:2015-01-21(宝马问题) 第9讲:2015-01-22(堆栈图2) 第10讲:2015-01-23(C语言01_后半段) 第10讲:2015-01-23(C语言完整版) 第11讲:2015-01-26(C语言02_数据类型) 第12讲:2015-01-27(C语言03_数据类型_IF语句) 第13讲:2015-01-28(C语言04_IF语句逆向分析上) 第14讲:2015-01-28(C语言04_IF语句逆向分析下) 第15讲:2015-01-29(C语言04_正向基础) 第16讲:2015-01-30(C语言05_循环语句) 第17讲:2015-02-02(C语言06_参数_返回值_局部变量_数组反汇编) 第18讲:2015-02-02(2015-01-30课后练习) 第19讲:2015-02-03(C语言07_多维数组) 第20讲:2015-02-03(2015-02-02课后练习) 第21讲:2015-02-04(C语言08_结构体) 第22讲:2015-02-05(C语言09_字节对齐_结构体数组) 第23讲:2015-02-06(C语言10_Switch语句反汇编) 第24讲:2015-02-26(C语言11_指针1) 第25讲:2015-02-27(C语言11_指针2) 第26讲:2015-02-28(C语言11_指针3) 第27讲:2015-02-28(C语言11_指针4) 第28讲:2015-03-02(C语言11_指针5) 第29讲:2015-03-03(C语言11_指针6) 第30讲:2015-03-04(C语言11_指针7) 第31讲:2015-03-06(C语言11_指针8) 第32讲:2015-03-09(位运算) 第33讲:2015-03-10(内存分配_文件读写) 第34讲:2015-03-11(PE头解析_手动) 第35讲:2015-03-12(PE头字段说明) 第36讲:2015-03-13(PE节) 第37讲:2015-03-16(FileBuffer转ImageBuffer) 第38讲:2015-03-17(代码节空白区添加代码) 第39讲:2015-03-18(任意节空白区添加代码) 第40讲:2015-03-19(新增节添加代码) 第41讲:2015-03-20(扩大节-合并节-数据目录) 第42讲:2015-03-23(静态连接库-动态链接库) 第43讲:2015-03-24(导出) 第44讲:2015-03-25(重定位) 第45讲:2015-03-26(移动导出-重定位) 第46讲:2015-03-27(IAT) 第47讲:2015-03-27(导入) 第48讲:2015-03-30(绑定导入) 第49讲:2015-03-31(导入注入) 第50讲:2015-04-01(C++ this指针 类 上) 第51讲:2015-04-01(C++ this指针 类 下) 第52讲:2015-04-02(C++ 构造-析构函数 继承) 第53讲:2015-04-03(C++ 权限控制) 第54讲:2015-04-07(C++ 虚函数) 第55讲:2015-04-08(C++ 动态绑定-多态-上) 第56讲:2015-04-08(C++ 动态绑定-多态-下) 第57讲:2015-04-09(C++ 模版) 第58讲:2015-04-10(C++ 引用-友元-运算符重载) 第59讲:2015-04-13(C++ new-delete-Vector) 第60讲:2015-04-14(C++Vector实现) 第61讲:2015-04-15(C++) 第62讲:2015-04-16(C++实现) 第63讲:2015-04-16(C++二叉树) 第64讲:2015-04-17(C++二叉树实现) 第65讲:2015-04-20(Win32 宽字符) 第66讲:2015-04-21(Win32 事件-消息-消息处理函数) 第67讲:2015-04-22(Win32 ESP寻址-定位回调函数-条件断点) 第68讲:2015-04-23(Win32 子窗口-消息处理函数定位) 第69讲:2015-04-24(Win32 资源文件-消息断点) 第70讲:2015-04-27(Win32 提取图标-修改标题) 第71讲:2015-04-28(Win32 通用控件-VM_NOTIFY) 第72讲:2015-04-29(Win32 PE查看器-项目要求) 项目一:PE查看器 开发周期(5天) 需求文档 第73讲:2015-05-07(Win32 创建线程) 第74讲:2015-05-08(Win32 线程控制_CONTEXT) 第75讲:2015-05-11(Win32 临界区) 第76讲:2015-05-12(Win32 互斥体) 第77讲:2015-05-13(Win32 事件) 第78讲:2015-05-14(Win32 信号量) 第79讲:2015-05-15(Win32 线程同步与线程互斥) 第80讲:2015-05-18(Win32 进程创建_句柄) 第81讲:2015-05-20(Win32 以挂起形式创建进程) 第82讲:2015-05-21(Win32 加密壳_项目说明) 项目二:加密壳 开发周期(5天) 需求文档 第83讲:2015-05-28(Win32 枚举窗口_鼠标键盘事件) 第84讲:2015-05-29(Win32 CE练习) 第85讲:2015-06-01(Win32 OD练习) 第86讲:2015-06-03(Win32 ShellCode_远程线程注入) 第87讲:2015-06-04(Win32 加载EXE_模块隐藏) 第88讲:2015-06-09(Win32 IAT_HOOK) 第89讲:2015-06-10(Win32 InlineHook) 第90讲:2015-06-11(Win32 进程通信) 第91讲:2015-06-11(Win32 进程监控_项目说明) 项目三:进程监控 开发周期(5天) 需求文档 第92讲:2015-06-15(硬编码_01) 第93讲:2015-06-16(硬编码_02) 第94讲:2015-06-17(硬编码_03) 第95讲:2015-06-18(硬编码_04) 第96讲:2015-06-19(硬编码_05)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shad0w-2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值