输入表详解

输入表结构如下:

输入表结构图
在数据成员目录的第二项可以找到输入表的offset
输入表offset
输入表其实是一个IMAGE_IMPORT_DESCRIPTOR结构数组,看下Winnt.h中的详细定义

typedef struct _IMAGE_IMPORT_DESCRIPTOR {	// 0 for terminating null import descriptor
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    };
    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;

每一个DLL都对应于一个IMAGE_IMPORT_DESCRIPTOR(输入描述符),以一个全0的输入描述符作为结束标志。

第一项用到的其实只是OriginalFirstThunk,它指向一个IMAGE_THUNK_DATA结构数组,每一个IMAGE_THUNK_DATA又与该PE文件调用的每一个输入函数有关系,给出IMAGE_THUNK_DATA结构。

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

IMAGE_THUNK_DATA是一个DWORD大小的一个联合,它在不同的时候有着不同的含义,跟IMAGE_IMPORT_DESCRIPTOR类似,以一个全0的IMAGE_IMPORT_DESCRIPTOR结束
ForwarderString是转向它的第一个索引的函数的名称的RVA(不懂Forwarder的可以搜一下中转函数,了解一下,我本人在DLL劫持中用过)
Function代表着输入函数的地址,这个之后再讲。
Ordinal代表该函数在导入的DLL里的序号,不过至今很少用了。只有当IMAGE_THUNK_DATA的最高位为1时才代表使用序号导入,此时低31位代表在导入DLL中的该函数的序号
AddressOfData,是一个指向IMAGE_IMPORT_BY_NAME的一个指针,它表示用函数名进行导入。当IMAGE_THUNK_DATA的最高位为0时代表使用函数名进行导入,此时这个四字节代表着该IMAGE_IMPORT_BY_NAME的RVA。
IMAGE_IMPORT_BY_NAME的结构如下:

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

由一个word类型和一个可变长的ASCII字符串组成,因为字符串,即导入函数的函数名长度不定,所以这里用一个BYTE类型粗略代替。
Hint用于被PE装载器快速查找导入函数的位置,但该值可有可无。
Name数组是该导入函数的函数名,是一个以NULL结尾的ASCII字符串。
觉得抽象的根据最上面的那副图结合着理解。

TimeDateStamp是一个时间戳,我们不研究这个。
ForwardChain,这是第一个被转向的API的索引,我们不研究这个。
Name,指向该DLL的名字的以一个以0结尾的ASCII字符串的RVA地址。
FirstThunk,这是一个非常重要的一个域,它跟OriginalFirstThunk一样也指向一个IMAGE_THUNK_DATA结构数组。只所以有这样一个并行数组是因为FirstThunk域才是PE加载器加载PE文件到内存后所用到的真正的域,PE加载器会根据OriginalFirstThunk指向的函数进行迭代搜索,将导入函数的实际地址填入到FirstThunk指向的地址去,然后PE文件仅有FirstThunk这个域就可以实现调用导入函数的功能了。
随便找一个PE文件,我们用IDA打开看一下PE文件是如何利用导入表进行调用的
这里有一个外部函数,相当于一个jmp跳到了put例程里去了
在这里插入图片描述
然后这里第一条指令又是一个jmp指令跳到了导入表里,而这个jmp呢,其实是跳到了FirstThunk指向的内存里的puts函数的地址,类似于假如FirstThunk里存储的是0x2000,那么跳转的时候就是jmp [ImageBase+0x2000+ord(__imp_puts) * sizeof(DWORD) ],ord是puts的序号,有兴趣的可以再去od动态调试看看,你会发现你跳到的那一大片值就是一堆函数指针的地方。
结构图类似于这样:
PE装载器填充后的图
ImportTable的offset是0x2040,跳到这里,记得转换成文件偏移,我们来实战分析下第一个DLL:
第一个IMAGE_IMPORT_DESCRIPTOR
在这里插入图片描述
TimeDateStamp为0,ForwarderChain也为0,Name函数名的RVA是0x2174,这里是user32.dll
在这里插入图片描述
OriginalFirstThunk指向0x208C的RVA地址,换算一下,是0x48c。
IMAGE_THUNK_DATA
值为0x2110,说明是用函数名导入的,继续换算,找到了下面的地址

hint是019B,函数名是LoadIconA

查找其余函数
在这里插入图片描述
这里就不详细分析了,直接列出

RVA		文件偏移		Hint		函数名
211C		51C				01DD	PostQuitMessage		
20F4		4F4				0128	GetMessageA
20E0		4E0				0094	DispatchMessageA
2150		550				027D	TranslateMessage
2164		564				028B	UpdateWindow
2102		502				0197	LoadCursorA
20CE		4CE				0083 	DefWindowProcA
20BC		4BC				0058	CreateWindowsExA
212E		52E				01EF	RegisterClassExA
2142		542				0265	ShowWindow

可以推测下导出表的这些函数,很像建立一个窗口并且分发消息的固有流程,首先RegisterClassExA,然后CreateWindows,接着ShowWindow,然后DispatchMessage分发相应的消息,所以有时候我们对恶意代码进行静态分析就可以先从导入表入手。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值