Jump to content
Tuts 4 You

Function hooking on x64


Netskyes

Recommended Posts

I'm trying to understand how hooking in general works, targetting x64 application. (As I'm totally beginneer, some questions might make no sense)

1. Is using assembly required to hook a function? (As inline assembly isn't supported)

2. What is the difference between jump and a trampoline?

3. Is the process of hooking procedure on x64 different?

4 - What does the following byte array actually represents? (Is it different on x64)

BYTE jump[6] = { 0xe9, 0x00, 0x00, 0x00, 0x00, 0xc3 };

5 - When finally writing to process, do we overwrite the original?

-----

So if I understand correctly. (Few steps)

Define original functions structure:

int (WINAPI* _Recv)(SOCKET s, char* buf, int len, int flags);`

Obtain the original function address:

 DWORD funcAddress = (DWORD)GetProcAddress(GetModuleHandle(moduleName), funcName);

Lets say I'm hooking a recv function. Copy first 6 bytes to _Recv. (Original is _Recv parameter)

CopyMemory(original, &funcAddress, 6);

How should I then calculate the position at where I should write the modified (jump, trampoline)??

Thanks!

  • Like 1
Link to comment

I will show you a general example of how I do it.

//The local hook which will be jumped to from the original API
//should have a similar signature to the original API

typedef long long (*HookFun)(long long arg1);
long long LocalHook(long long arg1)
{

	// Do your thing !

}


// pAddress = address where we want to place our hook
// dwJumpTo = address where we want to jump to
// dwLen    = length of the bytes to overwrite

void MakeJMP(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen)
{
	
    DWORD dwOldProtect, dwBkup, dwRelAddr;

    // give the paged memory read/write permissions
    
    VirtualProtect(pAddress, dwLen, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    
    // calculate the distance between our address and our target location
    // and subtract the 5bytes, which is the size of the jmp
    // (0xE9 0xAA 0xBB 0xCC 0xDD) = 5 bytes

    dwRelAddr = (DWORD) (dwJumpTo - (DWORD) pAddress) - 5;

    // overwrite the byte at pAddress with the jmp opcode (0xE9)
    
    *pAddress = 0xE9;
    
    // overwrite the next 4 bytes (which is the size of a DWORD)
    // with the dwRelAddr
    
    *((DWORD *)(pAddress + 0x1)) = dwRelAddr;

    // overwrite the remaining bytes with the NOP opcode (0x90)
    // NOP opcode = No OPeration
    
    for(DWORD x = 0x5; x < dwLen; x++) *(pAddress + x) = 0x90;

    // restore the paged memory permissions saved in dwOldProtect
    
    VirtualProtect(pAddress, dwLen, dwOldProtect, &dwBkup);
    
    return;

}




//At some point your will call this to apply the patch
void PatchMemory()
{


	//Load target module, DLL which contains the API you want to patch
	//We use LoadLibrary to make sure it's loaded by your APP
	HMODULE hModule = LoadLibrary(TEXT("TargetDLL.dll"));

	//get target API procedure address
	FARPROC ApiFunctionAddress = GetProcAddress(hModule, "TargetAPI");

	//get a temp address for your local procedure
	HookFun hook2 = LocalHook;

	//Apply the patch
	MakeJMP((BYTE*)(ApiFunctionAddress),(DWORD)hook2,6);

	
	return;
 

}

 

Edited by Kurapica
  • Like 4
  • Thanks 1
Link to comment

Thanks so much for your help! I've tried to implement that but for some reason the moment it hooks, the application crashes (closes). Would you know maybe why?

I also don't quite understand this part: 

// overwrite the next 4 bytes (which is the size of a DWORD)
// with the dwRelAddr
    
*((DWORD *)(pAddress + 0x1)) = dwRelAddr;

Doesn't it just get the pointer to location and overwrite that specific address? Since dwRelAddr is simply a location? Thanks :)

Edited by Netskyes
Link to comment

It's hard to debug such situations, I recommend using OutputDebugString to spit some info after each step.

also attach your target to a debugger and inspect the effect after each step, you can put a breakpoint on the beginning of

your code and debug it step by step inside the debugger !

have fun !

Link to comment

My (very old) x64 detour function:

Quote

void* DetourFunction(void* pSource, void* pDestination, int dwLen)

{

DWORD MinLen = 14;

if (dwLen < MinLen)

return NULL;

BYTE stub[] = {

0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [$+6]

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ptr

};

void* pTrampoline = VirtualAlloc(0, dwLen + sizeof(stub), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

DWORD dwOld = 0;

VirtualProtect(pSource, dwLen, PAGE_EXECUTE_READWRITE, &dwOld);

DWORD64 retto = (DWORD64)pSource + dwLen

// trampoline

memcpy(stub + 6, &retto, 8);

 

memcpy((void*)((DWORD_PTR)pTrampoline), pSource, dwLen);

memcpy((void*)((DWORD_PTR)pTrampoline + dwLen), stub, sizeof(stub));

// orig

memcpy(stub + 6, &pDestination, 8);

memcpy(pSource, stub, sizeof(stub));

for (int i = MinLen; i < dwLen; i++)

*(BYTE*)((DWORD_PTR)pSource + i) = 0x90;

VirtualProtect(pSource, dwLen, dwOld, &dwOld);

 

return (void*)((DWORD_PTR)pTrampoline);

}

Sry for format, i did this on my phone

It is basically like this:

At the beginning of hooked your function, jump to the hook.

At the end of the hook, jump to the trampoline.

The trampoline contains your first bytes you overwrote with the jmp instruction, at the end of it jump back to the beginning of the original function + jmp length, to avoid a loop.

 

My func is not taking account of relocations, but it should do well for most situations. 

 

@Kurapica, you are showing how to do a jmp in x86, but it won't work on x64. Intel's JMP instruction can handly only 4 bytes of jump offset, but on x64, it is 8 bytes, so you have to either do 

mov rax, dest

jmp rax

 

Or just do what i did to not touch any other registers. There are more ways though.

Edited by A200K
Tried to fix formatting
  • Like 1
Link to comment

It can work in some cases, but imagine the offset between your main image base and your dll which hooks a function in the main image is bigger than 4 bytes, it will obviously fail. Thats why its a bad practice to use JMP <offset> in x64 hooks.

Link to comment

Why not use one of the many existing hooking libraries to do this?

 - MinHook: https://github.com/TsudaKageyu/minhook
 - PolyHook: https://github.com/stevemk14ebr/PolyHook
 - MHook: http://codefromthe70s.org/mhook24.aspx

There is also Microsoft Detours Professional that includes 64bit support (commercial). madCHook is another designed in Delphi but works in C/C++ as well.

And so on. No need to reinvent the wheel with things like this.

Link to comment
17 hours ago, A200K said:

My (very old) x64 detour function:

Sry for format, i did this on my phone

It is basically like this:

At the beginning of hooked your function, jump to the hook.

At the end of the hook, jump to the trampoline.

The trampoline contains your first bytes you overwrote with the jmp instruction, at the end of it jump back to the beginning of the original function + jmp length, to avoid a loop.

 

My func is not taking account of relocations, but it should do well for most situations. 

 

@Kurapica, you are showing how to do a jmp in x86, but it won't work on x64. Intel's JMP instruction can handly only 4 bytes of jump offset, but on x64, it is 8 bytes, so you have to either do 

mov rax, dest

jmp rax

 

Or just do what i did to not touch any other registers. There are more ways though.

Thank you, this helps a lot! :) Also could you please tell me what is dwLen length of? Thanks!

Edited by Netskyes
Link to comment
8 hours ago, Netskyes said:

Thank you, this helps a lot! :) Also could you please tell me what is dwLen length of? Thanks!

dwLen is the amount of bytes we want to overwrite.

dwMinLen is the minimum amount of bytes we need to overwrite (6 bytes of opcodes + 8 bytes of actual address = 14), so you need to adapt dwLen to your actual function, to not break any instructions.

If you find a jump or anything else that relies on relative offsets inside of these first 14 bytes, the hook might not work.

For a more stable hook, you might take a look at the hooking libraries as atom0s pointed out.

  • Like 1
Link to comment
14 minutes ago, A200K said:

dwLen is the amount of bytes we want to overwrite.

dwMinLen is the minimum amount of bytes we need to overwrite (6 bytes of opcodes + 8 bytes of actual address = 14), so you need to adapt dwLen to your actual function, to not break any instructions.

If you find a jump or anything else that relies on relative offsets inside of these first 14 bytes, the hook might not work.

For a more stable hook, you might take a look at the hooking libraries as atom0s pointed out.

Thanks for explanation :D Well I would, but I'm really eager to learn first how this actually works without any external libs ^^

  • Like 1
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...