您的位置:首页 > 其它

PE手工分析-导入表和导入函数地址表分析

2012-05-10 09:42 211 查看
在上篇:PE手工分析-PE头 中我们了解了PE文件头内容,在此基础上我们来分析一下导入表和导入函数地址表的内容.

还是使用上篇使用的PE文件来分析,上一篇中我们基本上已经定位出PE头的位置以及相应内容.

在PE扩展头中包含 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表

通过该数据目录表我们可以进一步对导入表和导入函数地址表进行详细的分析(其数据目录表分析也雷同)

详细信息可以查看MSDN定义.

数据目录表结构定义:

typedef struct _IMAGE_DATA_DIRECTORY {

DWORD   VirtualAddress;

DWORD   Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]数据目录表分析如下:

索引

数据目录表

文件偏移地址

偏移

大小

说明

0

导出表

00000170h

00 00 00 00

00 00 00 00

没有导出

1

导入表

00000178h

00 E0 01 00

64 00 00 00

...

12

导入地址表

000001d0h

A0 E2 01 00

3C 02 00 00



...

...

导入表RVA=01E000

IAT RVA=01E2A0

RVA的概念请参考:RVA,另外可以参考《加密解密ii》中的第二章介绍



了解了RVA之后我们就明白了在获取导入表的RVA之后需要根据节地址转换成文件相对地址FOA

所以为了获取导入表的文件相对地址需要对PE的节表进行分析

首先需要知道节表所在的位置(紧跟数据目录表结尾)

总共16个数据目录,导入地址函数表在第12(0开始)个位置

节表起始位置=1d0+8+3×8=1F0

节表结构如下:

typedef struct _IMAGE_SECTION_HEADER {

BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];// #define IMAGE_SIZEOF_SHORT_NAME 8

union {

DWORD   PhysicalAddress;

DWORD   VirtualSize;

} Misc;

DWORD   VirtualAddress;

DWORD   SizeOfRawData;

DWORD   PointerToRawData;

DWORD   PointerToRelocations;

DWORD   PointerToLinenumbers;

WORD    NumberOfRelocations;

WORD    NumberOfLinenumbers;

DWORD   Characteristics;

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


从上面镜像头分析可以之后该PE中包含有7个节

每个节包含40个字节

总共占有空间:40×7=280(0x118)->01F0-20E



可以看到每个节对应的内容如下:

段名称

虚拟地址

虚拟大小

物理地址

物理大小

标志

PointerToRawData

.textbss

1000

0

10000

0

2EE0

.text

11000

897D

00008A00

60000020

00000400

.rdata

1A000

24B5

2600

40000040

8E00

.data

01D000

05F4

2000

C0000040

B400

.idata

0001E000

10C8

1200

C0000040

B600

.rsrc

00020000

0459

0600

40000040

C800

.reloc

00021000

06DA

0800

42000040

CE00

节表的分析完毕,然后我们需要针对导入表的RVA获取相应的FOA

RVA:01E000=>PointerToRawData:B600


指向导入表描述符

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD   Characteristics;            //

DWORD   OriginalFirstThunk;         //

} DUMMYUNIONNAME;         //0001e270=>B600+270=b870->01E4DC->_foo@4

DWORD   TimeDateStamp;                  // 0

DWORD   ForwarderChain;                 //0

DWORD   Name;                 //01E4E6=>B600+4E6=BAE6->dllExport.dll

DWORD   FirstThunk;                     //01E4AC=>B600+4AC=BAAC->01E4DC->_foo@4

} IMAGE_IMPORT_DESCRIPTOR;


其文件内容如下:





OriginalFirstThunk指向地址(RVA)01E4DC->(FOA)BADC

FirstThunk指向地址(RVA)01E4AC->(FOA)BAAC对应结构为

typedef struct _IMAGE_THUNK_DATA32 {

union {

DWORD ForwarderString;      // PBYTE

DWORD Function;             // PDWORD

DWORD Ordinal;

DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME

} u1;

} IMAGE_THUNK_DATA32;


相应IMAGE_THUNK_DATA指向的内容为

typedef struct _IMAGE_IMPORT_BY_NAME {

WORD    Hint;            //00

BYTE    Name[1];     //_foo@4

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;


这里对应的导入表分析基本完成了

相应的IAT分析如下:

RVA:01E2A0=>PointerToRawData:B600+(2A0)=B8A0在找到了IAT表的地址后让我们来看一下其中的内容



可以看到这里的BAAC正好处理B8A0的区域中.

所以很显然,FirstThunk属于IAT中的某一个地址区域

相应PE文件内容如下

Dll加载之前导入表结构



在DLL加载之后导入表中内容将被操作系统填充为函数的VA



到这里基本上已经把IAT和INT绑定起来了,而在函数调用过程中将如何实现调用IAT的函数地址呢?

程序中每个调用 API 函数的 CALL 指令所使用的地址都是相应函数登记在 IAT 表的地址

源文档 <http://blog.csdn.net/misterliwei/article/details/840983>

那么,IAT导入函数地址表,和导入表有什么联系呢??

其实,所有的DLL的IMAGE_IMPORT_DESCRIPTOR结构的FirstThunk指向的是一片连续的内存空间,第一个DLL的IMAGE_IMPORT_DESCRIPTOR结构的FirstThunk的值,就是IAT表的起始地址!

也就是说,导入表中的首个IMAGE_IMPORT_DESCRIPTOR的FirstThunk字段,在内存中,等同于IMAGE_NT_HEADER.OptionHeader.DataDirectory[12].VirtualAddress

数据目录表第13项,索引值为12,就是IAT了。

在RING3的API劫持中,很多人都会选择使用IAT劫持,也就是基于这个理论的。

源文档 <http://tieba.baidu.com/f?kz=726947835>

对于IAT和导入表之间的关系用如下图片作为结尾



导入表中的FirstThunk指向IAT表中的某一项

之前的理解有问题所以后面加以修改
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: