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.