破解软件感悟-PE文件格式之Import Table(引入表)(四)
先来看一个可执行文件的实例:本例程打开一PE文件,将所有引入dll和对应的函数名读入一编辑控件,同时显示 IMAGE_IMPORT_DESCRIPTOR 结构各域值。
C:\QQDownload\blah.EXE
================[ IMAGE_IMPORT_DESCRIPTOR ]=============
OriginalFirstThunk = 303C
TimeDateStamp = 0
ForwarderChain = 0
Name = KERNEL32.dll
FirstThunk = 3064
Hint Function
-----------------------------------------
0 CreateFileA
0 FreeLibrary
0 ExitProcess
0 LoadLibraryA
0 ReadFile
0 WriteFile
0 GetProcAddress
================[ IMAGE_IMPORT_DESCRIPTOR ]=============
OriginalFirstThunk = 305C
TimeDateStamp = 0
ForwarderChain = 0
Name = USER32.dll
FirstThunk = 3084
Hint Function
-----------------------------------------
0 MessageBoxA
上面的数据我们如何得到的呢?
理论:
一.首先,您得了解什么是引入函数。一个引入函数是被某模块调用的但又不在调用者模块中的函数,因而命名为"import(引入)"。引入函数实际位于一个或者更多的DLL里。调用者模块里只保留一些函数信息,包括函数名及其驻留的DLL名。现在,我们怎样才能找到PE文件中保存的信息呢? 转到 data directory 寻求答案吧。再回顾一把,下面就是 PE header:
IMAGE_NT_HEADERS STRUCT
Signature dd ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER <>
IMAGE_NT_HEADERS ENDS
optional header 最后一个成员就是 data directory(数据目录):
IMAGE_OPTIONAL_HEADER32 STRUCT
....
LoaderFlags dd ?
NumberOfRvaAndSizes dd ?
DataDirectory IMAGE_DATA_DIRECTORY 16 dup(<>)
IMAGE_OPTIONAL_HEADER32 ENDS
data directory 是一个 IMAGE_DATA_DIRECTORY 结构数组,共有16个成员。如果您还记得节表可以看作是PE文件各节的根目录的话,也可以认为 data directory 是存储在这些节里的逻辑元素的根目录。明确点,data directory 包含了PE文件中各重要数据结构的位置和尺寸信息。每个成员包含了一个重要数据结构的信息。
|
Member |
Info inside |
|
0 |
Export symbols |
|
1 |
Import symbols |
|
2 |
Resources |
|
3 |
Exception |
|
4 |
Security |
|
5 |
Base relocation |
|
6 |
Debug |
|
7 |
Copyright string |
|
8 |
Unknown |
|
9 |
Thread local storage (TLS) |
|
10 |
Load configuration |
|
11 |
Bound Import |
|
12 |
Import Address Table |
|
13 |
Delay Import |
|
14 |
COM descriptor |
上面那些金色显示的是我熟悉的。了解 data directory 包含域后,我们可以仔细研究它们了。data directory 的每个成员都是 IMAGE_DATA_DIRECTORY 结构类型的,其定义如下所示:
IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress dd ?
isize dd ?
IMAGE_DATA_DIRECTORY ENDS
VirtualAddress 实际上是数据结构的相对虚拟地址(RVA)。比如,如果该结构是关于import symbols的,该域就包含指向IMAGE_IMPORT_DESCRIPTOR 数组的RVA。
isize 含有VirtualAddress所指向数据结构的字节数。
下面就是如何找寻PE文件中重要数据结构的一般方法:
1. 从 DOS header 定位到 PE header
2. 从 optional header 读取 data directory 的地址。
3. IMAGE_DATA_DIRECTORY 结构尺寸乘上找寻结构的索引号: 比如您要找寻import symbols的位置信息,必须用IMAGE_DATA_DIRECTORY 结构尺寸(8 bytes)乘上1(import symbols在data directory中的索引号)。
4. 将上面的结果加上data directory地址,我们就得到包含所查询数据结构信息的 IMAGE_DATA_DIRECTORY 结构项。
小段总结:以上说明data directory是一个结构数组,这里面包含16个结构成员,每一个结构成员是IMAGE_DATA_DIRECTORY 结构类型,而这种结构类型包含VirtualAddress dd ?
isize dd ?
二.现在我们开始真正讨论引入表了。
|
1 |
Import symbols |
从上面已经分析过了,这是一个IMAGE_DATA_DIRECTORY 结构类型,这个结构类型中包含VirtualAddress,这个里面放的是引入表地址
什么是引入表呢?
引入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组。如下:
IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics dd ?
OriginalFirstThunk dd ?
ends
TimeDateStamp dd ?
ForwarderChain dd ?
Name1 dd ?
FirstThunk dd ?
IMAGE_IMPORT_DESCRIPTOR ENDS
结构第一项是一个union子结构。 事实上,这个union子结构只是给 OriginalFirstThunk 增添了个别名,您也可以称其为"Characteristics"。 该成员项含有指向一个 IMAGE_THUNK_DATA 结构数组的RVA。
什么是 IMAGE_THUNK_DATA? 这是一个dword类型的集合。通常我们将其解释为指向一个 IMAGE_IMPORT_BY_NAME 结构的指针。注意 IMAGE_THUNK_DATA 包含了指向一个IMAGE_IMPORT_BY_NAME 结构的指针: 而不是结构本身。
我们用通俗的语言表示就是:
现有几个 IMAGE_IMPORT_BY_NAME 结构,我们收集起这些结构的RVA (IMAGE_THUNK_DATA)组成一个数组,并以0结尾,然后再将数组的RVA放入 OriginalFirstThunk。
此 IMAGE_IMPORT_BY_NAME 结构存有一个引入函数的相关信息。再来研究 IMAGE_IMPORT_BY_NAME 结构到底是什么样子的呢:
IMAGE_IMPORT_BY_NAME STRUCT
Hint dw ?
Name1 db ?
IMAGE_IMPORT_BY_NAME ENDS
Hint 指示本函数在其所驻留DLL的引出表中的索引号。该域被PE装载器用来在DLL的引出表里快速查询函数。该值不是必须的,一些连接器将此值设为0。
Name1 含有引入函数的函数名。函数名是一个ASCIIZ字符串。注意这里虽然将Name1的大小定义成字节,其实它是可变尺寸域,只不过我们没有更好方法来表示结构中的可变尺寸域。The structure is provided so that you can refer to the data structure with descriptive names.
小段总结: 可以这样理解上面这段话:
OriginalFirstThunk中的每个成员项实际上是一个指向IMAGE_THUNK_DATA结构数组中的每个成员
IMAGE_THUNK_DATA结构数组中存放的是指向IMAGE_IMPORT_BY_NAME 结构数组的指针
IMAGE_IMPORT_BY_NAME结构数组存放的是每个函数的RAV
三.好了,如果您还在犯糊涂,就朝这边看过来: 现在有几个 IMAGE_IMPORT_BY_NAME 结构,同时您又创建了两个结构数组,并同样寸入指向那些 IMAGE_IMPORT_BY_NAME 结构的RVAs,这样两个数组就包含相同数值了(可谓相当精确的复制啊)。 最后您决定将第一个数组的RVA赋给 OriginalFirstThunk,第二个数组的RVA赋给 FirstThunk,这样一切都很清楚了。
|
OriginalFirstThunk |
|
IMAGE_IMPORT_BY_NAME |
|
FirstThunk |
||||||||||||||||||||||||||||||
|
| |
|
|
|
| |
||||||||||||||||||||||||||||||
|
|
|
|
|
现在您应该明白我的意思。不要被IMAGE_THUNK_DATA这个名字弄糊涂: 它仅是指向 IMAGE_IMPORT_BY_NAME 结构的RVA。 如果将 IMAGE_THUNK_DATA 字眼想象成RVA,就更容易明白了。OriginalFirstThunk 和 FirstThunk 所指向的这两个数组大小取决于PE文件从DLL中引入函数的数目。比如,如果PE文件从kernel32.dll中引入10个函数,那么IMAGE_IMPORT_DESCRIPTOR 结构的 Name1域包含指向字符串"kernel32.dll"的RVA,同时每个IMAGE_THUNK_DATA 数组有10个元素。
下一个问题是: 为什么我们需要两个完全相同的数组? 为了回答该问题,我们需要了解当PE文件被装载到内存时,PE装载器将查找IMAGE_THUNK_DATA 和 IMAGE_IMPORT_BY_NAME 这些结构数组,以此决定引入函数的地址。然后用引入函数真实地址来替代由FirstThunk指向的 IMAGE_THUNK_DATA 数组里的元素值。因此当PE文件准备执行时,上图已转换成:
|
OriginalFirstThunk |
|
IMAGE_IMPORT_BY_NAME |
|
FirstThunk |
||||||||||||||||||||||||||||||
|
| |
|
|
|
| |
||||||||||||||||||||||||||||||
|
|
|
|
|
由OriginalFirstThunk 指向的RVA数组始终不会改变,所以若还反过头来查找引入函数名,PE装载器还能找寻到。
当然再简单的事物都有其复杂的一面。有些情况下一些函数仅由序数引出,也就是说您不能用函数名来调用它们: 您只能用它们的位置来调用。此时,调用者模块中就不存在该函数的IMAGE_IMPORT_BY_NAME 结构。不同的,对应该函数的 IMAGE_THUNK_DATA 值的低位字指示函数序数,而最高二进位 (MSB)设为1。例如,如果一个函数只由序数引出且其序数是1234h,那么对应该函数的 IMAGE_THUNK_DATA 值是80001234h。Microsoft提供了一个方便的常量来测试dword值的MSB位,就是 IMAGE_ORDINAL_FLAG32,其值为80000000h。
假设我们要列出某个PE文件的所有引入函数,可以照着下面步骤走:
- 校验文件是否是有效的PE。
- 从 DOS header 定位到 PE header。
- 获取位于 OptionalHeader 数据目录地址。
- 转至数据目录的第二个成员提取其VirtualAddress值。
- 利用上值定位第一个 IMAGE_IMPORT_DESCRIPTOR 结构。
- 检查 OriginalFirstThunk值。若不为0,顺着 OriginalFirstThunk 里的RVA值转入那个RVA数组。若 OriginalFirstThunk 为0,就改用FirstThunk值。有些连接器生成PE文件时会置OriginalFirstThunk值为0,这应该算是个bug。不过为了安全起见,我们还是检查 OriginalFirstThunk值先。
- 对于每个数组元素,我们比对元素值是否等于IMAGE_ORDINAL_FLAG32。如果该元素值的最高二进位为1, 那么函数是由序数引入的,可以从该值的低字节提取序数。
- 如果元素值的最高二进位为0,就可将该值作为RVA转入 IMAGE_IMPORT_BY_NAME 数组,跳过 Hint 就是函数名字了。
- 再跳至下一个数组元素提取函数名一直到数组底部(它以null结尾)。现在我们已遍历完一个DLL的引入函数,接下去处理下一个DLL。
- 即跳转到下一个 IMAGE_IMPORT_DESCRIPTOR 并处理之,如此这般循环直到数组见底。(IMAGE_IMPORT_DESCRIPTOR 数组以一个全0域元素结尾)。
破解软件感悟-PE文件格式之Import Table(引入表)(四)的更多相关文章
- 破解软件感悟-PE文件格式之实例总结(五)
有很多介绍PE文件的文章,但是我打算写一篇关于输入表的文章,因为它对于破解很有用. 我想解释它的最好的方法是举一个例子,你可以跟着我逐步深入,一步一步的思考,最后你将完全明白,我选择了一个我刚 ...
- PE文件格式(加密与解密3)(一)
本次的了解主要讲解 PE的基本概念.MS-DOS文件头.PE文件头.区块.输入表.输出表等. 这里我将会结合一个简单的小程序来加深我对PE文件结构的了解. 使用学习工具:有StudyPE.LordPE ...
- PE Header and Export Table for Delphi
Malware Analysis Tutorial 8: PE Header and Export Table 2. Background Information of PE HeaderAny bi ...
- 破解软件系列-PE文件深入浅出之Section Table节表
我们已经学了许多关于 DOS header 和 PE header 的知识.接下来就该轮到 section table(节表)了.节表其实就是紧挨着 PE header 的一结构数组.该数组成员的数目 ...
- Reverse Core 第二部分 - 13章 - PE文件格式
@date: 2016/11/24 @author: dlive PE (portable executable) ,它是微软在Unix平台的COFF(Common Object File For ...
- PE文件格式详解,第三讲,可选头文件格式,以及节表
PE文件格式详解,第三讲,可选头文件格式,以及节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶可选头结构以及作 ...
- PE文件格式分析
PE文件格式分析 PE 的意思是 Portable Executable(可移植的执行体).它是 Win32环境自身所带的执行文件格式.它的一些特性继承自Unix的Coff(common object ...
- PE文件格式介绍
Useful Tools: 1. WDK安装目录下搜下depends.exe,这个工具可以查看.exe文件依赖的.dll,以及用到的dll中的api. 2.PE文件格式分析器: 有很多的PE格式分析器 ...
- C++PE文件格式解析类(轻松制作自己的PE文件解析器)
PE是Portable Executable File Format(可移植的运行体)简写,它是眼下Windows平台上的主流可运行文件格式. PE文件里包括的内容非常多,详细我就不在这解释了,有兴趣 ...
随机推荐
- xml和xsl配合使用实例
找到一个实际应用的例子,是英语统考打印准考证的实例,关于xml和xsl配合使用的. 下面看看xml文档 <?xml version='1.0' encoding='utf-8' ?> &l ...
- 响应式的dribbble作品集魔术布局展示效果
在线演示1 本地下载 相信做设计的朋友肯定都知道dribbble.com,它是一个非常棒的设计师分享作品的网站,全世界数以万计的设计高手和行家都在这个网站上分享自己的作品,当然,如果你常在上面闲逛的话 ...
- 深入理解Oracle索引(25):一招鲜、吃遍天之单字段索引创建思路
本文较短.不过实用性很好.还是记录之. ㈠ 先别看SQL语句.看执行计划.挑出走全表扫的表 ㈡ 回头看SQL语句.分析上述表的约束字段有哪些.检查各个约束字段的索引是否存在 ㈢ 选择 ...
- Java学习笔记之接口
一.接口的概念与定义 首先考虑一个简单的接口的定义: public interface Output { int MAX_LINE = 40; void out(); void getData(Str ...
- android复制数据库到SD卡(网上搜集,未经验证)
android中使用sqlite.复制assets下的数据库到SD卡.支持大于1M的文件 如果使用SD卡,需要在AndroidManifest.xml中设置权限 <uses-permission ...
- 仿Android网易新闻客户端,并增加水平图片滑动,改进阅读体验
仿网易新闻Android端APP 主要功能展示和代码实现 差不多花了一周的时间,目前实现的了新闻下的包括头条.体育.娱乐的一系列的新闻展示,以及点击后进入的新闻详情展示. 目前效果 目前效果请访问该网 ...
- Map生成器 map适配器如今能够使用各种不同的Generator,iterator和常量值的组合来填充Map初始化对象
Map生成器 map适配器如今能够使用各种不同的Generator,iterator和常量值的组合来填充Map初始化对象 package org.rui.collection2.map; /** * ...
- OC和JS之间的交互
OC和JS之间的交互 目录 对OC和JS之间交互的理解 JS调用OC OC调用JS 对OC和JS之间交互的理解 JS调用OC JS文件 function sendCommand(cmd,param){ ...
- 0c-39-ARC下单对象内存管理
1.ARC工作原理详述 ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只不过是在代码编译时为你自动在合适的位置插入release或autorelease A ...
- 分享一款页面视差滚动切换jquery.localscroll插件
今天给大家分享一款页面视差滚动切换jquery.localscroll插件. 滚动鼠标液动条看下页面的切换效果.该插件适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera ...