鱼C论坛

 找回密码
 立即注册
查看: 2610|回复: 0

[学习笔记] 《解密系列-系统篇》第八讲:PE结构详解8

[复制链接]
发表于 2018-1-21 15:26:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 shuiyu 于 2018-1-21 15:30 编辑

越努力,越幸运。欢迎大家来看我的笔记,不对的请各位大佬指正,谢谢

具体详情可到查看:http://blog.csdn.net/shuiyu486/article/details/79116713


一、输入表结构
(1)回顾一下,在 PE文件头的 IMAGE_OPTIONAL_HEADER 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表的。而输入表是以一个 IMAGE_IMPORT_DESCRIPTOR(简称IID) 的数组组成。每个被 PE文件链接进来的 DLL文件都分别对应一个 IID数组结构。在这个 IID数组中,并没有指出有多少个项(就是没有明确指明有多少个链接文件),但它最后是以一个全为NULL(0) 的 IID 作为结束的标志

(2)
IMAGE_IMPORT_DESCRIPTOR(IID) 结构定义如下:(占5个DWORD,即20个字节
  1. IMAGE_IMPORT_DESCRIPTOR STRUCT
  2.     union
  3.         Characteristics             DWORD   ?
  4.         OriginalFirstThunk        DWORD   ?
  5.     ends
  6.     TimeDateStamp                DWORD   ?
  7.     ForwarderChain                DWORD   ?
  8.     Name                              DWORD   ?
  9.     FirstThunk                        DWORD   ?
  10. IMAGE_IMPORT_DESCRIPTOR ENDS
复制代码

(3)重要的成员介绍:
第一个成员是一个union结构(联合类型或者说是共用体),在下面两个任选一个(就是说这两个元素不能同时存在),一般是选第二个(即:OriginalFirstThunk)。
  1. Characteristics    DWORD   ?
  2. OriginalFirstThunk        DWORD   ?
复制代码


OriginalFirstThunk
它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function name 的地址。

FirstThunk
它包含由IMAGE_THUNK_DATA定义的 first thunk数组的虚地址,通过loader用函数虚地址初始化thunk。在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。


二、OriginalFirstThunk 和 FirstThunk
(1)看图: (此图表示PE文件在磁盘中时(静态),OriginalFirstThunk(指向INT)和 FirstThunk(指向IAT) 都是指向IMAGE_IMPORT_BY_NAME的,下面会验证)
1.jpg

我们看到:OriginalFirstThunk 和 FirstThunk 他们都是两个类型为IMAGE_THUNK_DATA 的数组,它是一个指针大小的联合(union)类型。每一个IMAGE_THUNK_DATA 结构定义一个导入函数信息(即指向结构为IMAGE_IMPORT_BY_NAME 的家伙,这家伙稍后再议),然后数组最后以一个内容为0 的 IMAGE_THUNK_DATA 结构作为结束标志。

(2) IMAGE_THUNK_DATA 结构的定义如下:
  1. IMAGE_THUNK_DATA STRUC
  2.     union u1
  3.         ForwarderString       DWORD  ?        ; 指向一个转向者字符串的RVA
  4.         Function                  DWORD  ?        ; 被输入的函数的内存地址
  5.         Ordinal                    DWORD  ?        ; 被输入的API 的序数值
  6.         AddressOfData         DWORD  ?        ; 指向 IMAGE_IMPORT_BY_NAME
  7.     ends
  8. IMAGE_THUNK_DATA ENDS
复制代码


(3)我们可以看出由于是union结构,所以IMAGE_THUNK_DATA 事实上是一个双字(DWORD)大小。该结构在不同时候赋予不同的意义(伟大神奇不得鸟……)。其实union这种数据结构很容易理解:说白了就是当时穷,能省就省,再说白了,就是几兄弟姐妹轮流穿一条裤子去相亲!理解了吧?哈哈~

(4)那我们怎么来区分何时是何意义呢?
规定如下:
当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。
当 IMAGE_THUNK_DATA 值的最高位为 0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个 RVA,指向一个 IMAGE_IMPORT_BY_NAME 结构。(演示请看小甲鱼解密系列视频讲座)

(5)IMAGE_IMPORT_BY_NAME 结构仅仅只有一个字型数据的大小,存有一个输入函数的相关信息结构。其结构如下:
  1. IMAGE_IMPORT_BY_NAME STRUCT
  2.     Hint        WORD    ?
  3.     Name      BYTE      ?
  4. IMAGE_IMPORT_BY_NAME ENDS
复制代码


(6)结构中的 Hint 字段也表示函数的序号,不过这个字段是可选的,有些编译器总是将它设置为 0,Name 字段定义了导入函数的名称字符串,这是一个以 0 为结尾的字符串。


三、输入地址表(IAT)
(1)为什么由两个并行的指针数组同时指向 IMAGE_IMPORT_BY_NAME 结构呢?第一个数组(由 OriginalFirstThunk 所指向)是单独的一项,而且不能被改写,我们前边称为 INT。第二个数组(由 FirstThunk 所指向)事实上是由 PE 装载器重写的(IAT)。

(2)PE 装载器首先搜索 OriginalFirstThunk ,找到之后加载程序迭代搜索数组中的每个指针,找到每个 IMAGE_IMPORT_BY_NAME 结构所指向的输入函数的地址,然后加载器用函数真正入口地址来替代由 FirstThunk 数组中的一个入口,因此我们称为输入地址表(IAT)。所以,当我们的 PE 文件装载内存后准备执行时,刚刚的图就会转化为下图:
2.jpg

(3)上图表示PE文件在内存中时,OriginalFirstThunk 是指向IMAGE_IMPORT_BY_NAME的,而FirstThunk则指向了函数的真正地址,下面会验证~

四、输入表实例分析
(1)由上面的讲解和视频的描述,其实我们都知道了:
在磁盘中(静态)OriginalFirstThunk(指向INT) 和 FirstThunk(指向IAT)最后都是指向IMAGE_IMPORT_BY_NAME的,然后找到IMAGE_IMPORT_BY_NAME中的函数。

在内存中(动态)
FirstThunk指向的IAT直接装着函数的真正地址了~

(2)实例验证:在磁盘中(静态)
1.根据数据目录表的第二个成员就是指向输入表的,使用PEInfo工具,找到输入表的RVA(2A000),从而找到输入表的物理地址(28000)(方法过程我就不用我多说了吧
3.JPG

2.找到输入表的物理地址后;根据"而输入表是以一个 IMAGE_IMPORT_DESCRIPTOR(简称IID) 的数组组成,每个被 PE文件链接进来的 DLL文件都分别对应一个 IID数组结构"
根据IID结构,我们就可以找到OriginalFirstThunk和 FirstThunk,(一个在结构的第一个,另一个在结构的末尾)
OriginalFirstThunk:2A15C(这是一个RVA),物理地址为:2815C
FirstThunk:2A2AC(这是一个RVA),物理地址为:282AC
4.JPG

3.根据上面得到的物理地址,找到地址上的值(方法同上)
物理地址为:2815C    地址上的值问为:0002A2DC
物理地址为:282AC    地址上的值问为:0002A2DC
5.JPG

4.判断: 现在我们得到的值,其实就是IMAGE_THUNK_DATA 结构的值!根据:
当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。
当 IMAGE_THUNK_DATA 值的最高位为 0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个 RVA,指向一个 IMAGE_IMPORT_BY_NAME 结构。

现在的两个值都是为:0002A2DC,最高位为0,表示是一个RVA,则又要寻找物理地址(方法同上),得到物理地址为:282DC。根据物理地址我们看到了IMAGE_IMPORT_BY_NAME!!,因为在INT和IAT上的值都是0002A2DC,所以也证明了:OriginalFirstThunk(指向INT) 和 FirstThunk(指向IAT)最后都是指向IMAGE_IMPORT_BY_NAME的
6.JPG


(3)实例验证:在内存中(动态)
1.使用LordPE把我们的实验程序(Hello.exe)在运行中,从内存里dump(转存)出来,得到dumped_hello.exe。(方法自己看视频去

2.使用PEInfo工具,找到输入表的RVA(2A000),从而找到输入表的物理地址(2A000)(方法过程我就不用我多说了吧

3. 根据IID结构,我们就可以找到 FirstThunk,(一个在结构的第一个,另一个在结构的末尾,这里我们值找FirstThunk
FirstThunk:2A2AC(这是一个RVA),物理地址为:2A2AC

4.根据上面得到的物理地址,找到地址上的值(方法同上)
物理地址为:282AC    地址上的值问为:77D507EA
7.JPG

5.判断
当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。
证明了:在内存中(动态)FirstThunk指向的IAT直接装着函数的真正地址了~




谢谢小甲鱼带来的视频教程,感谢!!
本节结束,多谢览阅!
越努力,越幸运。谢谢大家来看我的笔记,不对的请各位大佬指教,谢谢

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-28 01:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表