Reverse operation of dripping - mutual conversion of RVA and FOA

Keywords: Windows Attribute

PE has two different states: 1.FileBuffer file state 2.ImageBuffer memory state.
When the requirement is: change the value of a variable to know its address in memory state, we need to find its address in file state to modify it. Or vice versa, we know its address in file state and find out its address in memory state. The RVA and FOA need to be converted to each other.

The conversion between RVA and FOA mainly uses the information in the section table, so we need to review the information in the section table first.

Section table

The section table exists under the Optional PE header to contain information about sections. As follows:

typedef struct _IMAGE_SECTION_HEADER {			
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];	//Takes up 8 bytes, each section's name		
    union {			
            DWORD   PhysicalAddress;			
            DWORD   VirtualSize;//Size of section data without alignment			
    } Misc;	// The true size of the joint venture before alignment, which can be inaccurate	
    DWORD   VirtualAddress;	//Section offset in section memory		
    DWORD   SizeOfRawData;	// Size of section after alignment in file		
    DWORD   PointerToRawData;  // Offset in file		
    DWORD   PointerToRelocations;			
    DWORD   PointerToLinenumbers;			
    WORD    NumberOfRelocations;			
    WORD    NumberOfLinenumbers;			
    DWORD   Characteristics; //Attribute of section			
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;			

VA: virtual address, the virtual address in memory. Such as 0x00403000.
RVA: relative virtual offset. Is the offset address. For example, the RVA of 0x00403000 is 0x00403000 imagebase = 0x0000300.
FOA: is the offset address in the file.

Use PETool to parse the notepad.exe section table information under System32 as follows.

How to convert?

1.RVA2FOA

That is to say, we now know the offset in memory state and need to find the offset in file state.
The steps are as follows:

step1: the address in memory subtracts the base address to get the offset, that is, RVA.
Step 2: loop through the information of each section in the section table to determine which section it is in. (it needs to meet the following requirements: memory offset + misaligned size of section data > Image > memory offset)
step3: find out which section is followed by subtracting the section's in memory offset (virtualaddress) to get the relative offset in the section.
step4: the relative offset of the section obtained in the previous step + the offset of the section in the file (PointToRawData), i.e. FOA

2.FOA2RVA

Now we know how to convert the offset in memory to the offset in file. Now it's the reverse process
step1: subtract the file base address from the address in the file to get the offset in the file, that is, FOA.
Step 2: loop through the information of each section in the section table to determine which section it is in. (file alignment + file offset > File > file offset)
step3: after finding out which section, subtract the section's virtualaddress in the file to get the relative offset in the section.
step4: the relative offset of this section obtained in the previous step + the offset of this section in memory (VirtualAddress), that is to say, the RVA is obtained.

The code is as follows:
Including: exe - > filebuffer - > imagebuffer - > New - > buffer and RVA2FOA and 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 return value is the calculated file size
int ReadPEFile(char* file_path,PVOID* pFileBuffer)
{
	FILE* pfile = NULL;  // field name pointer
	DWORD file_size = 0;
	LPVOID pTempFilebuffer = NULL;
	
	// Open file
	pfile = fopen(file_path,"rb");  // If there's a new pointer, make a judgment
	if(!pfile)
	{
		printf("open exe File failed!\n");//If the allocation fails, close the file, release the dynamic memory, and point to NULL
		return 0;
	}	
	// Read file size
	fseek(pfile,0,SEEK_END);
	file_size = ftell(pfile);
	fseek(pfile,0,SEEK_SET);
	// Allocation space
	pTempFilebuffer = malloc(file_size);  // If there's a new pointer, make a judgment
	if(!pTempFilebuffer)
	{
		printf("Failed to allocate space!\n");//If the allocation fails, close the file, release the dynamic memory, and point to NULL
		fclose(pfile);
		return 0 ;
	}
	// Read data into memory
	size_t n = fread(pTempFilebuffer,file_size,1,pfile);
	if(!n)
	{
		printf("Data read to memory failed!\n"); //If the allocation fails, close the file, release the dynamic memory, and point to NULL
		fclose(pfile);
		free(pTempFilebuffer);
		return 0 ;
	}
	// Close file (read to memory already)
	*pFileBuffer = pTempFilebuffer;
	pTempFilebuffer = NULL;
	fclose(pfile); 
	return file_size;
}

