I have this PELoader project which basically loads a PE into memory. and it's working fine with x86 PEs but when tried to make it work with x64s I got stuck.
What I've tried is:
IMAGE_NT_HEADERS*
with IMAGE_NT_HEADERS64*
(DWORD)
with reinterpret_cast<DWORD_PTR>
and the exception changed. I guess it was necessary but not enough.Update 2
I just found out that the problem is in the reallocation. (check the code)
I've fixed an Error before I discovered this and it was in the data type that stores the base address from the optional header.
it was stored in a DWORD but I've changed it to char* and problem solved.
so I think the Error is in the casting or some data types of some values.
Here is the relocation Code:
/** Handle relocations **/
//this is how much we shifted the ImageBase
DWORD_PTR delta_VA_reloc = (reinterpret_cast<DWORD_PTR>(ImageBase)) - p_NT_HDR->OptionalHeader.ImageBase;
// if there is a relocation table, and we actually shitfted the ImageBase
if (data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0 && delta_VA_reloc != 0) {
printf("\n[*] The allocated address is not the prefered address, started relocating\n");
//calculate the relocation table address
IMAGE_BASE_RELOCATION* p_reloc = (IMAGE_BASE_RELOCATION*)(ImageBase + data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
//once again, a null terminated array
while (p_reloc->VirtualAddress != 0) {
// how many relocations in this block
// ie the total size, minus the size of the "header", divided by 2 (those are words, so 2 bytes for each)
//std::cout << sizeof(WORD) << "\n"; sizeof word is 2
DWORD size = (p_reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
// the first relocation element in the block, right after the header (using pointer arithmetic again)
WORD* reloc = (WORD*)(p_reloc + 1);
for (int i = 0; i < size; ++i) {
//type is the first 4 bits of the relocation word
int type = reloc[i] >> 12;
// offset is the last 12 bits
unsigned long long int offset = reloc[i] & 0x0fff;
//printf("--------------- %#llx\n", offset);
//this is the address we are going to change
DWORD* change_addr = (DWORD*)(ImageBase + p_reloc->VirtualAddress + offset);
// there is only one type used that needs to make a change
// When you relocate you should look if the flag HIGHT_LOW is active for PE32 and DIR64 for PE32+
switch (type) {
case IMAGE_REL_BASED_HIGHLOW://for x86
*change_addr += delta_VA_reloc;
break;
case IMAGE_REL_BASED_DIR64://for x64
*change_addr += delta_VA_reloc;
break;
default:
break;
}
}
// switch to the next relocation block, based on the size
p_reloc = (IMAGE_BASE_RELOCATION*)((reinterpret_cast<DWORD_PTR>(p_reloc)) + p_reloc->SizeOfBlock);
}
}
I found the problem:
I don't know exactly but when using DWORD* in this line:
DWORD* change_addr = (DWORD*)(ImageBase + p_reloc->VirtualAddress + offset);
and I printed the chnage_addr, in x86 it prints the whole 6 bytes that represents the address.
but in x64 it only prints 4 bytes. I think in windows DWORD* is smaller in x64.
So the solution I found is using long data types
unsigned long long int* change_addr = (unsigned long long int*)(ImageBase + p_reloc->VirtualAddress + offset);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.