【逆向基础】十八、PE文件格式(三)

一、简介

文本章主要讲结构体IMAGE_DATA_DIRECTORY数组。它制定了各种数据目录的地址与大;PE装载器可以通过这些信息准确加载PE文件所需的函数,资源等;此外,数据目录表也是设置钩子,注入等逆向的理论基础。所以学习这个结构体数组对于想学逆向的小伙伴是非常重要的(ps:头文件中所有地址均为RVA);
在这里插入图片描述

二、结构体:IMAGE_DATA_DIRECTORY

IMAGE_DATA_DIRECTORY结构体数组表示数据目录表(Data Directory Table)。其数组实际大小由IMAGE_OPTIONAL_HEADER32结构体中的变量NumberOfRvaAndSizes决定;每个结构体都对应PE文件中的一个数据目录的地址和大小;

typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress;DWORD Size;
}IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

在这里插入图片描述

VirtualAddress:

VirtualAddress:表示数据目录项在PE文件中的相对虚拟地址(Relative Virtual Address)。RVA是一个相对于PE文件加载到内存后的基地址的偏移量,用于定位数据在内存中的位置。

Size:

Size:表示数据目录对应表的大小。

三、结构体:IMAGE_IMPORT_DESCRIPTOR

每个IMAGE_IMPORT_DESCRIPTOR结构体占用20个字节;这些字节用于告诉操作系统当前导入dll何时被修改,去哪里调用函数,去哪找到函数名,是否被绑定等;每dll对应一个结构体,多个导入dll对应了结构体数组;PE文件中的导入表是逆向和病毒分析中比较重要的一个表,所以我们来一起学习学习;
(ps:dll相关内容可去这里瞧瞧:dll小课堂)

typedef struct _IMAGE_IMPORT_DESCRIPTOR {union {DWORD   Characteristics;    //特征DWORD   OriginalFirstThunk; //输入名称表(INT)的RVA};DWORD   TimeDateStamp;          //时间戳DWORD   ForwarderChain;         //函数转发情况 DWORD   Name;                   //DLL名字的指针DWORD   FirstThunk;             //输入地址表(IAT)的 RVA
} IMAGE_IMPORT_DESCRIPTOR;

3.1 成员变量介绍

Characteristics/OriginalFirstThunk

Characteristics:特征值
OriginalFirstThunk:表示当前导入文件INT(Import Name Table)首地址;详情下文INT讲解;

TimeDateStamp

TimeDateStamp:表示时间戳,表示当前导入dll的被绑定时间;

ForwarderChain

ForwarderChain:表示函数转发的状态

Name

Name:表示当前文件的名称地址,执行导入函数所属的库名称,遇到0x00表示结束;;

FirstThunk

FirstThunk:表示当前导入文件IAT(Import Address Table)首地址;详情下文IAT讲解;
在这里插入图片描述

3.2、IAT(Import Address Table:导入函数地址表)

IAT表示导入地址表,此表用于存储从其它dll(Dynamic Link Library:动态链接库)中导入函数的实际入口地址; PE文件运行是就可以通过IAT表中的函数指针直接调用导入函数。导出地址表每个元素都存放了一个函数指针,遇到全零时表示结束;

找导入地址表过程如下

步骤一:查找IAT表的地址;如图所示,VirtualAddress = 0x19000 (实际上0x19000RAV值);
在这里插入图片描述

步骤二:查找地址所在节区范围,如图所示,在节区.idata的范围内,即PointerToRawData = 0x7000
在这里插入图片描述

步骤三:计算FOA(File Offset Address)的值
由于RVA = 0x19000,与导入地址表的地址偏差值为0,所以FOA = PointerToRawData = 0x7000;

步骤四:去文件找对应地址,如果所示即为导入地址表;遇到全零的地址表示结束;
如果所示即为某dll文件导入函数时的函数地址;
在这里插入图片描述
步骤五:PE加载器在加载PE文件时,会根据INT中的函数信息在对应的DLL中查询实际函数的地址,并更新IAT中的条目为这些函数的实际地址。也就是说IAT的初始值会在加载时被改变;

3.3、INT(Import Name Table:导入函数名称表)

数据目录数组中,单独的下标表示INT,但这是让我们更好理解PE格式的好东西,所以一起学习学习吧;它是如下结构体的数组;每一个元素值都指向一个结构体地址;

typedef struct _IMAGE_IMPORT_BY_NAME {WORD    Hint;BYTE*    Name;
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
3.3.1 成员变量介绍
Hint

Hint可以认为是函数在PE文件中的唯一标识ID;用两个字节表示;

Name

Name表示一个字符指针,遇到0x00表示字符结束;

找导入函数名称表过程如下:

IMAGE_DATA_DIRECTORY数组中无INT的下标,找到INT表需要通过导入表中的指针OriginalFirstThunk来确定对应导入dll中的函数名称表地址;
步骤一:找到任意导入文件的导入表地址
在这里插入图片描述
步骤二:选取任意一张导入表中的OriginalFirstThunk来确定对应导入dll中的函数名称表地址;取第一个OriginalFirstThunk = 0x00019250;根据值判断在那个节区范围内;如果说是,导入名称表的地址在节区.idata的范围内;
在这里插入图片描述
步骤三:计算FOA(File Offset Address)的值
由于VA = 0x19000;PointerToRawData = 0x7000;RVA = 0x19250;
所以RVA - 0x19000 = FOA - PointerToRawData;
FOA = 19250 - 19000 + 7000;
FOA = 0x7250;

步骤四:去文件找对应地址,如果所示即为导入函数名称表;遇到全零的地址表示结束;
在这里插入图片描述
步骤五:根据INT表中每个元素的值,找到对应导入函数名称结构体位置;例如找到第一个结构体IMAGE_IMPORT_BY_NAME的地址是0x00019392;通过RVA计算FOA得到
FOA = 0x7392(步骤与前面一样,此处省略);得到对应的Hint = 0x0031;name = " __vcrt_LoadLibraryExW"
在这里插入图片描述
步骤六:从步骤四开始遍历INT表,可得到当前导入dll文件中的所有导入函数及其函数ID
在这里插入图片描述

3.4、INT 与 IAT 的关联

INT(Import Name Table)IAT(Import Address Table)均使用数组存储dll的信息;
数组的特点如下:

1、均未指出数组大小,且都已Null表示结束(指针为null);
2、INT的数组大小应该与IAT数组大小相同;
3、IAT表的数组值会在文件加载时,根据INT的信息更新为函数的实际地址。

通过INT 输入IAT的值步骤如下:

1.读取IID的Name成员,获取库名称字符串(“kernel32.dll”)。
2.装载相应库。 →LoadLibrary("kernel32.dll")
3.读取IIDOriginalFirstThunk成员,获取INT地扯。
4,逐一读取INT中数组的值,获取相应IMAGE_IMPORT_BY_NAME地址(RVA)
5·使用IMAGE-IMPORT-BY-NAME的Hint(ordinal)Name项,获取相应函数的起始地址。 →GetProcAddress("GetCurrentThreadld")
6.读取IIDFirstThunk(IAT)成员,获得IAT地址。
7.将上面获得的函数地址输入相应IAT数组值。
8,重复以上步骤4-7,直到INT结束(遇到NULL时)。

四、结构体:_IMAGE_EXPORT_DIRECTORY

IMAGE_EXPORT_DIRECTORY结构体定义了导出表的结构,它包含了DLL文件中导出的函数和变量的相关信息。导出表的主要作用是将PE文件中存在的函数引出到外部,以便其他程序或模块可以使用这些函数,实现代码的重用。通过导出表,DLL文件可以向系统提供导出函数的名称、序号和入口地址等信息,以便PE加载器通过这些信息来完成动态链接的整个过程,且PE文件中仅有一个IMAGE_EXPORT_DIRECTORY结构体即可描述导出信息。其定义如下
ps:因为exe文件导出函数表为空,所以使用自己编写的dll进行讲解

typedef struct _IMAGE_EXPORT_DIRECTORY {DWORD   Characteristics; 	    //标志,未用DWORD   TimeDateStamp; 		    //时间戳 WORD    MajorVersion; 		    //未用WORD    MinorVersion;		    //未用DWORD   Name;					//指向该导出表的文件字符串DWORD   Base;					//导出函数的起始序号DWORD   NumberOfFunctions;	    //所有的导出函数个数DWORD   NumberOfNames;		    //以函数名导出的函数个数DWORD   AddressOfFunctions;     // 所有导出函数地址表RVADWORD   AddressOfNames;         // 函数名称地址表RVADWORD   AddressOfNameOrdinals;  // 函数序号地扯表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

在这里插入图片描述

4.1 成员变量介绍

TimeDateStamp

TimeDateStamp表示dll文件的时间信息;值用秒来表示;

Name

Name表示库文件名称地址

NumberOfNames

NumberOfNames表示当前文件包含以函数名称方式导出的函数总数;(ps:不包括以序号方式导出的函数);

NumberOfFunctions

NumberOfFunctions表示当前文件包含以函数名称和序号两种方式导出的函数总数;即所有导出函数的总数;

AddressOfFunctions

AddressOfFunctions表示一个指向所有导出函数地址的地址表;此表包含了所有导出函数(函数名和序号的方式)的入口点地址;

AddressOfNames

AddressOfNames表示一个指向导出函数名称地址的地址表;

AddressOfNameOrdinals

AddressOfNameOrdinals表示一个指向导出函数名称序号地址的地址表;与AddressOfNames表相对应;

4.2、EAT(Export Address Table:导出函数地址表)

EAT(Export Address Table)表示导出函数地址表,对应属性中的AddressOfFunctions;实际上是一个四个字节类型的指针数组;指针指向了实际的导出函数地址(RVA:Relate Virtual Address);

4.3、ENT(Export Name Table:导出函数名称表)

AddressOfNames表示一个指向导出函数名称的地址表;此表进包含了已名称方式导出的函数名地址;不包括已序号导出的函数地址;
导出函数名称表也有类似的结构图如下;单HintName分别使用AddressOfNameOrdinalsAddressOfNames进行存储了;

typedef struct _IMAGE_IMPORT_BY_NAME {WORD    Hint;BYTE*    Name;
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

4.4、ENT与EAT的关联

当一个程序需要动态加载并调用某个DLL中的函数时,它首先会查找该DLL的导出表。然后根据函数名在AddressOfNames指向的列表中进行查找匹配的函数名,使用AddressOfNameOrdinals中对应的序号在AddressOfFunctions指向的列表中查找函数的入口地址。最后,程序使用这个入口地址来调用所需的函数。

找导出对应函数地址过程如下

举例:我们要找出导出函数myAdd的地址,步骤如下
步骤一找导出函数表地址;即找到IMAGE_DATA_DIRECTORY数组中下标为0的元素;其VirtualAddress元素对应的值即导出函数表的RVA(Relate Virtual Address);根据导出函数结构体_IMAGE_EXPORT_DIRECTORY解析对应信息;

在这里插入图片描述
在这里插入图片描述

步骤二找所有导出函数名字符串;属性AddressOfNames=0x17D90的值为部分函数名称地址表地址;属性NumberOfNames的值为2,表示此表有两个导出函数名;

取两个值分别对应两个函数的名称地址值RVA_1 = 0x17D90,RVA_2= 0x17D94;根据计算得出RAW_1 = 0x6B90,RAW_2 = 0x6B94;
在这里插入图片描述
这处地址的值即存储函数名的实际地址值RVA_1 = 0x17DA8,RVA_2 = 0x17DAE;根据计算得出RAW_1 = 0x6BA8,RAW_2 = 0x6BAE;
在这里插入图片描述
根据函数名地址,找到如下两个函数名字符串(遇到0x00表示结束);
在这里插入图片描述
(ps:此处函数索引是从0开始自增1)

步骤三找指定函数名称地址下标
例如:myAdd,就需要先找到函数在AddressOfFunctions数组中的下标ordinal;
如果我们要找myAdd的导出函数地址,则需要先找到它对应的ordinal;
属性AddressOfNameOrdinals=0x17d98的值为函数名称序号地址表地址;属性NumberOfNames的值为2,表示此表有两个导出函数名序号;

取两个值分别对应两个函数的名称序号地址RVA_1 = 0x17D98,RVA_2 = 0x17D9A;根据计算得出RAW_1 = 0x6B98,RAW_2 = 0x6B9A;
在这里插入图片描述
根据函数名序号地址,找到如下两个函数名序号值对应ordinals_1 = 0x0000;ordinals_2 = 0x0002
在这里插入图片描述
(ps:此处函数索引是从0开始自增1)

步骤四:找到所有函数地址表,属性NumberOfFunctions = 0x17D88的值为所有函数名称地址表地址;属性NumberOfFunctions的值为所有函数名称地址表地址;属性NumberOfNames的值为2,表示此表有两个导出函数名;

取两个值分别对应两个函数的名称地址值RVA_1 = 0x17D88,RVA_2= 0x17D8C;根据计算得出RAW_1 = 0x6B88,RAW_2 = 0x6B8C;
在这里插入图片描述

这处地址的值即存储函数名的实际地址值RVA_1 = 0x11127,RVA_2 = 0x11159;根据计算得出RAW_1 = 0x0527,RAW_2 = 0x0559;
在这里插入图片描述
在这里插入图片描述
根据函数名地址,找到EAT数组如下
FunctionAddress[0] = 0x000444E9;
FunctionAddress[1] = 0x000452E9;
在这里插入图片描述
(ps:此处函数索引是从0开始自增1)

步骤5根据寻找指定函数的下标,来确认对应函数的地址;

若我们查询myAdd函数的地址,则由表1可知函数myAdd索引为0,对应表2索引0的ordinal的值为0x0000;
去表3查找下标为0x0000的地址即为myAdd函数的地址,即0x0x000444E9;

综上所述,我们已经获取了三张表;
表1:AddressOfNames指向的以函数名称方式导出的函数名称表;大小为NumberOfNames
在这里插入图片描述
表2:AddressOfNameOrdinals指向的所有函数名称序号表;大小为NumberOfFunctions
在这里插入图片描述

表3:AddressOfFunctions指向的所有函数地址表;大小为NumberOfFunctions; 其中

FunctionAddress[0] = 0x000444E9;
FunctionAddress[1] = 0x000452E9;
在这里插入图片描述

五、小结

文章主要讲解了讲结构体IMAGE_DATA_DIRECTORY数组中15种数据目录中的导出表和导出表;这两种表是我们深入理解PE文件格式的关键,有助于我们想更深入的逆向分析前进;假以时日,我相信理解完所有表格的我们,也会像很多前辈一样,可以修改或者保护我们的PE文件,以达到加壳,防止逆向等效果。学海无涯,且行且珍惜呀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/464057.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Session条件竞争--理论

条件竞争 多个线程同时访问一个共享变量或文件时,由于线程的执行顺序不符合预期而导致最后的执行结果不符合开发者的预期。 session session,被称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间…

Centos8安装软件失败更换镜像源

问题 在Centos 8上安装git,报错如下: sudo dnf install git -y Repository extras is listed more than once in the configuration CentOS Linux 8 - AppStream 0.0 B/s …

如何让网页中的图片不可下载,让文字不可选中/复制

使用css中的伪属性来完成这个操作. 效果展示 文字不可复制: 图中这几个中文字符无法被选中,双击前面这几个字也只能选中后面的英文内容,无法选中也就无法复制. 既然常规方式无法选中,那打开浏览器开发者工具总能复制吧! 我经常这样干, 但是很遗憾,页面检查中根本就没那些内容…

Linux 之 信号概念、进程、进程间通信、线程、线程同步

学习任务: 1、 信号:信号的分类、进程对信号的处理、向进程发送信号、信号掩码 2、 进程:进程与程序的概念、进程的内存布局、进程的虚拟地址空间、fork创建子进程、wait监视子进程 3、 学习进程间通信(管道和FIFO、信号、消息队列…

Jmeter——结合Allure展示测试报告

在平时用jmeter做测试时,生成报告的模板,不是特别好。大家应该也知道allure报告,页面美观。 先来看效果图,报告首页,如下所示: 报告详情信息,如下所示: 运行run.py文件,…

ElasticSearch - Bucket Script 使用指南

文章目录 官方文档Bucket Script 官文1. 什么是 ElasticSearch 中的 Bucket Script?2. 适用场景3. Bucket Script 的基本结构4. 关键参数详解5. 示例官方示例:计算每月 T 恤销售额占总销售额的比率百分比示例计算:点击率 (CTR) 6. 注意事项与…

java、excel表格合并、指定单元格查找、合并文件夹

#创作灵感# 公司需求 记录工作内容 后端:JAVA、Solon、easyExcel、FastJson2 前端:vue2.js、js、HTML 模式1:合并文件夹 * 现有很多文件夹 想合并全部全部的文件夹的文件到一个文件夹内 * 每个部门发布的表格 合并全部的表格为方便操作 模…

【初阶数据结构篇】链式结构二叉树(二叉链)的实现(感受递归暴力美学)

文章目录 须知 💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力! 👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗&#xff1…

aws(学习笔记第十课) 对AWS的EBS如何备份(snapshot)以及使用snapshot恢复数据,AWS实例存储

aws(学习笔记第十课) 对AWS的EBS如何备份(snapshot)以及使用snapshot,AWS实例存储 学习内容: 对AWS的EBS如何备份AWS实例存储EBS和实例存储的不足 1. 对AWS的EBS如何备份(snapshot)以及使用snapshot恢复数…

适用于 c++ 的 wxWidgets框架源码编译SDK-windows篇

本文章记录了下载wxWidgets源码在windows 11上使用visual Studio 2022编译的全过程,讲的不详细请给我留言,让我知道错误并改进。 本教程是入门级。有更深入的交流可以留言给我。 如今互联网流行现在大家都忘记了这块桌面的开发,我认为桌面应用还是有用武之地,是WEB无法替代…

Pycharm贪吃蛇小游戏后续2

前文中我们提到用面向对象去编写贪吃蛇 目前功能实现贪吃蛇吃食物,身体加长,其次可以按下-(键盘上右分大小写的-,不是数字的-)来改变speed,终端可以看到速度,后续将陆续实现撞墙死亡&#xff0…

你丢失的数据,10款数据恢复软件帮你找!!

现实与虚拟的交错,互联网的进步,加大了我们之间交流的效率,而且便便捷了许许多多的事,比如信息保存;今天咱们来聊聊数据恢复这个话题。你是不是会一不小心删除了重要文件?硬盘出了问题,数据不见…

ArcGIS005:ArcMap常用操作101-150例动图演示

摘要:本文涵盖了GIS软件操作的多方面内容,包括地图文档的新建、打开、保存及版本兼容性处理;错误与警告的查阅及帮助文档的使用技巧;地图打印比例尺的调整与地图信息的完善;图层操作的撤销与恢复,界面元素的…

算法【Java】—— 动态规划之斐波那契数列模型

动态规划 动态规划的思路一共有五个步骤: 状态表示:由经验和题目要求得出,这个确实有点抽象,下面的题目会带大家慢慢感受状态标识状态转移方程初始化:避免越界访问 dp 表,所以在进行填表之前我们要预先填…

【学习】软件测试中的过程管理为何如此重要

在软件世界的繁华盛景之中,无数代码编织成了璀璨的星空,而每一颗闪烁的星点背后,都离不开精心的过程管理来确保其光华不减。正如一座摩天大楼需要稳固的地基与精细的设计图一样,软件的成功问世同样依赖于严谨、系统的流程管控。本…

深入学习 Scrapy 框架:从入门到精通的全面指南

深入学习 Scrapy 框架:从入门到精通的全面指南 引言 在数据驱动的时代,网络爬虫成为了获取信息的重要工具。Scrapy 是一个强大的 Python 爬虫框架,专为快速高效地提取网页数据而设计。本文将深入探讨 Scrapy 的使用,从基础知识到…

【Python】【数据可视化】【商务智能方法与应用】课程 作业一 飞桨AI Studio

作业说明 程序运行和题目图形相同可得90分,图形显示有所变化,美观清晰可适当加分。 import matplotlib.pyplot as plt import numpy as npx np.linspace(0, 1, 100) y1 x**2 y2 x**4plt.figure(figsize(8, 6))# yx^2 plt.plot(x, y1, -., labelyx^2,…

Postgresql源码(137)执行器参数传递与使用

参考 《Postgresql源码(127)投影ExecProject的表达式执行分析》 0 总结速查 prepare p_04(int,int) as select b from tbl_01 where a $1 and b $2为例。 custom计划中,在表达式计算中使用参数的值,因为custom计划会带参数值&…

自适应对话式团队构建,提升语言模型代理的复杂任务解决能力

人工智能咨询培训老师叶梓 转载标明出处 如何有效利用多个大模型(LLM)代理解决复杂任务一直是一个研究热点。由美国南加州大学、宾夕法尼亚州立大学、华盛顿大学、早稻田大学和谷歌DeepMind的研究人员联合提出了一种新的解决方案——自适应团队构建&…

GitHub上传自己的项目

目录 一、安装Git插件 1)下载 2)安装 二、创建Gothub的创库 三、通过Git上传本地文件到Github 四、其他 1、部分指令 2、如果已经运行过git init并设置了[user],下次可以直接用 一、安装Git插件 1)下载 下载地址&#x…