I am writing a function to convert RVA to VA .
unsigned myRVAtoRAW (HANDLE pe_image, unsigned rva) { IMAGE_DOS_HEADER dos_header; std::memcpy(reinterpret_cast<char*>(&dos_header), pe_image, sizeof(dos_header)); if (dos_header.e_magic != 'ZM') { std::cout << "Unable to read IMAGE_DOS_HEADER" << std::endl; return 0; } // Start of PE header must be DWORD-aligned if (dos_header.e_lfanew % sizeof(DWORD) != 0) { std::cout << "PE header is not DWORD-aligned" << std::endl; return 0; } IMAGE_NT_HEADERS32 nt_headers; std::memcpy(&nt_headers, (char *) pe_image + dos_header.e_lfanew, sizeof(nt_headers)); if (nt_headers.Signature != 'EP') { std::cout << "Incorrent PE signature" << std::endl; return 0; } return 0; } Explanation of the code: the first parameter of the function is the address of the beginning of the projection of the executable file on the address space of this program. It is returned by the MapViewOfFile function.
The function correctly reads IMAGE_DOS_HEADER , the MZ signature test passes without errors.
Then you need to read IMAGE_NT_HEADERS32 . The dos_header.e_lfanew field indicates the beginning of the PE header, i.e. on the first byte PE00 . I thought that this address is set relative to the beginning of the file in memory, because In 10 minutes, I did not google exactly what address is contained here.
I will give examples of how I filled this structure.
std::memcpy(reinterpret_cast<char*>(&nt_headers), (char *)pe_image + dos_header.e_lfanew, sizeof(nt_headers)); The above is the only thing that does not lead to errors. I believe that e_lfanew is a relative address.
std::memcpy(reinterpret_cast<char*>(&nt_headers), dos_header.e_lfanew, sizeof(nt_headers)); Causes an error when starting the program.
And again: if a pointer is given to a file projection, how in C ++ will it be correct to fill structures through it? The memcpy function does not allow you to track, for example, such errors when the data is smaller than the size of the structure.
Addition: the function seems to perform address translation correctly. Addition: here I probably did not take into account the address of the beginning of the code section.
unsigned myRVAtoRAW (HANDLE pe_image, unsigned rva) { IMAGE_DOS_HEADER dos_header; std::memcpy(&dos_header, pe_image, sizeof(dos_header)); if (dos_header.e_magic != 'ZM') { std::cout << "Unable to read IMAGE_DOS_HEADER" << std::endl; return 0; } // Start of PE header must be DWORD-aligned if (dos_header.e_lfanew % sizeof(DWORD) != 0) { std::cout << "PE header is not DWORD-aligned" << std::endl; return 0; } IMAGE_NT_HEADERS nt_headers; std::memcpy(&nt_headers, ((char *) pe_image) + dos_header.e_lfanew, sizeof(nt_headers)); // Check: is our file PE if (nt_headers.Signature != 'EP') { std::cout << "Incorrent PE signature" << std::endl; return 0; } // Check: is this file PE32 if (nt_headers.OptionalHeader.Magic != 0x10B) { std::cout << "This PE is not PE32" << std::endl; return 0; } std::cout << "ImageBase = " << nt_headers.OptionalHeader.ImageBase << std::endl; // Calculate RVA to VA return rva + nt_headers.OptionalHeader.ImageBase; }