Purpose of the article
The purpose of this article is the desire of the author to show some nuances in the development of the functions of RvaToRaw / RawToRva, which are important for system utilities working with executable files of PE format.
Who is the article aimed at?
- The reader is familiar with the “Portable Executable” file format.
- The reader> = 1 times wrote the parser of this file
- The reader is well aware of what RvaToRaw is.
Terminology
RVA - This is an abbreviation of English. Words Relative Virtual Address and means the offset in bytes from the beginning of the real or estimated module load address.
RAW - File offset from the beginning of the file. Another successful name is “File offset”.
')
Development
When searching Google for the keywords “rva to raw” or “rva to offset” you may stumble upon the following code:
DWORD RVAToOffset(IMAGE_NT_HEADERS32* pNtHdr, DWORD dwRVA) { int i; WORD wSections; PIMAGE_SECTION_HEADER pSectionHdr; pSectionHdr = IMAGE_FIRST_SECTION(pNtHdr); wSections = pNtHdr->FileHeader.NumberOfSections; for (i = 0; i < wSections; i++) { if (pSectionHdr->VirtualAddress <= dwRVA) if ((pSectionHdr->VirtualAddress + pSectionHdr->Misc.VirtualSize) > dwRVA) { dwRVA -= pSectionHdr->VirtualAddress; dwRVA += pSectionHdr->PointerToRawData; return (dwRVA); } pSectionHdr++; } return (-1); }
The code has a few blunders and is very popular.
It does not include:
- The rva value can be less than OptionalHeader.SizeOfHeaders, i.e. the value may indicate inside the header
- Values ​​of VirtualAddress, VirtualSize, PointerToRawData are not aligned at all
- The macro IMAGE_FIRST_SECTION is used , which does not take into account 64-bit files, winnt.h honestly says this.
More correct code, from the point of view of the author:
Code class leveling offsets and addresses inline uint32_t alignDown(uint32_t value_, uint32_t factor) { return value_ & ~(factor-1); } inline uint32_t alignUp(uint32_t value_, uint32_t factor) { return alignDown(value_ - 1, factor) + factor; } class Aligner { public: const uint32_t FORCED_FILE_ALIGNMENT = 0x200; const uint32_t MIN_SECTION_ALIGNMENT = 0x1000; public: Aligner(uint32_t fileAlignment_, uint32_t sectionAlignement_) : fileAlignment(fileAlignment_) , sectionAlignement(sectionAlignement_) { } uint32_t getVirtualSize(uint32_t size) { return needAlign(sectionAlignement) ? alignUp(size, sectionAlignement) : size; } uint32_t getVirtualAddress(uint32_t address) { return needAlign(sectionAlignement) ? alignDown(address, sectionAlignement) : address; } uint32_t getFileOffset(uint32_t offset) { return needAlign(sectionAlignement) ? alignDown(offset, FORCED_FILE_ALIGNMENT) : offset; } uint32_t getSectionSize(const ImgSectionHeader& header) { uint32_t fileSize = header.SizeOfRawData; uint32_t virtualSize = header.Misc.VirtualSize; if (needAlign(sectionAlignement)) { fileSize = alignUp(fileSize, fileAlignment); virtualSize = alignUp(virtualSize, sectionAlignement); } return std::min(fileSize, virtualSize); } private: uint32_t fileAlignment; uint32_t sectionAlignement; bool needAlign(uint32_t sectionAlignement) { return sectionAlignement >= MIN_SECTION_ALIGNMENT; } };
Method code for converting Rva to Raw const uint32_t numInvalidRaw = (uint32_t)( -1 ); uint32_t PeUtils::RvaToRaw( const PeImage& peImage, uint32_t rva ) { uint32_t result = INVALID_RAW; const auto& optionalHeader = peImage.NtHeaders.OptionalHeader; if (rva < optionalHeader.SizeOfHeaders) return rva; Aligner aligner(optionalHeader.FileAlignment, optionalHeader.SectionAlignment); if (peImage.NtHeaders.FileHeader.NumberOfSections > 0) { for (const auto& section : peImage.Sections) { if (section.PointerToRawData == 0) continue; auto sectionStart = aligner.getVirtualAddress(section.VirtualAddress); auto sectionSize = aligner.getSectionSize(section); auto sectionEnd = sectionStart + sectionSize; if (sectionStart <= rva && rva < sectionEnd) { auto sectionOffset = aligner.getFileOffset(section.PointerToRawData); sectionOffset += (rva - sectionStart); if (sectionOffset < peImage.SizeOfFileImage) result = sectionOffset; } }
What is improved:
- It takes into account the fact that it is not always necessary to align (see the needAlign method)
- The fact that the number of sections may be zero is taken into account. And such a file can be downloaded!
- When required, the PointerToRawData and VirtualAddress values ​​are aligned down
- More correct section size calculation
Nota bene:
In fact, even this is not the final version, but it is already closer to how the system loader understands such files.
Testing
Create a test suite of executable files and periodically check your RvaToRaw \ RawToRva for this set, which could be changed after refactoring or fixing bugs.
Ways to get test files:
- Apply an executable file wrapper that can create off-standard values ​​in the headers. An example of such a packer is Upack.exe, but there are many other
- Periodically replenish their collection of new malicious files, for example with MDL (see additional sources)
Nota bene:I apologize for the reminder, but any doubtful file is best run in a guest virtual machine, for example, based on VMWare or VirtualBox.
You can also check the correctness of your code using Hiew, IDA Pro or a debugger.
Additional sources:
- #include <winnt.h>. Hider is included in MSVC and not only. This leader should be a desktop reference for anyone who writes the parser of this file!
- Matt Pitrek. "PE and COFF object file formats". rsdn.ru/article/baseserv/pe_coff.xml
- Maxim M. Gumerov. "PE File Downloader". rsdn.ru/article/baseserv/peloader.xml
- Forums >> IDA Pro >> # new PE loader bug and new crack-me. www.openrce.org/forums/posts/969
- MDL. www.malwaredomainlist.com/mdl.php . Resource where you can download samples of questionable files
Post scriptum:
- I emphasize that the author did not set himself the goal of surprise or ridicule anyone. The author has a great desire to improve the quality of the system code. I really hope that in the future “great Google” will issue links to the fairly correct code RvaToRaw \ RawToRva
- The author will also be happy about any questions, any criticism and any wishes.