我们知道PE 文件中的数据被载入内存后根据不同页面属性被划分成很多区块(节),并有区块表(节表)的数据来描述这些区块。这里我们需要注意的问题是:一个区块中的数据仅仅只是由于属性相同而放在一起,并不一定是同一种用途的内容。例如接着要讲的输入表、输出表等就有可能和只读常量一起被放在同一个区块中,因为他们的属性都是可读不可写的。

其次,由于不同用途的数据有可能被放入同一个区块中,因此仅仅依靠区块表是无法确定和定位的。那要怎么办?对了,PE 文件头中 IMAGE_OPTIONAL_DEADER32 结构的数据目录表来指出他们的位置,我们可以由数据目录表来定位的数据包括输入表、输出表、资源、重定位表和TLS等15 种数据。(数据目录表,不要以为他在前边出现就不重要哦~)

这节课我们谈的是输入表,为什么需要输入表呢?因为我们从数据目录表得到的仅仅是一些指定数据的RVA 和数据块的尺寸,很明显,不同的数据块中的数据组织方式(结构)是显然不同的,例如输入表和资源数据块中的数据就完全是牛马不相及的两个东西。因此,我们想要深入了解PE 文件就必须了解这些数据的组织方式,以及了解系统是如何处理调用它们的。

输入函数

在代码分析或编程中经常遇到“输入函数(Import Functions,也称导入函数)”的概念。这里我们就来解释下,输入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于相关的DLL 文件中,在调用者程序中只保留相关的函数信息(如函数名、DLL 文件名等)就可以。对于磁盘上的PE 文件来说,它无法得知这些输入函数在内存中的地址,只有当PE 文件被装入内存后,Windows 加载器才将相关DLL 装入,并将调用输入函数的指令和函数实际所处的地址联系起来。这就是“动态链接”的概念。动态链接是通过PE 文件中定义的“输入表”来完成的,输入表中保存的正是函数名和其驻留的DLL 名等。

实例预演(视频中将演示,这里只能截图)

以上是咱这次实验的小青蛙哈~灰常简单的一个小程序,如图双击程序只显示一个对话窗口,然后就结束~试验用小程序,我们尽量的将内部的结构删减,调试起来才方便些。我们这次体验的目的就是想靠所学的知识,试图来找到MessageBox 在内存中的地址。
注:MessageBox 是来自于USER32.DLL 动态链接库里的一个函数,我们通过对PE 文件的静态反编译分析来观察hello.exe 这个试验品是如何定位和调用MessageBox 这个在“异乡”的函数哈。
(MessageBox 有两个版本,一个是MessageBoxA 还有一个是MessageBoxW 分别带便ASCII码形式和UNICODE)

体验开始:
1. 我们用曾经号称为屠龙刀的W32DAM 对hello.exe进行反编译,如图:

我们可以看到这个程序只有两个导入模块(Import Module),分别导入来自两个动态链接库(USER32.DLL和KERNEL32.DLL)的若干函数,我们还清晰可见,咱要跟踪的MessageBoxA 就在USER32.DLL 中,这里程序还自动给我们定位了它的虚拟地址:2A2DC,但我们不要用这个,因为我们说过这回我们是来探险的,凡事讲究人工……

我们通过W32DASM 的查找功能找出MessageBox 这个函数代码的位置,并试图查看他的汇编跳转~

push xxxx   push xxxx   push xxxx   push xxxx 之后再来一个Call xxxx ……
没错,这就是调用函数的一个标准形式。每个push 其实就是将所要调用的函数需要的参数入栈,为啥要入栈呢?这又要从地球的起源说起了……我这里就简单的说下吧:栈的发明,使得函数和子程序的出现成为可能!对于局部变量和参数,栈的特性最适合不过~不明白不急哈,这个是汇编语言和编译原理的范畴了,以后咱学习深入自然会碰到,到时候再来详细解决就可以。

int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType // style of message box );
由定义可见MessageBox 函数共有四个参数,因此我们四次push xxxx 分别将参数按照STDCALL 的方式入栈之后,就可以CALL MessageBox 这个函数了。好,既然是CALL 我们的目标函数了,通过反汇编我们就可以观察到它的地址是:[0042A2AC],难道就这么简单?42A2AC 就是目标函数的地址?

那我们就直接把程序往下拉,试图找找这个 42A2AC 的地址吧~
可是…… ……

我们悲剧的发觉,程序压根还没到 42A2AC 这地方,到了421FF8 就结束了!!什么情况呢?
或许……或许……或许……在没详细讲输入表时我们还不能直接给大家答案……

好吧,这里提问一下,这是一个什么地址呢?偏移地址 or 虚拟地址?

恩,没错,这是一个VA 哈,那这个VA 上节课我们说过可以换算成存放在实际物理内存上的方法。具体就是将这个VA 与该程序的各个区块的VA 地址逐一对比,由于PE 头文件有记载每个区块的VA 地址也同时记载着它的实际物理地址,因此我们可以通过判断该VA 位于哪个区块内并求出与区块VA 的差值进而求出该VA 的实际物理地址。那既然前边直直的路子走不通,我们就试下把他转换为物理地址试试吧?!

