PE文件结构详解(四)PE导入表
PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPORT,即导入表。
也许大家注意到过,在IMAGE_DATA_DIRECTORY中,有几项的名字都和导入表有关系,其中包括:IMAGE_DIRECTORY_ENTRY_IMPORT,IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,IMAGE_DIRECTORY_ENTRY_IAT 和IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT这几个导入都是用来干什么的,他们之间又是什么关系呢?听我慢慢道来。
- IMAGE_DIRECTORY_ENTRY_IMPORT就是我们通常所知道的导入表,在PE文件加载时,会根据这个表里的内容加载依赖的DLL,并填充所需函数的地址。
- IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 叫做绑定导入表,在第一种导入表导入地址的修正是在PE加载时完成,如果一个PE文件导入的DLL或者函数多那么加载起来就会略显的慢一些,所以出现了绑 定导入,在加载以前就修正了导入表,这样就会快一些。
- IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 叫做延迟导入表,一个PE文件也许提供了很多功能,也导入了很多其他DLL,但是并非每次加载都会用到它提供的所有功能,也不一定会用到它需要导入的所有 DLL,因此延迟导入就出现了,只有在一个PE文件真正用到需要的DLL,这个DLL才会被加载,甚至于只有真正使用某个导入函数,这个函数地址才会被修 正。
- IMAGE_DIRECTORY_ENTRY_IAT是导入地址表,前面的三个表其实是导入函数的描述,真正的函数地址是被填充在导入地址表中的。
举个实际的例子,看一下下面这张图:
这个代码调用了一个RegOpenKeyW的导入函数,我们看到其opcode是FF
15 00 00 19 30气质FF 15表示这是一个间接调用,即call dword ptr [30190000]
;这表示要调用的地址存放在30190000这个地址中,而30190000这个地址在导入地址表的范围内,当模块加载时,PE 加载器会根据导入表中描述的信息修正30190000这个内存中的内容。
那么导入表里到底记录了那些信息,如何根据这些信息修正IAT呢?我们一起来看一下导入表的定义:
- 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;
使用RtlImageDirectoryEntryToData并将索引号传1,会得到一个如上结构的指针,实际上指向一个上述结构的数组,每个导入的DLL都会成为数组中的一项,也就是说,一个这样的结构对应一个导入的DLL。
Characteristics和OriginalFirstThunk:一个联合体,如果是数组的最后一项Characteristics为0,否则OriginalFirstThunk保存一个RVA,指向一个IMAGE_THUNK_DATA的数组,这个数组中的每一项表示一个导入函数。
TimeDateStamp:映象绑定前,这个值是0,绑定后是导入模块的时间戳。
ForwarderChain:转发链,如果没有转发器,这个值是-1。
Name:一个RVA,指向导入模块的名字,所以一个IMAGE_IMPORT_DESCRIPTOR描述一个导入的DLL。
FirstThunk:也是一个RVA,也指向一个IMAGE_THUNK_DATA数组。
既然OriginalFirstThunk与FirstThunk都指向一个IMAGE_THUNK_DATA数组,而且这两个域的名字都长得很像,他俩有什么区别呢?为了解答这个问题,先来认识一下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;
ForwarderString是转发用的,暂时不用考虑,Function表示函数地址,如果是按序号导入Ordinal就有用了,若是按名字导入AddressOfData便指向名字信息。可以看出这个结构体就是一个大的union,大家都知道union虽包含多个域但是在不同时刻代表不同的意义那到底应该是名字还是序号,
该如何区分呢?可以通过Ordinal判断,如果Ordinal的最高位是1,就是按序号导入的,这时候,低16位就是导入序号,如果最高位是0,则
AddressOfData是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构,用来保存名字信息,由于Ordinal和AddressOfData实际上是同一个内存空间,所以AddressOfData
其实只有低31位可以表示RVA,但是一个PE文件不可能超过2G,所以最高位永远为0,这样设计很合理的利用了空间。实际编写代码的时候微软提供两个宏
定义处理序号导入:IMAGE_SNAP_BY_ORDINAL判断是否按序号导入,IMAGE_ORDINAL用来获取导入序号。
这时我们可以回头看看OriginalFirstThunk与FirstThunk,OriginalFirstThunk指向的IMAGE_THUNK_DATA数组包含导入信息,在这个数组中只有Ordinal和AddressOfData是有用的,因此可以通过OriginalFirstThunk查找到函数的地址。FirstThunk则略有不同,在PE文件加载以前或者说在导入表未处理以前,他所指向的数组与OriginalFirstThunk中的数组虽不是同一个,但是内容却是相同的,都包含了导入信息,而在加载之后,FirstThunk中的Function开始生效,他指向实际的函数地址,因为FirstThunk实际上指向IAT中的一个位置,IAT就充当了IMAGE_THUNK_DATA数组,加载完成后,这些IAT项就变成了实际的函数地址,即Function的意义。还是上个图对比一下:
上图是加载前。
上图是加载后。
最后总结一下:
- 导入表其实是一个IMAGE_IMPORT_DESCRIPTOR的数组,每个导入的DLL对应一个IMAGE_IMPORT_DESCRIPTOR。
- IMAGE_IMPORT_DESCRIPTOR包含两个IMAGE_THUNK_DATA数组,数组中的每一项对应一个导入函数。
- 加载前OriginalFirstThunk与FirstThunk的数组都指向名字信息,加载后FirstThunk数组指向实际的函数地址。
PE文件结构详解(四)PE导入表的更多相关文章
- PE文件结构详解(五)延迟导入表
PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...
- PE文件结构详解(六)重定位
前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...
- PE文件结构详解(二)可执行文件头
在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...
- PE文件结构详解(三)PE导出表
上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...
- PE文件结构详解(一)基本概念
PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那 ...
- PE文件结构详解(三)
0x01 前言 上一篇讲到了数据目录表的结构和怎找到到数据目录表(DataDirectory[16]),这篇我们我来讲讲数据目录表后面的另一个结构——区块表. 0x01 区块 区块就是PE载入器将PE ...
- PE文件结构详解
(注:最左边是文件头的偏移量.) IMAGE_DOS_HEADER STRUCT { +0h WORD e_magic // Magic DOS signature MZ(4Dh 5Ah) DOS可执 ...
- 一起学Hive——详解四种导入数据的方式
在使用Hive的过程中,导入数据是必不可少的步骤,不同的数据导入方式效率也不一样,本文总结Hive四种不同的数据导入方式: 从本地文件系统导入数据 从HDFS中导入数据 从其他的Hive表中导入数据 ...
- 小甲鱼PE详解之输入表(导出表)详解(PE详解09)
小甲鱼PE详解之输出表(导出表)详解(PE详解09) 当PE 文件被执行的时候,Windows 加载器将文件装入内存并将导入表(Export Table) 登记的动态链接库(一般是DLL 格式)文件一 ...
随机推荐
- 20140527-ASP.NET中尖括号百分号用法
1.<%=%> 里面放的变量名,如:<div><h1>Hello World</h1><p>Welcome to Beginning ASP ...
- Myeclipse安装svn插件(link方式)
Myeclipse安装svn插件(link方式) 优点:1.离线安装2.非侵入式 演示版本 myeclipse--myeclipse8.6 svn--subeclipse-site-1.6.5.zip ...
- 求单链表倒数第m个结点
问题:求单链表倒数第m个结点,要求不准求链表的长度,也不许对链表进行逆转 解:设置两个指针p和q,p.q指向第一个结点.让p先移动到链表的第m个结点,然后p和q同时向后移动,直到p首先到达尾结点.此时 ...
- 网络A、B、C类IP地址的区别
学网络之前得先要明白几个概念:(起初我也不怎么知道后来就慢慢会了) 1字节=8位,1位就是1个数字,所以1字节等于8个数字. 2的8次方,和二进制11111111转换为十进制并不一回事. 0-127是 ...
- Eclipse 运行多个Tomcat实例
- 转:java中volatile关键字的含义
转:java中volatile关键字的含义 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...
- VS2012中进行Web性能和负载测试
问题1:无法使用ie进行录制 解决方法: 工具 >> 管理加载项 >> 在工具栏和扩展中找到发布者为Microsoft Corporation的Microsoft Web Te ...
- PHP获取Cookie模拟登录CURL
要提取google搜索的部分数据,发现google对于软件抓取它的数据屏蔽的厉害,以前伪造下 USER-AGENT 就可以抓数据,但是现在却不行了.利用抓包数据发现,Google 判断了 cookie ...
- 精美舒适的对话消息提示框--第三方开源--SweetAlertDialog
SweetAlertDialog(sweet-alert-dialog)是一个套制作精美.动画效果出色生动的Android对话.消息提示框 SweetAlertDialog(sweet-alert-d ...
- execute连接 类型
set rs=server.createobject("adodb.recordset") sql="select top 10 id,name from tablena ...