【PE结构】5.导入表

一、前言
二、PE整体结构
三、DOS头
四、NT头
五、区段头
六、导出表

七、导入表

八、资源表
九、其他表


1.概念

导入 是PE文件(EXE)在运行时,使用到了其他PE文件(DLL)中的函数、变量、类等这样的行为。
导入表 存储的是从其他PE文件(DLL)导入过来的函数名、序号。在加载到内存后,还存储这些函数的地址。
导入表结构 导入表是一个结构体数组,最后以一个结构体大小的全零元素结尾。每一个数组元素代表一个PE文件的导入信息。

PE文件(EXE)通常需要多个PE文件(DLL)的支持,所以导入表一般有多个。

2.导入表定位

前面提到的 NT头->扩展头->数据目录表->第个元素->相对虚拟地址(RVA)
还用010Editor打开百度云盘看一下
这里写图片描述
从扩展头里的数据,我们就可以得到导入表的定位信息RVA

再啰嗦一下RVA定位数据的使用
如果是在文件里用,需要用FOA
导入表FOA = 导入表RVA - 区段RVA + 区段FOA
如果是加载到内存里使用,需要用VA
导入表VA = 加载基址BaseImage + 导入表RVA

实践一下:
我们是在文件里操作,所以要先得到FOA
要得到FOA先要得到导入表所在.rdata区段信息
区段信息怎么得到还记得吗?
对,在区段头表里有相关信息,忘了就看看前面的文章吧
这里写图片描述
导入表FOA = 导入表RVA - 区段RVA + 区段FOA = 5706C0H - 45F000H + 45D800H = 56EEC0H

3.导入表结构

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk; // 指向 导入名称表INT(ImportNameTable)的RVA
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  
    DWORD   ForwarderChain;                 
    DWORD   Name;                   // 指向 DLL名称的地址 RVA
    DWORD   FirstThunk;             // 指向 导入地址表IAT(ImportAddressTable)的RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

导入表结构体大小为20个字节。
有几个模块,就有几个结构体,从起始地址开始,每个20字节,遇到20个0结束。

实践一下:
上一环节,我们找到了导入表FOA,我们来看看FOA对应的数据
这里写图片描述
图中圈除了三个元素,每个20字节
我们再寻找一下结束的位置,20个0
这里写图片描述
果然找到了20个0,0x56F207,应该就是导入表结束的位置。
我们在看看其他的一些数据
导入表大小=结束位置-起始位置=0x56F208-0x56EEC0=0x348=840
导入表元素个数=导入表大小/元素大小=840/20=42
也就是导入了42个DLL

4.导入名称表、导入地址表

4.1.表内数据元素结构

导入名称表和导入地址表结构完全相同。
INT和IAT两张表内的元素都是4字节大小的结构体IMAGE_THUNK_DATA
数据元素列表遇到4字节0结束

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      
        DWORD Function;             // 导入函数地址
        DWORD Ordinal;              // 序号导入用
        DWORD AddressOfData;        // 函数名导入用,指向PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

4.2.导入名称表、导入地址表 关系

在磁盘文件中,OriginalFirstThunk指向的INT 与 FirstThunk指向的IAT 两表中的数据是相同的。
加载到内存后,输入地址表会由加载器把对应的PE文件中的函数地址覆盖到这里。

4.3.使用规则

通过结构体定义我们可以看到,结构体里面是一个联合体
也就是说,其实是同一个数据,看你用哪种形式使用
如果加载到内存中,Function函数地址启用
如果磁盘文件中,只有后两个字段起作用Ordinal、AddressOfData。当数据最高位为1时Ordinal序号起作用,当数据最高位为0时AddressOfData函数名起作用,指向结构体。

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;   // 如不为空,值为函数在导出表中的索引
    CHAR   Name[1]; // 函数名的第一字节,到0结束
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

实践一下:
不按上面写的那么复杂的去做了,简单的在文件中看一下
这里写图片描述
找到导入表第一个元素,找到第一个元素内的INT地址和IAT地址
INT表FOA = 0x5715CC - 0x45F000 + 0x45D800 = 0x56FDCC
这里写图片描述
函数名FOA = 0x571634 - 0x45F000 + 0x45D800 = 0x56FE34
这里写图片描述


上一篇:【PE结构】导出表
下一篇:【PE结构】资源表

猜你喜欢

转载自blog.csdn.net/chy_chenyang/article/details/80796643