PE结构学习(3)_RVA转换成FOA

2023-10-12 02:40
文章标签 学习 结构 转换成 pe foa rva

本文主要是介绍PE结构学习(3)_RVA转换成FOA,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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;
}

这篇关于PE结构学习(3)_RVA转换成FOA的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/192670

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识