我们看到,咱的42A2AC 地址加载咱的2A000 和 2B000 之间(改程序VC编写,映像基地址为400000打头哦~),因此我们可以将该地址定位到改程序位于.idata 区块内。该区块的VA 起始地址为42A000,因此42A2AC - 42A000 = 2AC,raw data offs 跟我们说该区块的物理地址是28000,因此42A2AC 这个VA 所对应的物理偏移地址就是 28000 + 2AC = 282AC。

咱用UE 打开看下282AC 这偏移地址上有啥东西……

282AC 这个地址上存放着 DCA20200 这个数据,翻译成ASCII 码也是莫名其妙的说~
但我们把DCA20200 当成一个DWORD 类型的数据来读的话我们得到数据 0002A2DC(还记得大端与小端吧)
慢着,是不是很熟悉,又是2A****开头,跟咱之前的地址是不是差不多?那好,我们又按照刚才的方法转化为偏移地址试试,转化后得到的偏移地址是:282DC,咱再看看282DC 里边有啥神秘的东西吧?

哈哈,看到奇迹了吗?从282DC 地址读起,ASCII 码对应的值是MessageBoxA.USER32.dll

输入表结构

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

IMAGE_IMPORT_DESCRIPTOR STRUCT

union

Characteristics              DWORD   ? 
        OriginalFirstThunk        DWORD   ?

ends 
    TimeDateStamp                     DWORD   ? 
    ForwarderChain                     DWORD   ? 
    Name                                    DWORD   ? 
    FirstThunk                            DWORD   ?

IMAGE_IMPORT_DESCRIPTOR ENDS

成员介绍:

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

TimeDateStamp
该字段可以忽略。如果那里有绑定的话它包含时间/数据戳(time/data stamp)。如果它是0,就没有绑定在被导入的DLL中发生。在最近,它被设置为0xFFFFFFFF以表示绑定发生。

ForwarderChain
一般情况下我们也可以忽略该字段。在老版的绑定中,它引用API的第一个forwarder chain(传递器链表)。它可被设置为0xFFFFFFFF以代表没有forwarder。

Name
它表示DLL 名称的相对虚地址(译注:相对一个用null作为结束符的ASCII字符串的一个RVA,该字符串是该导入DLL文件的名称,如:KERNEL32.DLL)。

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

这个OriginalFirstThunk 和 FirstThunk明显是亲家,两家伙首先名字就差不多。那他们有什么不可告人的秘密呢?来,我们看下面一张图(画的很辛苦,大家仔细看哈):

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

我们得到 IMAGE_THUNK_DATA 结构的定义如下:
IMAGE_THUNK_DATA STRUC

union u1

ForwarderString       DWORD  ?           ; 指向一个转向者字符串的RVA
     Function                      DWORD  ?        ; 被输入的函数的内存地址
     Ordinal                       DWORD  ?         ; 被输入的API 的序数值
     AddressOfData         DWORD  ?           ; 指向 IMAGE_IMPORT_BY_NAME

ends

IMAGE_THUNK_DATA ENDS

我们可以看出由于是union结构,所以IMAGE_THUNK_DATA 事实上是一个双字大小。该结构在不同时候赋予不同的意义。

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

好,那接着我们讨论下指向的这个 IMAGE_IMPORT_BY_NAME 结构。IMAGE_IMPORT_BY_NAME 结构仅仅只有一个字型数据的大小,存有一个输入函数的相关信息结构。其结构如下:

IMAGE_IMPORT_BY_NAME STRUCT

Hint        WORD    ? 
Name      BYTE      ?

IMAGE_IMPORT_BY_NAME ENDS

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

输入地址表(IAT)

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

好了,那么 PE 装载器的核心操作时如何的呢?这里就给大家揭秘啦~
PE 装载器首先搜索 OriginalFirstThunk ,找到之后加载程序迭代搜索数组中的每个指针,找到每个 IMAGE_IMPORT_BY_NAME 结构所指向的输入函数的地址,然后加载器用函数真正入口地址来替代由 FirstThunk 数组中的一个入口,因此我们称为输入地址表(IAT)。所以,当我们的 PE 文件装载内存后准备执行时,刚刚的图就会转化为下图:

此时,输入表中其他部分就不重要了,程序依靠 IAT 提供的函数地址就可正常运行。

实践

静态

OriginalFirstThunk    INT

输入表地址为2A000

2A000为虚拟地址,28000为物理地址

下图就是导入表的地址。

2A15C-2A000+28000=2815C

2A2DC-2A000+28000=282DC,找到MessageBox函数

FirstThunk  IAT

2A2AC-2A000+28000=282AC

FirstThunk  IAT

动态

OriginalFirstThunk    INT

用LordPE jump full出动态的exe

win7 64位和win10上lordPE不好使,建议虚拟机XP上运行。

剩下的与静态分析一样方法。

注意一点:动态的虚拟地址与物理地址是一样的。

