首页
社区
课程
招聘
[原创]PE文件格式学习(3)——IAT
发表于: 2020-12-12 18:45 5946

[原创]PE文件格式学习(3)——IAT

2020-12-12 18:45
5946

16位的DOS时代不存在DLL(Dynamic Linked Library)这一概念,只有“库”(Library)一说。比如在C语言中使用printf()函数时,编译器会先从C库中读取相应函数的二进制代码,然后插入到应用程序。Windows OS支持多任务,若仍采用这种包含库的方式,会非常没有效率。于是Windows OS的设计者们根据需要引入了DLL这一概念。
加载DLL的方式有两种:

上面提到了DLL,在各种不同版本的Windows系统中,DLL的版本各不相同,同一函数在不同版本DLL中的位置也可能不同;另外一个原因在于DLL的重定位,DLL文件的ImageBase值一般为10000000,但当两个DLL尝试装载到同一个地址时会发生冲突,因此有一个DLL得寻找另一个地址装载,这就是所谓的DLL重定位。

因此当我们要调用某个DLL中的函数时,需要借助IAT(Import Address Table,导入地址表)作为中间桥梁。PE文件在装载时由PE装载器将导入函数的真实地址写入到IAT时,函数调用时则需要从IAT中获取函数的真实地址。

IAT提供的机制与DLL的隐式链接有关。

下面我在 VS2019 里编译如下代码:

修改几个编译选项:



IDA打开编译好的文件,找到调用 strcmp 函数的位置:


FF 15 是 call 的 opcode ,C8 20 40 00 是小端表示的地址,即0x004020C8,该 call 指令并没有直接跳转到0x004020C8这个地址,而是从.idata段读取了 strcmp 函数的真实地址后再进行跳转。.idata段内的数据被称为IAT(Import Address Table,导入地址表),这个过程被称为从IAT获取函数的真实地址。

然而.idata段内的数据并没有初始化,也就是说,strcmp 函数的真实地址是在运行时才能得到的,准确来说是在PE文件被加载到内存时才能得到,下面我们就来研究IAT是怎样被初始化的。

IMAGE_IMPORT_DESCRIPTOR结构体中记录着PE文件要导入哪些库文件。

其结构如图所示:

OrignalFirstThunk 存储的INT(Import Name Table)的RVA,INT中各元素的值为IMAGE_IMPORT_BY_NAME结构体指针。

我们在 010 editor 中找到 strcmp 函数所在DLL对应的 IMAGE_IMPORT_DESCRIPTOR:

看到 IAT 的 RVA 为 20C8h,INT 的 RVA 为 2630h。
在IDA中查看INT的数据:

再来看看PE装载器是如何把导入函数的真实地址写入IAT中的:

GetProcAddress 函数的作用是获取导入函数的虚拟地址:

随便找个地方下断点:

动态调试查看 ucrtbase.dll 的基址以及 strcmp 函数的地址:

真实的 strcmp 函数:

《逆向工程核心原理》李承远

 
 
#include <cstdio>
#include <cstring>
 
int main(){
    char input[100] = { 0 };
    scanf_s("%s", input);
    if (!strcmp(input, "password")) {
        printf("Right.\n");
    }
}
#include <cstdio>
#include <cstring>
 
int main(){
    char input[100] = { 0 };
    scanf_s("%s", input);
    if (!strcmp(input, "password")) {
        printf("Right.\n");
    }
}
 
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)
 
    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
最后于 2020-12-13 11:07 被34r7hm4n编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (6)
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
佬,“再来看看PE装载器是如何把导入函数的真实地址写入IAT中的:”这句话底下那个图是什么书?谢谢了
2020-12-21 11:24
0
雪    币: 14303
活跃值: (10769)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
3
wx_加速师 佬,“再来看看PE装载器是如何把导入函数的真实地址写入IAT中的:”这句话底下那个图是什么书?谢谢了
李承远的《逆向工程核心原理》,是本好书
2020-12-21 14:59
0
雪    币: 57
活跃值: (196)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习了~
2020-12-29 13:42
1
雪    币: 12159
活跃值: (5704)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
帖子截图的书上内容明显不严谨,INT只有特定的编译器才会生成比如vs的编译器,很多编译器只生成IAT
2020-12-29 14:34
0
雪    币: 14303
活跃值: (10769)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
6
hhkqqs 帖子截图的书上内容明显不严谨,INT只有特定的编译器才会生成比如vs的编译器,很多编译器只生成IAT
我刚刚试了下gcc编译,还是有INT的
2020-12-29 23:19
0
雪    币: 12159
活跃值: (5704)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
34r7hm4n 我刚刚试了下gcc编译,还是有INT的
应该有编译选项,我找过几个exe,统统只有IAT
2020-12-30 00:15
0
游客
登录 | 注册 方可回帖
返回
//