// filebuffer -> imagebuffer
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer)
{
	// Initialize PE header structure
	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;
	// Initialize the image buffer pointer (temporary)
	LPVOID pTempImagebuffer = NULL;

	if(!pFileBuffer)
	{
		printf("(2pimagebuffer stage)Read to memory pfilebuffer Invalid!\n");
		return 0 ;
	}
	// Determine whether it is an executable
	if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)  // Image? DOS? Signature is 4 bytes, convert pFileBuffer to 4-byte pointer type (PWORD)
	{
		printf("(2pimagebuffer stage)Do not contain MZ Logo, no exe Papers!\n");
		return 0;
	}
	//Force struct type conversion pDosHeader
	pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
	//Determine whether PE mark is included       
	if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // Note that the addition of the pointer is the type addition after removing a *. Must be converted to DWORD type plus minus.
	{																			  //The sum after addition is cast to 4-byte pointer type (PWORD) image \ NT \ signature 4bytes
		printf("(2pimagebuffer stage)Not valid PE Logo!\n");	
		return 0;
	}
	// Force struct type conversion
	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);

	// Allocate dynamic memory
	pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
	if(!pTempImagebuffer)
	{
		printf("Failed to allocate dynamic memory!\n");
		free(pTempImagebuffer);
		return 0;
	}
	// Initialize dynamic memory
	memset(pTempImagebuffer,0,pOptionHeader->SizeOfImage);
	// Copy head
	memcpy(pTempImagebuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
	// Circular copy section table
	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);
	}
	// Return data
	*pImageBuffer = pTempImagebuffer;
	pTempImagebuffer = NULL;
	return pOptionHeader->SizeOfImage;
}

//imagebuffer->newbuffer
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer)
{
	// Initialize PE header structure
	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;

	// Initialize the new buffer pointer (temporary)
	LPVOID pTempNewbuffer = NULL;

	// Determine if the pImageBuffer is valid
	if(!pImageBuffer)
	{
		printf("(2pnewbuffer stage)Read to memory pimagebuffer Invalid!\n");
		return 0;
	}
	//Determine whether it is an exe file
	if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
	{
		printf("(2pnewbuffer stage)Do not contain MZ Logo, no exe Papers!\n");
		return 0;
	}
	// Force struct type conversion
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		printf("(2pnewbuffer stage)Not valid PE Logo!\n");	
		return 0;
	}
	// Force struct type conversion
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // Type conversion must be enforced here
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

	//Get the size of new buffer
	int new_buffer_size = pOptionHeader->SizeOfHeaders;
	for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++)
	{
		new_buffer_size += pSectionHeader[i].SizeOfRawData;  // pSectionHeader[i] another addition
	}
	// Allocate memory (newbuffer)
	pTempNewbuffer = malloc(new_buffer_size);
	if(!pTempNewbuffer)
	{
		printf("(2pnewbuffer stage)distribution Newbuffer Failure!\n");
		return 0;
	}
	memset(pTempNewbuffer,0,new_buffer_size);
	// Copy head
	memcpy(pTempNewbuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
	// Circular copy section
	PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
	for(DWORD j = 0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++)
	{	//The offset of pointtorawdata section in the file, the offset address of VirtualAddress section in memory, and the size of SizeOfRawData section after alignment in the file
		memcpy((PDWORD)((DWORD)pTempNewbuffer+pTempSectionHeader->PointerToRawData),(PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),pTempSectionHeader->SizeOfRawData);
	}
	//Return data
	*pNewBuffer = pTempNewbuffer; //Release the temporary data after passing it to the parameter
	pTempNewbuffer = NULL;
	return new_buffer_size;  // Returns the calculated amount of allocated memory
}

