In this article, the definitions are
DWORD VirtualAddress
In EXEs, this field holds the RVA to where the loader should map the section. To calculate the real starting address of a given section in memory, add the base address of the image to the section's VirtualAddress stored in this field.
DWORD PointerToRawData
This is the file-based offset of where the raw data emitted by the compiler or assembler can be found. If your program memory maps a PE or COFF file itself (rather than letting the operating system load it), this field is more important than the VirtualAddress field. You'll have a completely linear file mapping in this situation, so you'll find the data for the sections at this offset, rather than at the RVA specified in the VirtualAddress field
Also RVA
is defined as
Many fields in PE files are specified in terms of RVAs. An RVA is simply the offset of some item, relative to where the file is memory-mapped
and
To convert an RVA into a usable pointer, simply add the RVA to the base address of the module. The base address is the starting address of a memory-mapped EXE or DLL
The problem in hand is to reach the import section
of a PE file.
hFile = CreateFile(..);
hFileMapping = CreateFileMapping(..);
lpFileBase = MapViewOfFile(..);
ImageBase = (PIMAGE_DOS_HEADER)lpFileBase;
PEHeader = (ImageBase + ImageBase->e_lfanew);
Now to get hold of import table
PIMAGE_OPTIONAL_HEADER PEImageOptionalHeader = &(PEHeader->OptionalHeader);
IMAGE_DATA_DIRECTORY importTable = PEImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
Since importTable.VirtualAddress
is an RVA, to get a usable pointer, i can add the base of the image file.
So ImageBase + importTable.virtualAddress
should get me to import section.But it does not.Why?
Then, if i reach at the correct section header(generally .idata
) and do this.
ImageBase + pointerToSection->PointerToRawData;
The above correctly takes me to array of IMAGE_IMPORT_DESCRIPTORS
.I understand that using pointerToSection->virtualAddress
instead of PointerToRawData
above, would not work as i am mapping the PE file myself.
Now to get the name
of the item, the loaded module is dependent on,I can use pointer to IMAGE_IMPORT_DESCRIPTORS
using the field name
which is again anRVA
.To convert an RVA
, i just have to add the ImageBase
..
LPSTR libname = (PCHAR)((DWORD)ImageBase+ImageimportDescriptor->Name);
But it does not work.Why?To convert an RVA
, we simply add base address of image.The below works
ImageBase+ImageimportDescriptor->Name + pointerToSection->PointerToRawData - pointerToSection->virtualAddress
And every time i need some info within a section, i need to make this adjustment
pointerToSection->PointerToRawData - pointerToSection->virtualAddress
Why is this adjustment required?
First, this line:
PEHeader = (ImageBase + ImageBase->e_lfanew);
Is incorrect. ImageBase
is of type PIMAGE_DOS_HEADERS
so when you add to it ImageBase->e_lfanew
, which is a DWORD
you are doing pointer arithmetic, that is, you are adding to ImageBase
as many bytes as (ImageBase->e_lfanew)*sizeof(IMAGE_DOS_HEADERS)
which is not what you want. What you want is to advance ImageBase->e_lfanew
bytes form the place ImageBase
points to. You achieve this by doing the following:
PIMAGE_NT_HEADERS PEheader = (PIMAGE_NT_HEADERS) ((PBYTE)(ImageBase) + dosHeader->e_lfanew);
Notice the cast to PBYTE
, this makes the operation advance byte by byte.
This is very common when dealing with PE file since many times you want to advance n bytes from a pointer and not n data structure the pointer points to. In the article you refered to it even says in a comment:
// Ignoring typecasts and pointer conversion issues for clarity...
pNTHeader = dosHeader + dosHeader->e_lfanew;
Now to your question about VirtualAddress
and PointerToRawData
:
The line ImageBase + importTable.virtualAddress
is also incorrect for the following reason:
The PE Loader doesn't map all the executable file continuously in memory like you do in your file mapping, it maps each section at its corresponding VirtualAddress
. In the latter situation, to get a VA from an RVA you just add the ImageBase
to the RVA
and it works, because the file on disk is meant to be a representation of what the PE loader maps in memory. However, since you are not mapping each sections where the PE loader would map them, adding the ImageBase
to an RVA
like you did doesn't work.
You need a function that given an RVA, gives you the file offset corresponding to that RVA in the file on disk. to get a VA you just need to add this file offset to the byte pointer that points to your mapped file.
Whenever you have an RVA (like the RVA to the import section, or to a name string or whatever), in order to access it you must do the following.
PBYTE actualAddress = (PBYTE) (lpFileBase + RVAtoFileOffset(pNTHeader, RVA))
Here is the function: I posted it on pastebin because I can't manage to format it correctly here.
Here is how it works: When you have an RVA, that RVA must lie within a section, so you traverse all section headers (which are right after the optional header) and whe you find the section where the RVA lies in, you calculate the offset of that RVA within that section RVA - VirtualAddress
and add it to the PointerToRawData
of that section, now you have the file offset of the RVA.
If the RVA is invalid (it doesn't lie within any section), the function returns 0.
You need to do this in order to access the import directory or the name of each import descriptor, since what you have are RVAs.
I hope I have helped.
Once we have valid PE/NT header address we can directly jump to first section:
PIMAGE_SECTION_HEADER sectionHeader;
sectionHeader = IMAGE_FIRST_SECTION(peHeader);
Also we can find name of section by masking
sectionHeader->Characteristics
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With