0

I'm trying to write a thunk for __thiscall using a struct.

I've tested this struct and it works:

#pragma pack(push, 1)
struct Thunk
{
   unsigned short leaECX;       
   unsigned long pThis;     
   unsigned char movEAX;       
   unsigned long pMemFunc;    
   unsigned short jmpEAX;       
};
#pragma pack(pop)

I fill this struct with the following bytecode (which I found online):

//Load effective address of this to ECX
//because __thiscall expect to get 'this' in ECX
leaECX = 0x0D8D;
pThis = here goes 'this' pointer;
//Move member function pointer to EAX
movEAX = 0xB8;
pMemFunc = here goes pointer to member function;
//Jump to member function
jmpEAX = 0xE0FF;

My question is can the movEAX and jmpEAX instructions be replaced with bytecode for assembly call instruction ?

If so how do I do it ?

I'm allocating this struct using VirtualAlloc and this flags MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE.

Is this a compact way or does it waste memory (allocate whole page instead of sizeof(Thunk)) ?

Jester
  • 56,577
  • 4
  • 81
  • 125
JobNick
  • 473
  • 1
  • 6
  • 18
  • 1
    I see two questions here. The second one should be answerable simply by reading the documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887%28v=vs.85%29.aspx which says allocations are 1 page in size (default: 4K), aligned on an allocation granularity boundary (default: 64k http://stackoverflow.com/q/438863/845092) – Mooing Duck Dec 17 '14 at 20:13

1 Answers1

0

You can use call but then of course execution will return to your thunk so you need more code afterwards. Also if you get rid of the mov I assume you will want to do the call address variant, in which case be mindful of the fact that that uses relative encoding, so you can't just poke your address into memory.

You can switch to relative jump to get rid of the mov, using something like this:

#pragma pack(push, 1)
struct Thunk
{
   unsigned short leaECX;       
   unsigned long pThis;     
   unsigned char jmp; 
   unsigned long pOffset;
};
#pragma pack(pop)

//Load effective address of this to ECX
//because __thiscall expect to get 'this' in ECX
leaECX = 0x0D8D;
pThis = here goes 'this' pointer;
jmp = 0xE9;
pOffset = (char*)address_of_member - (char*)&thunk.pOffset - 4;

Since memory protections are page granular you will need at least a page (VirtualAlloc does round up for you automatically). If you have multiple thunks you can of course pack them into the same page.

Jester
  • 56,577
  • 4
  • 81
  • 125
  • If I understand correctly if I replace movEAX+jmpEAX with call I'll have do add some code after call ? If so is it better to leave it as it is now ? – JobNick Dec 17 '14 at 20:35
  • Yes, the difference between `call` and `jmp` is that `call` will return to your thunk so you can do further processing. If you don't need that, then leave it `jmp`. You can use the relative `jmp` to get rid of the `mov` if you want, but then you need to calculate the offset. – Jester Dec 17 '14 at 20:37
  • Can you please elaborate how to do this calculation ? As it works now after my thunk get executed and calls member function program's flow return to the point after the thunk was called. If I've used `call` program's flow will return to the thunk itself ? – JobNick Dec 17 '14 at 20:42
  • Updated answer. Yes, with `call` flow would return to thunk which should eventually do a `ret` itself and that would return to the caller. – Jester Dec 17 '14 at 20:51
  • 1
    ATL's thunk relative `jmp` offset is: address_of_member - (pThunk+sizeof(Thunk)). Is it the same as your calculation ? – JobNick Dec 17 '14 at 21:01
  • Yes, as long as the offset is at the end of the struct because that uses the size of the whole struct. Also be mindful about C pointer arithmetic. – Jester Dec 17 '14 at 21:24
  • 1
    Is `-4` from your pointer arithmetic example is the result of sizeof(pOffset) ? I mean why is it magic number ? – JobNick Dec 18 '14 at 05:09
  • Yes, it is. Technically the offset is relative to the next instruction that would follow the `jmp`, that is how much extra adjustment needs to be applied to the instruction pointer in addition to incrementing by the size of the current instruction (the cpu automatically does that). – Jester Dec 18 '14 at 12:18