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

相关文章

作业提交过程之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) 使用类声明    在超前引用一个类之前,首先用一个特殊的语句说明该标识符是一个类名,即将被超前引用。其使用方法是

数据流与Bitmap之间相互转换

把获得的数据流转换成一副图片(Bitmap) 其原理就是把获得倒的数据流序列化到内存中,然后经过加工,在把数据从内存中反序列化出来就行了。 难点就是在如何实现加工。因为Bitmap有一个专有的格式,我们常称这个格式为数据头。加工的过程就是要把这个数据头与我们之前获得的数据流合并起来。(也就是要把这个头加入到我们之前获得的数据流的前面)      那么这个头是

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数 一、环境说明二、页面之间相互传参 一、环境说明 DevEco Studio 版本: API版本:以12为主 二、页面之间相互传参 说明: 页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页面url找到目标页面,从而实现跳转。通过页面路由模块,可以使用不同的url访问不同的页面,包括跳转到U

高斯平面直角坐标讲解,以及地理坐标转换高斯平面直角坐标

高斯平面直角坐标系(Gauss-Krüger 坐标系)是基于 高斯-克吕格投影 的一种常见的平面坐标系统,主要用于地理信息系统 (GIS)、测绘和工程等领域。该坐标系将地球表面的经纬度(地理坐标)通过一种投影方式转换为平面直角坐标,以便在二维平面中进行距离、面积和角度的计算。 一 投影原理 高斯平面直角坐标系使用的是 高斯-克吕格投影(Gauss-Krüger Projection),这是 横

转:android ro.debuggable属性调试修改(mprop逆向)

android ro属性调试修改(mprop逆向)      大家都知道如果需要调试android 的程序,以下两个条件满足一个就行。第一是apk的配置文件内的AndroidManifest.xml的 android:debuggable=”true”,第二就是/default.prop中ro.debuggable=1。两种方式第一种通常是解包添加属性再打包,随着加壳软件以及apk校验等,容易出

VC环境下整型转换为字符串型(2)

在串口下位机的发送中,可能会用到需要发送数字,显示为字符串型的 和上一篇文字《串口中字符串转换为整型》一正一反,知识点学习会了: #include<iostream.h> #include <stdio.h> #include <string.h>   void inttostr(int m,unsigned char * str) { int length=0;   int tmp,te