PE文件和COFF文件格式分析——导入表

因为之前已经介绍了PE基本格式以及导出表相关,因此一些基本的东西就直接省略,用工具直接定位。

PE文件和COFF文件格式分析(1)

PE文件和COFF文件格式分析——导出表

本次以 kernel32.dll 为例,查看其导入表。

首先用 Stud_PE.exe 查看导入表的位置如下:
在这里插入图片描述
也就是在文件的地址 0x084c88。跳过去看看,以红线开始
在这里插入图片描述
查看导入表定义:

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;

可见大小是 0x14 个字节。上面看到Stud_pe 中展示了导入表大小是 0x744。0x744 / 0x14 = 0x5D。
因为这里是一个数组!一共有 0x5D 个结构。用 0x84C88 + 0x744 = 0x853cc
在这里插入图片描述
看最后的 0x14 个字节是全0。也就是这个是数组结束的标志。即实际有效的是 0x5C 个导入表。
用 PE view 查看一下:
在这里插入图片描述
右侧是根据每个DLL 分开的。可以数一数确实是那么多。
因为是数组,这里就分析一个就可以,看图:
在这里插入图片描述
上面说到一个 IMAGE_IMPORT_DESCRIPTOR 的大小是 0x14 个字节,是上图中红色划线之间的部分。
对应每一个值就是:
在这里插入图片描述
有关上面几个字段的含义可以参考:
PE文件学习笔记(一)—导入导出表
PE文件格式学习(四):导入表

这里简单说一下,中间2个为0 的字段暂时不管,以后理解了再补充。
Name 就是这个导入表的名称的地址,这里是 0xa18d4,计算成文件地址是 0x868d4。
在这里插入图片描述
可以看到是一个以 0 结尾的字符串。
佐证一下:
在这里插入图片描述
另外两个重要的字段的含义:

/*
	FirstThunk;
	OriginalFirstThunk;
	这两个值含义在文件中应该是一样的,指向一个结构体数组 IMAGE_THUNK_DATA32[N]
	但是当PE 加载进内存就有差异了,FirstThunk 指向的数组和OriginalFirstThunk指向的不再一样
	FirstThunk 指向的数组的含义是函数地址!!),而 OriginalFirstThunk 指向的是名称和序数地址,也就是 IMAGE_IMPORT_BY_NAME 的地址
	该数组结束的标志是以一个 IMAGE_THUNK_DATA32 全部为0 作为标志
	IMAGE_THUNK_DATA32 最高位如果为0,则IMAGE_THUNK_DATA32.AddressOfData是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构,用来保存名字信息
	反之如果最高位为1,则低16位是导出序数。
	*/

因此,简单的说,在文件里面,通常 OriginalFirstThunk 指向一个数组的首地址,这个数组是:
IMAGE_THUNK_DATA32[N]。
至于 32 和 64 位差距暂时没研究。
看一下:
在这里插入图片描述
发现 FirstThunk = 0x816d0 ->RA = 0x666D0
OriginalFirstThunk = 0xa0fcc ->RA = 0x85fcc

在这里插入图片描述
在这里插入图片描述
我们发现,虽然 FirstThunk OriginalFirstThunk 的值不一样,但是对应地址写的 IMAGE_THUNK_DATA32[N]
的地址是一样的,印证了上面的说法。
由于这个地址最高位是0,一次他说一个指向一个名字结构的地址,这个名字结构就是 typedef struct

_IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    CHAR   Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

也就是前两个字节是导入函数在对应 DLL 中的序数,后面是一个以 0 结尾的字符串。

这个地址 0x0a1898(转换成文件地址:0x86898)。。跳过去:
在这里插入图片描述
可以看到序数是 0x0001,函数名字是 RtlCaptureStackBackTrace。
同理,看图
在这里插入图片描述
由于红线是 IMAGE_THUNK_DATA32[N] 数组首地址,以全0结构体结束,可以看到一共有三个值,第四个就是全0。
不妨再看第二个元素是 0x0a18B4,转换成文件地址:0x868B4:
在这里插入图片描述
可以看到,其实就是接着上面的一个名字结构体的。该DLL 有三个导入函数:
看工具分析结果:
在这里插入图片描述
上面说到:

发现  FirstThunk = 0x816d0 	->RA = 0x666D0 
OriginalFirstThunk = 0xa0fcc 	->RA = 0x85fcc

对比就可以发现:工具第一列就是 IMAGE_THUNK_DATA32 数组的每个元素的地址(文件中的地址)
这每个元素指向另一个地址(名称结构 IMAGE_IMPORT_BY_NAME 的地址),也就是 第二列 DATA 的值(这个值是RVA)。
Value 列,就是 IMAGE_IMPORT_BY_NAME 中序数和名字了。

最后,再看:
在这里插入图片描述

在这里插入图片描述

可以发现
FirstThunk = 0x816d0 ->RA = 0x666D0
OriginalFirstThunk = 0xa0fcc ->RA = 0x85fcc
两个字段指向的是不同的两个数组 IMAGE_THUNK_DATA32 [N] 的地址,数组以一个全0的结构体结束。
虽然这两个数组不同,但是数组的内容是一模一样的。
工具看一下:
在这里插入图片描述
在这里插入图片描述
可以发现两个不同的地方,对应的实际数据是一样的。

参考下图,来源: PE文件格式学习(四):导入表
在这里插入图片描述
按照一些说法,上图中 IAT,再加载进内存中后就不是和INT 一样相同指向了。(未证实)

最后附上代码分析的结果(一共 0x5C 个导入表)~:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章