滴水逆向作业——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 controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

python实现svg图片转换为png和gif

《python实现svg图片转换为png和gif》这篇文章主要为大家详细介绍了python如何实现将svg图片格式转换为png和gif,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录python实现svg图片转换为png和gifpython实现图片格式之间的相互转换延展:基于Py

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图

详解如何通过Python批量转换图片为PDF

《详解如何通过Python批量转换图片为PDF》:本文主要介绍如何基于Python+Tkinter开发的图片批量转PDF工具,可以支持批量添加图片,拖拽等操作,感兴趣的小伙伴可以参考一下... 目录1. 概述2. 功能亮点2.1 主要功能2.2 界面设计3. 使用指南3.1 运行环境3.2 使用步骤4. 核