//Newbuffer - > save
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 to FOA
DWORD convertRVAtoFOA(DWORD pRVA,PVOID pFileBuffer,PVOID pImageBuffer)
{
	// Initialize PE header structure
	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;
	
	// Determine if the pImageBuffer is valid
	if(!pImageBuffer)
	{
		printf("(RVA convert to FOA stage)Read to memory pimagebuffer Invalid!\n");
		return 0;
	}
	//Determine whether it is an exe file
	if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
	{
		printf("(RVA convert to FOA stage)Do not contain MZ Logo, no exe Papers!\n");
		return 0;
	}
	// Force struct type conversion
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		printf("(RVA convert to FOA stage)Not valid PE Logo!\n");	
		return 0;
	}
	// Force struct type conversion
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // Type conversion must be enforced here
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

	int image_panyi = pRVA;  // Pvva is the offset offset in memory
	printf("image_panyi:%#x\n",image_panyi);

	// Loop lookup in that imagebuffer section
	PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
	for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
	{	//Judge: Misc.VirtualSize+ VirtualAddress memory offset + misaligned size of section data > image ﹤ Panyi > memory offset VirtualAddress (that is, which section of the file is in)
		if((image_panyi>=pTempSectionHeader->VirtualAddress) && (image_panyi<pTempSectionHeader->VirtualAddress+pTempSectionHeader->Misc.VirtualSize))
		{
			
			return image_panyi-pTempSectionHeader->VirtualAddress+pTempSectionHeader->PointerToRawData;
		}
	}
	return 0;

}

// FOA to RVA

DWORD convertFOAtoRVA(DWORD pFOA,PVOID pFileBuffer,PVOID pImageBuffer)
{
	// Initialize PE header structure
	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;
	
	// Determine if the pImageBuffer is valid
	if(!pFileBuffer)
	{
		printf("(FOA convert to RVA stage)Read to memory pimagebuffer Invalid!\n");
		return 0;
	}
	//Determine whether it is an exe file
	if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
	{
		printf("(FOA convert to RVA stage)Do not contain MZ Logo, no exe Papers!\n");
		return 0;
	}
	// Force struct type conversion
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		printf("(FOA convert to RVA stage)Not valid PE Logo!\n");	
		return 0;
	}
	// Force struct type conversion
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // Type conversion must be enforced here
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

	int file_panyi = pFOA;  // Pvva is the offset offset in the file
	printf("file_panyi:%#x\n",file_panyi);

	// Loop lookup in that imagebuffer section
	PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
	for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
	{	//Judgment: File alignment + file offset > File > file offset (that is, which section of the file is in)
		if((file_panyi>=pTempSectionHeader->PointerToRawData) && (file_panyi<pTempSectionHeader->PointerToRawData+pTempSectionHeader->SizeOfRawData))
		{
			
			return file_panyi-pTempSectionHeader->PointerToRawData+pTempSectionHeader->VirtualAddress;
		}
	}
	return 0;
	printf("Address conversion failed!\n");

}

void operate_pe()
{   // Initialize operation
	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);  // &The value of pfilebuffer (void * * type) can be modified by its delivery address
	printf("exe->filebuffer  The return value is the calculated file size:%#x\n",ret1);
	// filebuffer -> imagebuffer
	int ret2 = CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
	printf("filebuffer -> imagebuffer The return value is the calculated file size:%#x\n",ret2);
	//imagebuffer->newbuffer
	int FileSize = CopyImageBufferToNewBuffer(pImageBuffer,&pNewFileBuffer);
	printf("imagebuffer->newbuffer The return value is the calculated file size:%#x\n",FileSize);
	//Newbuffer - > save
	//int ret4 = newbuffer_write2_exe(pNewFileBuffer,FileSize, write_file_path);
	//Printf ("newbuffer - > Save return value is:% d\n",ret4);
	int pRVA = 0x00021178;
	int pFOA = 0x00020450;
	int ret_FOA = convertRVAtoFOA(pRVA,pFileBuffer,pImageBuffer);
	printf("Memory offset%#x Convert to offset in file:%#x\n",pRVA,ret_FOA);
	int ret_RVA = convertFOAtoRVA(pFOA,pFileBuffer,pImageBuffer);
	printf("File offset%#x Convert to offset in memory:%#x\n",pFOA,ret_RVA);

}

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


The results are as follows:

The conversion between file offset and memory offset is realized.

Published 6 original articles, won praise 1, visited 80
Private letter follow

Posted by William on Wed, 19 Feb 2020 20:15:22 -0800