滴水逆向作业——RVA与FOA相互转换

2023-10-12 02:40

本文主要是介绍滴水逆向作业——RVA与FOA相互转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PE有两种不同的状态:1.FileBuffer文件状态 2.ImageBuffer内存状态。
当需求为:改变一个变量的值,知道它在内存状态下的地址,我们需要找到他在文件状态下的地址对它进行修改。或者反之,我们知道它在文件状态下的地址、找出他在内存状态下的地址。就需要RVA与FOA相互转换。

RVA与FOA相互转换主要会使用到节表中的信息,所以我们先需要复习一下节表中的信息。

节表

节表存在于可选PE头之下,它的作用是包含个节的信息。如下:


typedef struct _IMAGE_SECTION_HEADER {			BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];	//占8个字节,分别是各个节的名字		union {			DWORD   PhysicalAddress;			DWORD   VirtualSize;//节数据没有对齐后的大小			} Misc;	// 联合体	该节在没有对齐前的真实尺寸,该值可以不准确	DWORD   VirtualAddress;	//节区内存中节的偏移		DWORD   SizeOfRawData;	// 节区在文件中对齐后的大小		DWORD   PointerToRawData;  // 在文件中的偏移		DWORD   PointerToRelocations;			DWORD   PointerToLinenumbers;			WORD    NumberOfRelocations;			WORD    NumberOfLinenumbers;			DWORD   Characteristics; //节的属性			
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;			

VA:virtualaddress,在内存中的虚拟地址。如0x00403000。
RVA:相对虚拟偏移. 就是偏移地址。如0x00403000的RVA就是0x00403000-ImageBase = 0x0000300。
FOA:是文件中的偏移地址

使用PETool解析System32下的notepad.exe节表信息如下。
在这里插入图片描述

如何转换?

1.RVA2FOA

即我们现在知道内存状态下的偏移,需要找到文件状态下的偏移。
步骤如下图:
在这里插入图片描述
step1:内存中的地址减去内存基址得到偏移,即RVA。
step2:循环遍历节表中各个节的信息,判断在哪个节中。(需要满足:内存偏移+节数据没对齐的大小>image_panyi>内存偏移)
step3:找出在哪个节后,减去该节在内存中的偏移(VirturalAddress)得到在该节中的相对偏移。
step4:上一步得到的该节的相对偏移+该节在文件中的偏移(PointToRawData),即得到FOA

2.FOA2RVA

现在我们已经知道如何从内存中的偏移转化为文件中的偏移。现在是它的逆过程
step1:文件中的地址减去文件基址,得到在文件中的偏移,即FOA。
step2:循环遍历节表中各个节的信息,判断在哪个节中。(文件对齐+文件偏移>file_panyi>文件偏移)
step3:找出在哪个节后,减去该节在文件中的偏移(VirturalAddress)得到在该节中的相对偏移。
step4:上一步得到的该节的相对偏移+该节在内存中的偏移(VirtualAddress),即得到RVA。

代码如下:
包括:exe->filebuffer->imagebuffer->new->buffer及RVA2FOA和FOA2RVA

