If you don’t know what hooking is, Wikipedia will help you out.

In this article, we’re going to hook/redirect calls to the well-known API function MessageBoxA. The technique is actually fairly simple and short, and I will keep it that way.

In order to overwrite it, we need to find where the function resides in memory. I created a test project in Visual Studio, and wrote:

// Get address for MessageBoxA FARPROC addr = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); // Print address to debugger char buf[16]; sprintf(buf, "%08x

", addr); OutputDebugStringA(buf);

It said “7691fd1e” in the debugger output window. This address could change from time to time though, if for example the DLL is forced by the PE loader to relocate.

Okay? That was easy! So let’s write some machine code to MessageBoxA to jmp to our function instead. Since arguments are put on the stack, we can mess with EAX as much as we want (thanks to stdcall!).

For simplicity’s sake, I used the following code to jump to our code:

mov eax, our_address jmp eax

Or, when converted to binary machine code it is B8 AABBCCDD FFE0, where AABBCCDD is our address.

// Overwrite stuff! unsigned char* func = (unsigned char*) addr; func[0] = 0xb8; // mov eax, our_addr func[1] = our_addr & 0xff; func[2] = (our_addr >> 8) & 0xff; func[3] = (our_addr >> 16) & 0xff; func[4] = (our_addr >> 24) & 0xff; func[5] = 0xff; // jmp eax func[6] = 0xe0;

Run it, and WHAT?! It raised an exception. Windows has some memory protection flags we need to fix before our writes:

// Make sure we have READ + WRITE at address! DWORD oldProtection; VirtualProtect(addr, 1024, PAGE_EXECUTE_READWRITE, &oldProtection);

To my suprise, it’s working perfectly! The whole code is below. Note that it is very important that our proxy function has the same calling convention as the original function, namely __stdcall, otherwise the stack will get messed up.

int __stdcall OurMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { // All redirected calls will land here! OutputDebugStringA("Intercepted call:"); OutputDebugStringA(lpText); return 0; } void hook() { // Get address for MessageBoxA FARPROC addr = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); // Print address to debugger char buf[16]; sprintf(buf, "%08x

", addr); OutputDebugStringA(buf); // Unprotect 1024 bytes DWORD oldProtection; VirtualProtect(addr, 1024, PAGE_EXECUTE_READWRITE, &oldProtection); DWORD our_addr = (DWORD) &OurMessageBoxA; // Write some code to address! unsigned char* func = (unsigned char*) addr; // mov eax, our_addr func[0] = 0xb8; func[1] = our_addr & 0xff; func[2] = (our_addr >> 8) & 0xff; func[3] = (our_addr >> 16) & 0xff; func[4] = (our_addr >> 24) & 0xff; // jmp eax func[5] = 0xff; func[6] = 0xe0; // Restore protection VirtualProtect(addr, 1024, oldProtection, NULL); MessageBoxA(0, "Test", "Test", 0); }

I hope you enjoyed this as much as I did. There are still some challenges left for the reader, including figuring out how to return back to the original function after the call has been intercepted (this might be tricky since we’ve overwritten the beginning of the function).