欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-26 12:17:29
...

(开始这段别人总结的比较好,我就拿来用了)

1.RVA2FOA

即我们现在知道内存状态下的偏移,需要找到文件状态下的偏移。
步骤如下图:

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

step1:内存中的地址减去内存基址得到偏移,即RVA。
step2:循环遍历节表中各个节的信息,判断在哪个节中。(需要满足:内存偏移+节数据没对齐的大小>image_panyi>内存偏移)
step3:找出在哪个节后,减去该节在内存中的偏移(VirturalAddress)得到在该节中的相对偏移。
step4:上一步得到的该节的相对偏移+该节在文件中的偏移(PointToRawData),即得到FOA
2.FOA2RVA

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

代码如下:

#include<stdlib.h>
#include<stdio.h>
#include "string.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");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
		return 0;
	}
	// 读取文件大小
	fseek(pfile, 0, SEEK_END);
	file_size = ftell(pfile);
	fseek(pfile, 0, SEEK_SET);
	// 分配空间
	pTempFilebuffer = malloc(file_size);  // 如果有新的指针,就要进行判断
	if (!pTempFilebuffer)
	{
		printf("分配空间失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
		fclose(pfile);
		return 0;
	}
	// 将数据读取到内存中
	size_t n = fread(pTempFilebuffer, file_size, 1, pfile);
	if (!n)
	{
		printf("数据读取到内存中失败!\n"); //如果分配失败就要关闭文件、释放动态内存、指针指向NULL
		fclose(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;
	}
	//强制结构体类型转换pDosHeader
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	//判断是否含有PE标志       
	if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
	{																			  //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
		printf("(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;

}

DWORD convertRVAtoFOA1(IN LPVOID pImageBuffer,IN LPVOID pFileBuffer, IN DWORD dwRva)
{
	// 初始化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;

	DWORD ImageOffset = dwRva ;
	//DWORD RelSectionOffset = 0;

	// 判断pImageBuffer是否有效
	if (!pImageBuffer)
	{
		printf("(RVA转换成FOA阶段)读取到内存的ImageBuffer无效!\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;
	}

	printf("ImageOffset: %x\n", ImageOffset);
	// 强制结构体类型转换
	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);

	PIMAGE_SECTION_HEADER pSectionTemp = pSectionHeader;

	if (ImageOffset <= pOptionHeader->SizeOfHeaders)
		return (DWORD)pFileBuffer + ImageOffset;
	else
	{
		for (int n = 0; n < pPEHeader->NumberOfSections; n++, pSectionTemp++)
		{
			if ((ImageOffset >= pSectionTemp[n].VirtualAddress)&&(ImageOffset <= pSectionTemp[n].VirtualAddress + pSectionTemp[n].Misc.VirtualSize))
			{
				return ImageOffset - pSectionTemp[n].VirtualAddress + pSectionTemp[n].PointerToRawData;
			}
		}
	}
	printf("地址转换失败!\n");
	return 0;

}

DWORD convertFOAtoRVA1(IN LPVOID pFileBuffer, IN LPVOID pImageBuffer, IN DWORD dwFoa)
{
	// 初始化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;

	DWORD FileOffset = dwFoa;
	//DWORD RelSectionOffset = 0;

	// 判断pImageBuffer是否有效
	if (!pFileBuffer)
	{
		printf("(FOA转换RVA成阶段)读取到内存的ImageBuffer无效!\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;
	}
	printf("FileOffset: %x\n", FileOffset);

	// 强制结构体类型转换
	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);

	PIMAGE_SECTION_HEADER pSectionTemp = pSectionHeader;

	if (FileOffset <= pOptionHeader->SizeOfHeaders)
		return (DWORD)pFileBuffer + FileOffset;
	else
	{	
		for (int n = 0; n < pPEHeader->NumberOfSections; n++, pSectionTemp++)
		{	//判断 :   文件对齐+文件偏移>file_panyi>文件偏移  (即是在文件的哪个节中)
			if ((FileOffset >= pSectionTemp->PointerToRawData) && (FileOffset <= pSectionTemp->PointerToRawData + pSectionTemp->SizeOfRawData))
			{
				return FileOffset - pSectionTemp->PointerToRawData + pSectionTemp->VirtualAddress;
			}
		}	
	}
	printf("地址转换失败!\n");
	return 0;
}

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->filebuffer
	int ret1 = ReadPEFile(file_path, &pFileBuffer);  // &pFileBuffer(void**类型) 传递地址对其值可以进行修改
	printf("exe->filebuffer  返回值为计算所得文件大小:%#x\n", ret1);
	// filebuffer -> imagebuffer
	int ret2 = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
	printf("filebuffer -> imagebuffer返回值为计算所得文件大小:%#x\n", ret2);
	//imagebuffer->newbuffer
	int 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 = 0x00012345;
	int pFOA = 0x00023456;
	int ret_FOA1 = convertRVAtoFOA1(pImageBuffer, pFileBuffer, pRVA);
	printf("内存偏移%#x 转换为文件中的偏移:%#x\n", pRVA, ret_FOA1);
	int ret_RVA1 = convertFOAtoRVA1(pFileBuffer,pImageBuffer, pFOA);
	printf("文件偏移%#x 转换为内存中的偏移:%#x\n", pFOA, ret_RVA1);

}

int main()
{
	operate_pe();
	getchar();
	return 0;
}

结果展示:

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

 

相关标签: 内存管理