Search code examples
c++cwindowswinapiportable-executable

how to advance to next block of memory when relocating image


basically i just want to understand how does this works i saw a couple of people advancing to the second PIMAGE_BASE_RELOCATION struct (or second block of relocations) using current_base_relocation = reinterpret_cast<PIMAGE_BASE_RELOCATION>(reinterpret_cast<uint64_t>(current_base_relocation) + current_base_relocation->SizeOfBlock); where current_base_relocation is pointer to PIMAGE_BASE_RELOCATION the base struct and basically he adds the struct itself + another struct and size of entries and gets to the second block of memory but can someone explain how? for example for fixing imports in pe file when mapping it i could simple advance to the second struct in memory using ++struct to go to the second struct in the array but i don't understand how this one works.


Solution

  • You are asking why the following code works to advance to the next IMAGE_BASE_RELOCATION block when processing a PE file's IMAGE_DIRECTORY_ENTRY_BASERELOC directory:

    current_base_relocation = reinterpret_cast<PIMAGE_BASE_RELOCATION>(reinterpret_cast<uint64_t>(current_base_relocation) + current_base_relocation->SizeOfBlock);
    

    The IMAGE_BASE_RELOCATION structure is a header for the block and does not represent the whole block. If the structure did represent the whole block, you could advance it with a current_base_relocation++. This is why you are supplied with the size member (current_base_relocation->SizeOfBlock). Note that this size includes the size of the header structure (IMAGE_BASE_RELOCATION) AND the array that follows, in bytes. The pointer calculation to obtain the WORD-sized data array would be the first location past the IMAGE_BASE_RELOCATION structure (the header), and can be obtained like this:

    word* pCurBlockEntry = (word*)((byte*)current_base_relocation + sizeof(*current_base_relocation))) 
    

    Now to answer your question. The calculation you provided is likely one you'd see in the loop body, after it has processed the first block as you pointed out. It begins with the current pointer to a IMAGE_BASE_RELOCATION struct. The pointer is then cast to a 64-bit unsigned integer so that when you add a the byte size to it (current_base_relocation->SizeOfBlock) you advance by the number of bytes rather than by the number of IMAGE_BASE_RELOCATION structures (that would be a serious bug). Any unsigned integer would do. The 64-bit unsigned integer is used because it is the same integral size as a pointer (for 64-bit code), otherwise a 32-bit unsigned integer could be used.

    For instance, without the cast and assuming current_base_relocation->SizeOfBlock happens to be 32, pointer-arithmetic dictates your pointer would advance by 32 IMAGE_BASE_RELOCATION structures rather than than 32 bytes to the next block (this would include the current IMAGE_BASE_RELOCATION struct and trailing data array of 12 16-bit entries in this example). In reality, I think these blocks are all 4k though, at least according to the PE spec.

    Personally, when I want to advance a pointer by a byte size, I prefer to cast it as a byte* rather than an unsigned integer prior to the arithmetic. This simply prevents switching between integers and pointers which avoids compiler warnings when compiled as C++. However, both ways will work and achieve the same result, so your code is fine.

    With the value appropriately advanced by the correct number of bytes, the result is cast back to a IMAGE_BASE_RELOCATION pointer, since it should now be pointing at the beginning of the next block (the next IMAGE_BASE_RELOCATION structure).

    By the way, you'll know you've reached the end of the blocks when you have an entry where current_base_relocation->SizeOfBlock is zero.