4

I'm trying to hot patch an exe in memory, the source is available but I'm doing this for learning purposes. (so please no comments suggesting i modify the original source or use detours or any other libs)

Below are the functions I am having problems with.

vm_t* VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret )
{
    MessageBox(NULL, L"Oh snap! We hooked VM_Create!", L"Success!", MB_OK);
    return NULL;
}

void Hook_VM_Create(void)
{

    DWORD dwBackup;
    VirtualProtect((void*)0x00477C3E, 7, PAGE_EXECUTE_READWRITE, &dwBackup);

    //Patch the original VM_Create to jump to our detoured one.
    BYTE *jmp = (BYTE*)malloc(5);
    uint32_t offset = 0x00477C3E - (uint32_t)&VM_Create; //find the offset of the original function from our own
    memset((void*)jmp, 0xE9, 1);
    memcpy((void*)(jmp+1), &offset, sizeof(offset));
    memcpy((void*)0x00477C3E, jmp, 5);

    free(jmp);
}

I have a function VM_Create that I want to be called instead of the original function. I have not yet written a trampoline so it crashes (as expected). However the message box does not popup that I have detoured the original VM create to my own. I believe it is the way I'm overwriting the original instructions.

Adam
  • 707
  • 1
  • 7
  • 15

1 Answers1

4

I can see a few issues.

I assume that 0x00477C3E is the address of the original VM_Create function. You really should not hard code this. Use &VM_Create instead. Of course this will mean that you need to use a different name for your replacement function.

The offset is calculated incorrectly. You have the sign wrong. What's more the offset is applied to the instruction pointer at the end of the instruction and not the beginning. So you need to shift it by 5 (the size of the instruction). The offset should be a signed integer also.

Ideally, if you take into account my first point the code would look like this:

int32_t offset = (int32_t)&New_VM_Create - ((int32_t)&VM_Create+5);

Thanks to Hans Passant for fixing my own silly sign error in the original version!

If you are working on a 64 bit machine you need to do your arithmetic in 64 bits and, once you have calculated the offset, truncate it to a 32 bit offset.

Another nuance is that you should reset the memory to being read-only after having written the new JMP instruction, and call FlushInstructionCache.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Okay, I've updated that my offset so that it is now correct. I didn't think of adding 5 it makes sense now. But I'm still not getting a window saying that we've branched off to my vm_create. I know the way I'm writing bytes to memory is UGLY, is there a neater way to do this? Do you see any problems with the way I'm writing to memory? – Adam Nov 11 '11 at 05:58
  • Did you remember to turn the sign around in offset. In your terminology the code should be: `int32_t offset = (int32_t)&VM_Create + 5 - (int32_t)0x00477C3E;` – David Heffernan Nov 11 '11 at 06:18
  • The way you write to memory is fine. You should check the return value of `VirtualProtect` but if that did not work then you'd get a seg fault. You should pass 5 rather than 7 to `VirtualProtect`. Really, the only thing that can go wrong here is the calculation of the offset. – David Heffernan Nov 11 '11 at 06:20
  • 1
    The sign is wrong. Offset = &newFunc - (patchAddr + 5). -5. – Hans Passant Nov 11 '11 at 07:02
  • @HansPassant Thanks for that! I've fixed it now. Sometimes I wonder how I ever managed to get awarded not one but two degrees in mathematics!! ;-) – David Heffernan Nov 11 '11 at 07:08
  • @DavidHeffernan: FlushInstructionCache() doesn't do anything on x86/64. It's needed for ARM and likely Itanium. I don't think Windows cares much about the state of the application pages when the latter terminates. At least, not when they're still readable if they were readable originally. – Alexey Frunze Nov 11 '11 at 07:31
  • @Alex You don't them to be writeable though. Of course it works if they are but ideally it's worth restoring that protection. – David Heffernan Nov 11 '11 at 07:32
  • 1
    Okay, it's working perfectly now. I just have to write up my trampoline function now with the bytes I over write. And I understand the jump arithmetic perfectly now, the jmp instruction jumps relative from where it ends, not where the function begins (I knew this I don't know why I was thinking otherwise), so to calculate the offset we add 5 to the original vm_create, since the amount of data we wrote in for the jmp + relative address was 5 bytes. Thank you very much. And I did reset the memory to PAGE_EXECUTE_READ. – Adam Nov 11 '11 at 18:44
  • Glad you are happy. I love hooking like this. Get giddy on the power!! – David Heffernan Nov 11 '11 at 18:53
  • http://stackoverflow.com/questions/8099660/writing-a-trampoline-function I'm having trouble again, mind lending your expert aid? Or should I struggle a little longer :P – Adam Nov 11 '11 at 20:39