22. PE结构-PE详解之输入表(导入表)、屠龙刀W32Dasm(静态)、LordPE(动态)工具入门(查找dll、调用函数)相关推荐

  1. 25. PE结构-PE详解之资源

    资源结构 资源是PE 文件中非常重要的部分,几乎所有的PE 文件中都包含着资源,与导入表和导出表相比,资源的组织方式要复杂很多,其实我们只要看下图就知道俺所言不虚. 分3级:1 资源类型 ,2 资源I ...

  2. 21. PE结构-PE各个结构的基本概念

    exe与dll几乎没什么区别,唯一区别就是一个字段标识出这个文件是exe还是dll 32位叫PE32,64位叫PE32+ PE主要定义在winnt.h vc搜索:image format,定义PE结构 ...

  3. 压电加速度传感器的结构原理详解

    压电加速度传感器的结构原理详解 [摘要]简述了压电加速度传感器的结构原理.说明了该传感器灵敏度的线性度问题,分析了其正向反向灵敏度的差异与"饱和现象",以便在生产.鉴定与使用时加以 ...

  4. 【FFmpeg】结构体详解(二):AVStream、AVPacket、AVOutputFormat

    FFmpeg结构体详解 7.AVStream 8.AVPacket 9.AVOutputFormat 7.AVStream AVStream 是存储每一个视频/音频流信息的结构体. 重要的变量如下所示 ...

  5. 【FFmpeg】结构体详解(一):AVCodec、AVCodecContext、AVCodecParserContext、AVFrame、AVFormatContext 、AVIOContext

    FFmpeg结构体详解 一.FFmpeg中最关键的结构体之间的关系 1.解协议(http,rtsp,rtmp,mms) 2.解封装(flv,avi,rmvb,mp4) 3.解码(h264,mpeg2, ...

  6. php mysql修改命令_PHP编程:mysql alter table命令修改表结构实例详解

    <PHP编程:mysql alter table命令修改表结构实例详解>要点: 本文介绍了PHP编程:mysql alter table命令修改表结构实例详解,希望对您有用.如果有疑问,可 ...

  7. java 获取oracle表结构_Java导出oracle表结构实例详解

    Java导出oracle表结构实例详解 发布于 2020-7-20| 复制链接 摘记:  Java导出oracle表结构实例详解最近用到的,因为plsql是收费的,不让用,找了很多方法终于发现了这个. ...

  8. plsql导出表结构_mysqldump命令详解 Part 5-按条件备份表数据

    实验环境: MySQL 5.7.25 Redhat 6.10 前面我们建立了数据库并建立相关的对象 数据库 表 存储过程 函数 触发器 事件 今天的内容为按条件备份表数据 1. 备份语句 我们来备份t ...

  9. Siri详解之输入系统和活跃本体(转)

    Siri详解之输入系统和活跃本体(转) 文 / 张俊林 Siri是iPhone 4S内置的智能语音识别系统,吸引了许多用户的关注.本文将从技术层面详解Siri,主要讲述输入系统和活跃本体两部分内容. ...

  10. 计算机科学类专升本复习之“C语言结构体”详解(初稿)

    C语言结构体详解,C语言struct用法详解 前面所学到的"数组":它是一组具有"相同类型"的数据的集合. 但是在实际的编程中,我们往往还需要 一组" ...

最新文章

  1. android 47 service绑定
  2. java打出三角形乘法表_Java 练习(输出三角形,九九乘法表, 100以内的质数)
  3. 我的世界服务器里怎么无限随机传送,我的世界随机传送插件使用教程 权限指令分享...
  4. 环境搭建:Windows系统下Nacos集群搭建
  5. APP搜索如何又快又准?
  6. 在vs2008工程中制作cab包
  7. 【Linux】七种文件类型
  8. SlidingMenu使用笔记
  9. MySQL学习(五、数据操作语言DML和事务处理语言TCL)
  10. pdffactory 打印字体_PdfFactory(虚拟打印机)
  11. Redhat rpm常用命令以及如何配置yum软件仓库使用yum install
  12. 利用IPSec/L2TP代理上网
  13. 安防集成商的出路在哪里?
  14. 编程小白学python路线图_零基础Python学习路线图,让你少走弯路
  15. 2022-数据结构复习
  16. 【51单片机学习笔记】DS1302实时时钟程序
  17. 软件工程导论期末复习整理
  18. [华为matepad pro 12.6]实现平板编程(Xfce4篇)(Termux+Ubuntu21.04+Xfce4+VNC)
  19. 电效应和普朗克常量的测定
  20. pandas添加索引列名称

热门文章

  1. 高颜值可定制在线作图工具-第二版
  2. Mac电视投屏工具:Mirror for Any Device for Mac
  3. html用函数输出nn乘法表,第5讲 PHP编程.ppt
  4. python实现一个简单的加法计算器_Python简易项目 加减计算器的实现
  5. json符号解释大全_水电图纸图例大全,电气、弱电、给排水常用图例
  6. 微信小程序多图上传带进度提示的代码实例
  7. STM32H743+CubeMX-ADC+DMA采样四路AD
  8. Qt工作笔记-在界面上输出变量、类、this指针的地址
  9. HTML5超链接和多媒体,IT兄弟连 HTML5教程 多媒体应用 创建图像和链接
  10. gcc编译c文件 python.h 未找到