PE结构之RVA转换成FOA(3)

news/2024/4/30 0:17:13/文章来源:https://www.cnblogs.com/henry666/p/16623182.html

RVA转FOA

简介

PE文件有两种状态, 一种是在文件中的状态,另外一种是在内存中展开

若我们运行了一个PE文件且知道了某个全局变量的地址, 那么该如何通过这个全局变量地址来获得此变量在文件状态下的地址是什么呢?

RVA(relative Virtual Address), 又称为相对虚拟偏移,简单来说就是在内存状态下的偏移地址

FOA(File Ofseet Address), 又称为文件偏移地址, 就是在文件状态下的偏移地址

下图是PE文件在文件对齐和内存对齐状态下的映像结构图

这里文件对齐值是200,内存对齐的值是1000

内存对齐后的映像分布有个明显的拉伸

请添加图片描述


计算方法

1.求RVA的值

RVA = 全局变量在内存中的地址 - ImageBase(基址)

2.判断RVA是否位于PE头中或者内存对齐=文件对齐

如果RVA位于PE头中,则RVA = FOA

如果文件对齐 = 内存对齐,则 RVA = FOA

如果不在则进行下述操作

3.判断RVA位于哪个节

假设rva位于X节中,也就是说X节.VirtualAddress <= RVA <= X节.VirtualAddress+X节内存对齐后的大小

差值 = RVA - X节.VirtualAddress

4.得出FOA

FOA = X节.PointerToRawData(X节在文件中的地址) + 差值


C++代码

代码出处:https://www.52pojie.cn/thread-1408576-1-1.html

