Netskyes Posted June 28, 2016 Posted June 28, 2016 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! 1
Kurapica Posted June 28, 2016 Posted June 28, 2016 (edited) 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 June 28, 2016 by Kurapica 4 1
Netskyes Posted June 28, 2016 Author Posted June 28, 2016 (edited) 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 June 28, 2016 by Netskyes
Kurapica Posted June 28, 2016 Posted June 28, 2016 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 !
A200K Posted June 29, 2016 Posted June 29, 2016 (edited) 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 June 29, 2016 by A200K Tried to fix formatting 1
Kurapica Posted June 29, 2016 Posted June 29, 2016 The JMP in x64 also needs 4 bytes, same thing and I can confirm this worked in several 64 bit apps.
A200K Posted June 29, 2016 Posted June 29, 2016 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.
atom0s Posted June 29, 2016 Posted June 29, 2016 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.
Netskyes Posted June 29, 2016 Author Posted June 29, 2016 (edited) 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 June 29, 2016 by Netskyes
A200K Posted June 30, 2016 Posted June 30, 2016 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. 1
Netskyes Posted June 30, 2016 Author Posted June 30, 2016 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 Well I would, but I'm really eager to learn first how this actually works without any external libs ^^ 1
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now