滴水逆向作业——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轻松实现PDF转换为PDF/A的示例代码

《Java轻松实现PDF转换为PDF/A的示例代码》本文将深入探讨Java环境下,如何利用专业工具将PDF转换为PDF/A格式,为数字文档的永续保存提供可靠方案,文中的示例代码讲解详细,感兴趣的小伙伴... 目录为什么需要将PDF转换为PDF/A使用Spire.PDF for Java进行转换前的准备通过

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Python中Json和其他类型相互转换的实现示例

《Python中Json和其他类型相互转换的实现示例》本文介绍了在Python中使用json模块实现json数据与dict、object之间的高效转换,包括loads(),load(),dumps()... 项目中经常会用到json格式转为object对象、dict字典格式等。在此做个记录,方便后续用到该方

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

Python进行JSON和Excel文件转换处理指南

《Python进行JSON和Excel文件转换处理指南》在数据交换与系统集成中,JSON与Excel是两种极为常见的数据格式,本文将介绍如何使用Python实现将JSON转换为格式化的Excel文件,... 目录将 jsON 导入为格式化 Excel将 Excel 导出为结构化 JSON处理嵌套 JSON:

Kotlin Map映射转换问题小结

《KotlinMap映射转换问题小结》文章介绍了Kotlin集合转换的多种方法,包括map(一对一转换)、mapIndexed(带索引)、mapNotNull(过滤null)、mapKeys/map... 目录Kotlin 集合转换:map、mapIndexed、mapNotNull、mapKeys、map

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

利用Python脚本实现批量将图片转换为WebP格式

《利用Python脚本实现批量将图片转换为WebP格式》Python语言的简洁语法和库支持使其成为图像处理的理想选择,本文将介绍如何利用Python实现批量将图片转换为WebP格式的脚本,WebP作为... 目录简介1. python在图像处理中的应用2. WebP格式的原理和优势2.1 WebP格式与传统

java Long 与long之间的转换流程

《javaLong与long之间的转换流程》Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换,本文将详细介绍如何在Java中实现Long和long之间的转换,感... 目录概述流程步骤1:将long转换为Long对象步骤2:将Longhttp://www.cppcns.c