PE文件格式学习(四):导入表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tutucoo/article/details/83828686

UPDATE:

  • 在文章的末尾更新了一张图,在网上找的,有助于理解导入表的结构

1.概述

导入表是逆向和病毒分析中比较重要的一个表,在分析病毒时几乎第一时间都要看一下程序的导入表的内容,判断程序大概用了哪些功能。

导入表是数据目录表中的第2个元素,排在导出表的后面。

2.导入表解析

先来了解一下导入表在PE文件中的结构体:

struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics; 
        DWORD   OriginalFirstThunk;         
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp; 
    DWORD   ForwarderChain;         
    DWORD   Name;
    DWORD   FirstThunk;              
} IMAGE_IMPORT_DESCRIPTOR;

在说明字段之前,需要说明的是,导入表是一个数组,每个元素都是上面的结构体,长度是14h,并且以14h字节的“0”作为数组结尾,如果程序导入了多个模块,那么这个数组的长度应该是14h*(n+1)。

以下是字段的说明。

DWORD TimeDateStamp:一般为空,对应上图的0x00000000

DWORD ForwarderChain:对应上图的0x00000000

DWORD Name:导入模块名的RVA,对应上图的0x0000230c,

DWORD Characteristics:跟OrginalFirstThunk是联合体,对应上图的0x0000227c

DWORD OrginalFirstThunk:跟Characteristics是联合体,一般情况下是OrginalFirstThunk生效,它是IMAGE_THUNK_DATA数组的RVA,对应上图的0x0000227c

我们来看看IMAGE_THUNK_DATA结构体

struct _IMAGE_THUNK_DATA32{
    union {
        DWORD ForwarderString 
        DWORD Function ; 被输入的函数的内存地址
        DWORD Ordinal ; 被输入的API的序数值
       DWORD AddressOfData ; 高位为0则指向IMAGE_IMPORT_BY_NAME 结构体二
    }u1;
}IMAGE_THUNK_DATA32;

我们知道指向IMAGE_THUNK_DATA的RVA是0x0000227c,转为offset是107ch,从107ch开始到0x00000000结束是一个完整的导入函数数组,如下图:

我们对照图来一一讲解IMAGE_THUNK_DATA的字段。

ForwarderString:只有当IMAGE_IMPORT_DESCRIPTOR中的ForwarderChain有值时,它才有效

Function:导入函数的实际内存地址,只有在载入内存中时才有效

Ordinal:导入函数的导出序号,导出表中允许使用序号的方式导出函数,导入表也要有相应的机制。只有当IMAGE_THUNK_DATA的最高位是1的时候才有效

AddressOfData:当IMAGE_THUNK_DATA的最高位为0的时候有效,指向一个IMAGE_IMPORT_BY_NAME结构体数组,有没有一种可能是最高位为1,并且是以函数名的方式导入函数的呢?我们知道最高位为1的最小数也是0x80000000,这个位置是系统空间,程序不可访问,因此是不可能的。

IMAGE_IMPORT_BY_NAME结构体如下:

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

Hint:指出函数在所在的dll的输出表中的序号

Name:指出要输入的函数的函数名

接下来是IMAGE_IMPORT_DESCRIPTOR的最后一个字段。

DWORD FirstThunk:对应的是IMAGE_IMPORT_DESCRIPTOR那张图中的0x00002000,在磁盘中它指向的位置跟OriginalFirstThunk是一样的,同样是指向IMAGE_THUNK_DATA数组。如果PE文件被加载进内存,FirstThunk指向的IMAGE_THUNK_DATA数组中的Function保存的就是真实内存中的函数地址,OriginalFirstThunk指向的同样是IMAGE_THUNK_DATA数组,但是保存的并不是真实内存中的函数地址,而是IMAGE_IMPORT_BY_NAME数组的RVA,保存的是导入函数的文件名。

3.总结

1.重点需要分清在磁盘中和在内存中OriginalFirstThunk和FirstThunk的区别,在磁盘中二者都一样,在内存中前者的IMAGE_THUNK_DATA结构生效的是AddressOfData字段,因此指向的是IMAGE_IMPORT_BY_NAME数组的RVA,所以它也被称为INT,后者保存的是导入函数在内存中的真实地址,所以它被称为IAT。

2.需要厘清一点概念就是,PE中导入表,也就是IMAGE_IMPORT_DESCRIPTOR结构在一个数组中,意味着一个PE文件中可能有多个导入表,每个导入表中只有一个OriginalFirstThunk和FirstThunk,但是他们指向的IMAGE_THUNK_DATA是一个数组,数组的元素个数代表函数的个数,如果是IMAGE_THUNK_DATA中的AddressOfData字段生效,它指向的是一个IMAGE_IMPORT_BY_NAME数组,这个数组中的元素个数跟IMAGE_THUNK_DATA中的可能不一样,因为有的函数没有名字。

3.凡是数组的最后一定是以0填充,长度是数组元素的大小,字符串以00作为结束。

猜你喜欢

转载自blog.csdn.net/tutucoo/article/details/83828686