// PE.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <malloc.h>
#include <windows.h>
#include <winnt.h>
#include <math.h>
//在VC6这个比较旧的环境里,没有定义64位的这个宏,需要自己定义,在VS2019中无需自己定义
#define IMAGE_FILE_MACHINE_AMD64  0x8664//VA转FOA 32位
//第一个参数为要转换的在内存中的地址:VA
//第二个参数为指向dos头的指针
//第三个参数为指向nt头的指针
//第四个参数为存储指向节指针的数组
UINT VaToFoa32(UINT va, _IMAGE_DOS_HEADER *dos,_IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) {//得到RVA的值:RVA = VA - ImageBaseUINT rva = va - nt->OptionalHeader.ImageBase;//输出rvaprintf("rva:%X\n", rva);//找到PE文件头后的地址 = PE文件头首地址+PE文件头大小UINT PeEnd = (UINT)dos->e_lfanew+sizeof(_IMAGE_NT_HEADERS);//输出PeEndprintf("PeEnd:%X\n", PeEnd);//判断rva是否位于PE文件头中if (rva < PeEnd) {//如果rva位于PE文件头中,则foa==rva,直接返回rva即可printf("foa:%X\n", rva);        return rva;}else {//如果rva在PE文件头外//判断rva属于哪个节int i;for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {//计算内存对齐后节的大小UINT SizeInMemory = ceil((double)max((UINT)sectionArr[i]->Misc.VirtualSize ,(UINT)sectionArr[i]->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment;if (rva >= sectionArr[i]->VirtualAddress && rva < (sectionArr[i]->VirtualAddress + SizeInMemory)) {//找到所属的节//输出内存对齐后的节的大小printf("SizeInMemory:%X\n", SizeInMemory);break;}}if (i >= nt->FileHeader.NumberOfSections) {//未找到printf("没有找到匹配的节\n");return -1;}else {//计算差值= RVA - 节.VirtualAddressint offset = rva - sectionArr[i]->VirtualAddress;//FOA = 节.PointerToRawData + 差值int foa = sectionArr[i]->PointerToRawData + offset;printf("foa:%X\n", foa);return foa;}}}//VA转FOA 64位
//第一个参数为要转换的在内存中的地址:VA
//第二个参数为指向dos头的指针
//第三个参数为指向nt头的指针
//第四个参数为存储指向节指针的数组
UINT VaToFoa64(UINT va, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) {//得到RVA的值:RVA = VA - ImageBaseUINT rva = va - nt->OptionalHeader.ImageBase;//输出rvaprintf("rva:%X\n", rva);//找到PE文件头后的地址 = PE文件头首地址+PE文件头大小UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64);//输出PeEndprintf("PeEnd:%X\n", PeEnd);//判断rva是否位于PE文件头中if (rva < PeEnd) {//如果rva位于PE文件头中,则foa==rva,直接返回rva即可printf("foa:%X\n", rva);return rva;}else {//如果rva在PE文件头外//判断rva属于哪个节int i;for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {//计算内存对齐后节的大小UINT SizeInMemory = ceil((double)max((UINT)sectionArr[i]->Misc.VirtualSize ,(UINT)sectionArr[i]->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment;if (rva >= sectionArr[i]->VirtualAddress && rva < (sectionArr[i]->VirtualAddress + SizeInMemory)) {//找到所属的节//输出内存对齐后的节的大小printf("SizeInMemory:%X\n", SizeInMemory);break;}}if (i >= nt->FileHeader.NumberOfSections) {//未找到printf("没有找到匹配的节\n");return -1;}else {//计算差值= RVA - 节.VirtualAddressint offset = rva - sectionArr[i]->VirtualAddress;//FOA = 节.PointerToRawData + 差值int foa = sectionArr[i]->PointerToRawData + offset;printf("foa:%X\n", foa);return foa;}}}
int main(int argc, char* argv[])
{//创建DOS对应的结构体指针_IMAGE_DOS_HEADER* dos;//读取文件,返回文件句柄HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\GlobalVariety.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);//根据文件句柄创建映射HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, 0);//映射内容LPVOID pFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);//类型转换,用结构体的方式来读取dos = (_IMAGE_DOS_HEADER*)pFile;//输出dos->e_magic,以十六进制输出printf("dos->e_magic:%X\n", dos->e_magic);//创建指向PE文件头标志的指针DWORD* peId;//让PE文件头标志指针指向其对应的地址=DOS首地址+偏移peId = (DWORD*)((UINT)dos + dos->e_lfanew);//输出PE文件头标志,其值应为4550,否则不是PE文件printf("peId:%X\n", *peId);//创建指向可选PE头的第一个成员magic的指针WORD* magic;//让magic指针指向其对应的地址=PE文件头标志地址+PE文件头标志大小+标准PE头大小magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER));//输出magic,其值为0x10b代表32位程序,其值为0x20b代表64位程序printf("magic:%X\n", *magic);//根据magic判断为32位程序还是64位程序switch (*magic) {case IMAGE_NT_OPTIONAL_HDR32_MAGIC:{printf("32位程序\n");//确定为32位程序后,就可以使用_IMAGE_NT_HEADERS来接收数据了//创建指向PE文件头的指针_IMAGE_NT_HEADERS* nt;//让PE文件头指针指向其对应的地址nt = (_IMAGE_NT_HEADERS*)peId;printf("Machine:%X\n", nt->FileHeader.Machine);printf("Magic:%X\n", nt->OptionalHeader.Magic);//创建一个指针数组,该指针数组用来存储所有的节表指针//这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明了一个动态数组_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**) malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);//创建指向块表的指针_IMAGE_SECTION_HEADER* sectionHeader;//让块表的指针指向其对应的地址sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS));//计数,用来计算块表地址int cnt = 0;//比较 计数 和 块表的个数,即遍历所有块表while(cnt< nt->FileHeader.NumberOfSections){//创建指向块表的指针_IMAGE_SECTION_HEADER* section;//让块表的指针指向其对应的地址=第一个块表地址+计数*块表的大小section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER)*cnt);//将得到的块表指针存入数组sectionArr[cnt++] = section;//输出块表名称printf("%s\n", section->Name);}VaToFoa32(0x4198B0,dos, nt, sectionArr);break;}case IMAGE_NT_OPTIONAL_HDR64_MAGIC:{printf("64位程序\n");//确定为64位程序后,就可以使用_IMAGE_NT_HEADERS64来接收数据了//创建指向PE文件头的指针_IMAGE_NT_HEADERS64* nt;nt = (_IMAGE_NT_HEADERS64*)peId;printf("Machine:%X\n", nt->FileHeader.Machine);printf("Magic:%X\n", nt->OptionalHeader.Magic);//创建一个指针数组,该指针数组用来存储所有的节表指针//这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明了一个动态数组_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);//创建指向块表的指针_IMAGE_SECTION_HEADER* sectionHeader;//让块表的指针指向其对应的地址,区别在于这里加上的偏移为_IMAGE_NT_HEADERS64sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS64));//计数,用来计算块表地址int cnt = 0;//比较 计数 和 块表的个数,即遍历所有块表while (cnt < nt->FileHeader.NumberOfSections) {//创建指向块表的指针_IMAGE_SECTION_HEADER* section;//让块表的指针指向其对应的地址=第一个块表地址+计数*块表的大小section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);//将得到的块表指针存入数组sectionArr[cnt++] = section;//输出块表名称printf("%s\n", section->Name);}break;}default:{printf("error!\n");break;}}return 0;
}

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

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

相关文章

倒计时1天!计算巢软件免费试用中心发布,即刻报名!

免费、快速、开箱即用的云上应用新体验。免费、快速、开箱即用的云上应用新体验。计算巢即将重磅推出软件免费试用中心,为客户提供自动化交付的 ISV 软件试用版产品,包括 NebulaGraph、PingCAP、GitLab、EMQ、数云、用友等。计算巢充分利用快速部署的优势和云计算的弹性,实现…

微信上怎么设置每周二汽车限行限号提醒

对于很多人来说,每天手机不离手主要是为了接收各种各样的微信消息,无论是我们和孩子老师的交流还是和家人、同事们之间的交流都要用到微信。既然大多数人的都比较重视微信消息,所以有不少人都想要在手机微信上设置每周的汽车限行提醒,这样每周固定时间收到限号提醒,就不会…

学习:python异常

异常就是我们在写python时,经常看到的报错信息,例如:NameError TyperError ValueError 等 这些都是异常 异常会终止程序 SyntaxError 语法错误 NameError: name b is not defined变量名写错 IndentationError: unexpected indent缩进出错 ModuleNotFoundError第三方包未安装…

【深度学习】DNN房价预测

前言 我们使用深度学习网络实现波士顿房价预测,深度学习的目的就是寻找一个合适的函数输出我们想要的结果。深度学习实际上是机器学习领域中一个研究方向,深度学习的目标是让机器能够像人一样具有分析学习的能力,能够识别文字、图像、声音等数据。我认为深度学习与机器学习最…

视图(View)

视图家族有四个成员:View,ViewGroup,布局,控件 视图包括各类布局和控件,因为不管是布局还是控件,都是由视图基类View派生而来的,他们继承了View的所有属性。 视图组ViewGroup既是View的子类同时也是各种布局的基类,布局可以容纳其他视图,而控件不行,正是由于ViewGrou…

MAC【在线视频播放器】 ,300个电视直播,以及在线影视!

介绍 今天,我要来分享的是Mac下一款全能直播软件——在线视频播放器,目前作者已经更新至11.4版本。大家都知道,现在在网上收看一些直播是非常的难了,我曾经也为之苦恼过,后来发现了这么一款在线播放器才算解决了这一问题。这款在线播放器的开发者是Q群一位名叫“fosi”的大…

Makefile的使用

1 概要 软件的分层使软件的逻辑关系更清晰,但是也带来一个副作用,即Makefile也变得复杂了。道理显而易见:对于一个简单项目,如果所有文件都放在同一个文件夹内,Makefile写起来也会十分简单,但是我们不能一直停留在原始时代,当复杂项目的源文件按类型、功能、模块等分散到…

Codeforces Round #772 (Div. 2)

Codeforces Round #772 (Div. 2) VPA B C3min 12min 52min+4排名:rk3893 基准分:\(\color{ForestGreen}{1362}\) 从天选到天崩 A \(\color{Gray}{800}\)CF1635A Min Or Sum简要分析可知,其实答案就是对于所有数取或运算和(具体懒得管) 时间复杂度:\(O(n)\) int n,x; void w…

SMB登录事件排查经验分享

1. 概述1.1 案例先来看两张图: 看到这两张图的第一印象应该是这是一个成功的登陆,其类型为3,代表网络登陆,4624表示成功登陆,可能大部分人都是如此认为。 那么实际上呢?这里面是存在一定歧义的,今天给大家同步一下这里面的详细细节。1.2 原理当用户使用SMB 协议连接时,…

GET 和 POST详解

https://blog.csdn.net/qq_44204058/article/details/113984363 一、HTTP请求方法Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET,POST,PUT,DELETE. 一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查,改,增,…

leetcode 594. Longest Harmonious Subsequence 最长和谐子序列(简单).md

题目给我们一个数组,让我们找出最长的和谐子序列,和谐子序列就是序列中数组的最大最小差值均为1,这里只让我们求长度,而不需要返回具体的子序列。所以我们可以对数组进行排序,实际上只要找出来相差为1的两个数的总共出现个数就是一个和谐子序列长度了。一、题目大意 https…

2022DASCTF X SU 三月春季挑战赛 web

2022DASCTF——web1.ezpop 2.calc 3.upgdstoreezpop 给出了源码: <?phpclass crow {public $v1;public $v2;function eval() {echo new $this->v1($this->v2);}public function __invoke(){$this->v1->world();} }class fin {public $f1;public function __de…

SAAS市场不是“出身之争”,客户需求主导一切

“Salesforce中国区宣布解散”的消息,市场已经给出诸多分析和猜测。有意思的是,每当有外企中国业务受阻,市场就会有一波声音出来,认为这是外企在中国水土不服。这次也不例外,有一种观点认为外国软件不适合中国国情,未来将是中国SAAS厂商的机遇。 抛开现象看本质,抛开推测…

使用time.Time数据类型获取时间报错

报错类型:Error 1292: Incorrect datetime value: 0000-00-00 for column created_at at row 1 在添加用户到数据库时,使用的字段created_at,类型为time.Time ,无法正确的获取到当前数据点的报错记录,如下图所示: 解决方法与解决过程: 因为我这是学习别人的项目,所以拥…

今日内容之 CSS盒子模型和JS基础知识数据类型

CSS盒子模型所有的标签都可以看成是一个快递盒 1.margin(外边距):标签之间的距离 两个快递盒之间的距离 2.border(边框):标签的边框 快递盒的厚度 3.padding(内边距):内部文本与边框的距离 盒子内物…

由浇花工具开始物联网平台之开始前言篇【1】

在2020年时,突然有个想法,就是做个浇花工具,因为平时喜欢养花,有时忘记浇花,有时感觉手动浇花太麻烦,所以做个这个小玩意,是用.NET 开发的WinForm小程序,来控制单片机,带动水泵浇花,还可以测量干燥度自动浇花。现在突然又想起这事,那就由这个浇花工具开始我的物联网…

LTI系统正弦函数输入的稳态响应(Problem3.12)

这几天南方好几个省市高温大旱持续,长江流域水位下降,而北方多雨,这算是极端气候吗。一、稳态响应,涉及到的课后作业题是P3.12线性时不变系统(LTI)的稳态响应。 二、书中正文第75页三、解答过程 牢记: 1、如果你决定做某事,那就动手去做;不要受任何人、任何事的干…

[转]经典-python串口读取gps可视化 - MKT-porter - 博客园

(转载请删除括号里的内容) GPS模块设置 1使用ucenter设置gps输出 默认gps 9600 或者115200 选择串口链接 2 设置波特率 send之后重新连接gps模块,波特率修改成115200,send只是当前有效,断电恢复原来的. 3 修改GPS输出频率断电生效,保存在gps的内存里 4修改gps输出帧 默认输出…

易基因|作物育种:MdMTA介导的RNA甲基化(m6A修饰)在苹果抗逆品种选育中的作用研究

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 m6A是RNA上最丰富的一种修饰,平均每条转录本有1~3个m6A修饰。植物也有相应的m6A writers、readers、erasers系统。近年来,m6A修饰在植物育种领域的研究进展极为迅速。本期我们对m6A RNA甲基化在抗逆苹果品种…