// test_lc_01.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include "string.h"
#include <malloc.h>
#include <windows.h>// exe->filebuffer  返回值为计算所得文件大小
int ReadPEFile(char* file_path,PVOID* pFileBuffer)
{FILE* pfile = NULL;  // 文件指针DWORD file_size = 0;LPVOID pTempFilebuffer = NULL;// 打开文件pfile = fopen(file_path,"rb");  // 如果有新的指针,就要进行判断if(!pfile){printf("打开exe文件失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULLreturn 0;}	// 读取文件大小fseek(pfile,0,SEEK_END);file_size = ftell(pfile);fseek(pfile,0,SEEK_SET);// 分配空间pTempFilebuffer = malloc(file_size);  // 如果有新的指针,就要进行判断if(!pTempFilebuffer){printf("分配空间失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULLfclose(pfile);return 0 ;}// 将数据读取到内存中size_t n = fread(pTempFilebuffer,file_size,1,pfile);if(!n){printf("数据读取到内存中失败!\n"); //如果分配失败就要关闭文件、释放动态内存、指针指向NULLfclose(pfile);free(pTempFilebuffer);return 0 ;}// 关闭文件(已经读取到内存了)*pFileBuffer = pTempFilebuffer;pTempFilebuffer = NULL;fclose(pfile); return file_size;
}// filebuffer -> imagebuffer
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer)
{// 初始化PE头部结构体PIMAGE_DOS_HEADER pDosHeader = NULL;	PIMAGE_NT_HEADERS pNTHeader = NULL;	PIMAGE_FILE_HEADER pPEHeader = NULL;	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;// 初始化IMAGE_BUFFER指针(temparay)LPVOID pTempImagebuffer = NULL;if(!pFileBuffer){printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");return 0 ;}// 判断是否是可执行文件if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)  // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD){printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");return 0;}//强制结构体类型转换pDosHeaderpDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);//判断是否含有PE标志       if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。{																			  //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTESprintf("(2pimagebuffer阶段)不是有效的PE标志!\n");	return 0;}// 强制结构体类型转换pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);// 分配动态内存pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);if(!pTempImagebuffer){printf("分配动态内存失败!\n");free(pTempImagebuffer);return 0;}// 初始化动态内存memset(pTempImagebuffer,0,pOptionHeader->SizeOfImage);// 拷贝头部memcpy(pTempImagebuffer,pDosHeader,pOptionHeader->SizeOfHeaders);// 循环拷贝节表PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++){memcpy((void*)((DWORD)pTempImagebuffer+pTempSectionHeader->VirtualAddress),(void*)((DWORD)pFileBuffer+pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);}// 返回数据*pImageBuffer = pTempImagebuffer;pTempImagebuffer = NULL;return pOptionHeader->SizeOfImage;
}//imagebuffer->newbuffer
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer)
{// 初始化PE头部结构体PIMAGE_DOS_HEADER pDosHeader = NULL;	PIMAGE_NT_HEADERS pNTHeader = NULL;	PIMAGE_FILE_HEADER pPEHeader = NULL;	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;// 初始化NEW_BUFFER指针(temparay)LPVOID pTempNewbuffer = NULL;// 判断pImageBuffer是否有效if(!pImageBuffer){printf("(2pnewbuffer阶段)读取到内存的pimagebuffer无效!\n");return 0;}//判断是不是exe文件if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE){printf("(2pnewbuffer阶段)不含MZ标志,不是exe文件!\n");return 0;}// 强制结构体类型转换pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){printf("(2pnewbuffer阶段)不是有效的PE标志!\n");	return 0;}// 强制结构体类型转换pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);//获取new_buffer的大小int new_buffer_size = pOptionHeader->SizeOfHeaders;for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++){new_buffer_size += pSectionHeader[i].SizeOfRawData;  // pSectionHeader[i]另一种加法}// 分配内存(newbuffer)pTempNewbuffer = malloc(new_buffer_size);if(!pTempNewbuffer){printf("(2pnewbuffer阶段)分配Newbuffer失败!\n");return 0;}memset(pTempNewbuffer,0,new_buffer_size);// 拷贝头部memcpy(pTempNewbuffer,pDosHeader,pOptionHeader->SizeOfHeaders);// 循环拷贝节区PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;for(DWORD j = 0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++){	//PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸memcpy((PDWORD)((DWORD)pTempNewbuffer+pTempSectionHeader->PointerToRawData),(PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),pTempSectionHeader->SizeOfRawData);}//返回数据*pNewBuffer = pTempNewbuffer; //暂存的数据传给参数后释放pTempNewbuffer = NULL;return new_buffer_size;  // 返回计算得到的分配内存的大小
}//newbuffer->存盘
int newbuffer_write2_exe(PVOID NewFileBuffer,DWORD FileSize, char* FilePath)
{FILE* fp1 = fopen(FilePath,"wb");if(fp1 != NULL){fwrite(NewFileBuffer,FileSize,1,fp1);}fclose(fp1);return 1;}// RVA转换成FOA
DWORD convertRVAtoFOA(DWORD pRVA,PVOID pFileBuffer,PVOID pImageBuffer)
{// 初始化PE头部结构体PIMAGE_DOS_HEADER pDosHeader = NULL;	PIMAGE_NT_HEADERS pNTHeader = NULL;	PIMAGE_FILE_HEADER pPEHeader = NULL;	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;// 判断pImageBuffer是否有效if(!pImageBuffer){printf("(RVA转换成FOA阶段)读取到内存的pimagebuffer无效!\n");return 0;}//判断是不是exe文件if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE){printf("(RVA转换成FOA阶段)不含MZ标志,不是exe文件!\n");return 0;}// 强制结构体类型转换pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){printf("(RVA转换成FOA阶段)不是有效的PE标志!\n");	return 0;}// 强制结构体类型转换pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);int image_panyi = pRVA;  // pRVA是在内存中的偏移偏移printf("image_panyi:%#x\n",image_panyi);// 循环查找在那个imagebuffer节中PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++){	//判断 :  Misc.VirtualSize+ VirtualAddress 内存偏移+节数据没对齐的大小>image_panyi>内存偏移 VirtualAddress (即是在文件的哪个节中)if((image_panyi>=pTempSectionHeader->VirtualAddress) && (image_panyi<pTempSectionHeader->VirtualAddress+pTempSectionHeader->Misc.VirtualSize)){return image_panyi-pTempSectionHeader->VirtualAddress+pTempSectionHeader->PointerToRawData;}}return 0;}// FOA转换成RVADWORD convertFOAtoRVA(DWORD pFOA,PVOID pFileBuffer,PVOID pImageBuffer)
{// 初始化PE头部结构体PIMAGE_DOS_HEADER pDosHeader = NULL;	PIMAGE_NT_HEADERS pNTHeader = NULL;	PIMAGE_FILE_HEADER pPEHeader = NULL;	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;// 判断pImageBuffer是否有效if(!pFileBuffer){printf("(FOA转换成RVA阶段)读取到内存的pimagebuffer无效!\n");return 0;}//判断是不是exe文件if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE){printf("(FOA转换成RVA阶段)不含MZ标志,不是exe文件!\n");return 0;}// 强制结构体类型转换pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){printf("(FOA转换成RVA阶段)不是有效的PE标志!\n");	return 0;}// 强制结构体类型转换pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);int file_panyi = pFOA;  // pRVA是文件中的偏移偏移printf("file_panyi:%#x\n",file_panyi);// 循环查找在那个imagebuffer节中PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++){	//判断 :   文件对齐+文件偏移>file_panyi>文件偏移  (即是在文件的哪个节中)if((file_panyi>=pTempSectionHeader->PointerToRawData) && (file_panyi<pTempSectionHeader->PointerToRawData+pTempSectionHeader->SizeOfRawData)){return file_panyi-pTempSectionHeader->PointerToRawData+pTempSectionHeader->VirtualAddress;}}return 0;printf("地址转换失败!\n");}void operate_pe()
{   // 初始化操作PVOID pFileBuffer = NULL;PVOID pImageBuffer = NULL;PVOID pNewFileBuffer = NULL;DWORD NewFileBufferSize = 0;//char file_path[] = "D:\\Lib\\IPMSG2007.exe";char file_path[] = "C:\\Windows\\System32\\notepad.exe";char write_file_path[] = "D:\\Lib\\cp_notepad.exe";// exe->filebufferint ret1 = ReadPEFile(file_path,&pFileBuffer);  // &pFileBuffer(void**类型) 传递地址对其值可以进行修改printf("exe->filebuffer  返回值为计算所得文件大小:%#x\n",ret1);// filebuffer -> imagebufferint ret2 = CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);printf("filebuffer -> imagebuffer返回值为计算所得文件大小:%#x\n",ret2);//imagebuffer->newbufferint FileSize = CopyImageBufferToNewBuffer(pImageBuffer,&pNewFileBuffer);printf("imagebuffer->newbuffer返回值为计算所得文件大小:%#x\n",FileSize);//newbuffer->存盘//int ret4 = newbuffer_write2_exe(pNewFileBuffer,FileSize, write_file_path);//printf("newbuffer->存盘返回值为:%d\n",ret4);int pRVA = 0x00021178;int pFOA = 0x00020450;int ret_FOA = convertRVAtoFOA(pRVA,pFileBuffer,pImageBuffer);printf("内存偏移%#x 转换为文件中的偏移:%#x\n",pRVA,ret_FOA);int ret_RVA = convertFOAtoRVA(pFOA,pFileBuffer,pImageBuffer);printf("文件偏移%#x 转换为内存中的偏移:%#x\n",pFOA,ret_RVA);}int main()
{	operate_pe();getchar();return 0;
}

结果如下:
在这里插入图片描述
实现了文件偏移与内存偏移之间的相互转化。

这篇关于滴水逆向作业——RVA与FOA相互转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

Java实现XML与JSON的互相转换详解

《Java实现XML与JSON的互相转换详解》这篇文章主要为大家详细介绍了如何使用Java实现XML与JSON的互相转换,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. XML转jsON1.1 代码目的1.2 代码实现2. JSON转XML3. JSON转XML并输出成指定的

Java实现将Markdown转换为纯文本

《Java实现将Markdown转换为纯文本》这篇文章主要为大家详细介绍了两种在Java中实现Markdown转纯文本的主流方法,文中的示例代码讲解详细,大家可以根据需求选择适合的方案... 目录方法一:使用正则表达式(轻量级方案)方法二:使用 Flexmark-Java 库(专业方案)1. 添加依赖(Ma

Java实现将byte[]转换为File对象

《Java实现将byte[]转换为File对象》这篇文章将通过一个简单的例子为大家演示Java如何实现byte[]转换为File对象,并将其上传到外部服务器,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言1. 问题背景2. 环境准备3. 实现步骤3.1 从 URL 获取图片字节数据3.2 将字节数组

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像