滴水逆向作业——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

相关文章

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

PDF 软件如何帮助您编辑、转换和保护文件。

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案,还是尝试组织和编辑主文档,PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时,请考虑这些因素。 1. 确定您的 PDF 文档软件需求。 不同的 PDF 文档软件程序可以具有不同的功能,因此在决定哪个是最适合您的 PDF 软件之前,请花点时间评估您的

C# double[] 和Matlab数组MWArray[]转换

C# double[] 转换成MWArray[], 直接赋值就行             MWNumericArray[] ma = new MWNumericArray[4];             double[] dT = new double[] { 0 };             double[] dT1 = new double[] { 0,2 };

Android逆向(反调,脱壳,过ssl证书脚本)

文章目录 总结 基础Android基础工具 定位关键代码页面activity定位数据包参数定位堆栈追踪 编写反调脱壳好用的脚本过ssl证书校验抓包反调的脚本打印堆栈bilibili反调的脚本 总结 暑假做了两个月的Android逆向,记录一下自己学到的东西。对于app渗透有了一些思路。 这两个月主要做的是代码分析,对于分析完后的持久化等没有学习。主要是如何反编译源码,如何找到

解决C/C++ 头文件相互包含 问题的方法

所谓超前引用是指一个类型在定义之前就被用来定义变量和声明函数。 类A和类B需要彼此互相引用,这样必然有一个类会先被定义,而另外一个类后被定义,这样在 先被定义的类引用后被定义的类的时候,就导致了所谓的超前引用。 超前引用导致的错误有以下几种处理办法:   1) 使用类声明    在超前引用一个类之前,首先用一个特殊的语句说明该标识符是一个类名,即将被超前引用。其使用方法是