API Hooking

API hooking is a technique used to intercept and modify the behavior of an API function.

API hooking is mostly used for malware analysis and debugging but it can be used to be used in malware development to gather sensitive information or data, modify or intercept function calls for malicious purposes or bypass security measures by altering how the operating system or a program behaves.

Detours Library

The Detours Hooking Library, is a software library developed by Microsoft Research that allows for intercepting and redirecting function calls in Windows.

To use the Detours library's functions, the Detours repository must be downloaded and compiled to get the static library files (.lib) files needed for the compilation. In addition to that the detours.h header file should be included.

https://github.com/microsoft/Detours/wiki/ https://github.com/microsoft/Detours/wiki/Using-Detours

#include <Windows.h>
#include <stdio.h>
#include "detours.h" // from the detours library

// if compiling as 64-bit
#ifdef _M_X64
#pragma comment (lib, "detoursx64.lib")
#endif // _M_X64

// if compiling as 32-bit
#ifdef _M_IX86
#pragma comment (lib, "detoursx86.lib")
#endif // _M_IX86

// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxa
typedef int (WINAPI* fnMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

// used as a unhooked MessageBoxA in `MyMessageBoxA`
// and used by `DetourAttach` & `DetourDetach`
fnMessageBoxA g_pMessageBoxA = MessageBoxA;

// the function that will run instead MessageBoxA when hooked
INT WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {

	printf("[+] Original Parameters : \n");
	printf("\t - lpText	: %s\n", lpText);
	printf("\t - lpCaption	: %s\n", lpCaption);

	return g_pMessageBoxA(hWnd, "Malware Development Is Cool", "Hooked MsgBox", uType);
}

//	DETOURS UNHOOKING ROUTINE:
BOOL Unhook() {

	DWORD	dwDetoursErr = NULL;

	if ((dwDetoursErr = DetourTransactionBegin()) != NO_ERROR) {
		printf("[!] DetourTransactionBegin Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}
	if ((dwDetoursErr = DetourUpdateThread(GetCurrentThread())) != NO_ERROR) {
		printf("[!] DetourUpdateThread Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}
	if ((dwDetoursErr = DetourDetach((PVOID)&g_pMessageBoxA, MyMessageBoxA)) != NO_ERROR) {
		printf("[!] DetourDetach Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}

	// actual hook removal happen after `DetourTransactionCommit`
	if ((dwDetoursErr = DetourTransactionCommit()) != NO_ERROR) {
		printf("[!] DetourTransactionCommit Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}

	return TRUE;
}

//	DETOURS HOOKING ROUTINE:
BOOL InstallHook() {
	
	DWORD	dwDetoursErr = NULL;

	if ((dwDetoursErr = DetourTransactionBegin()) != NO_ERROR) {
		printf("[!] DetourTransactionBegin Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}
	if ((dwDetoursErr = DetourUpdateThread(GetCurrentThread())) != NO_ERROR) {
		printf("[!] DetourUpdateThread Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}
	if ((dwDetoursErr = DetourAttach((PVOID)&g_pMessageBoxA, MyMessageBoxA)) != NO_ERROR) {
		printf("[!] DetourAttach Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}

	// actual hook installing happen after `DetourTransactionCommit`
	if ((dwDetoursErr = DetourTransactionCommit()) != NO_ERROR) {
		printf("[!] DetourTransactionCommit Failed With Error : %d \n", dwDetoursErr);
		return FALSE;
	}

	return TRUE;
}

int main() {

	// will run
	MessageBoxA(NULL, "What Do You Think About Malware Development ?", "Original MsgBox", MB_OK | MB_ICONQUESTION);

//------------------------------------------------------------------
//  hooking
	
	printf("[i] Installing The Hook ... ");
	if (!InstallHook()) {
		return -1;
	}
	printf("[+] DONE \n");
	

//------------------------------------------------------------------	
//  wont run - hooked
	
	MessageBoxA(NULL, "Malware Development Is Bad", "Original MsgBox", MB_OK | MB_ICONWARNING);

//------------------------------------------------------------------
//  unhooking
	
	printf("[i] Removing The Hook ... ");
	if (!Unhook()) {
		return -1;
	}
	printf("[+] DONE \n");

//------------------------------------------------------------------
//  will run - hook disabled
	
	MessageBoxA(NULL, "Normal MsgBox Again", "Original MsgBox", MB_OK | MB_ICONINFORMATION);


	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

Minhook Library

Minhook is a hooking library written in C that can be used to achieve API hooking.

Is simpler and offers lightweight APIs.

Minhook library requires the static .lib file and the MinHook.h header file to be included in the VS project.

https://github.com/TsudaKageyu/minhook

https://github.com/TsudaKageyu/minhook/blob/master/include/MinHook.h

#include <Windows.h>
#include <stdio.h>

#include "MinHook.h" // from the minhook library

// if compiling as 64-bit
#ifdef _M_X64
#pragma comment (lib, "libMinHook.x64.lib")
#endif // _M_X64

// if compiling as 32-bit
#ifdef _M_IX86
#pragma comment (lib, "libMinHook.x86.lib")
#endif // _M_IX86


// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxa
typedef int (WINAPI* fnMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

// used as a unhooked MessageBoxA in `MyMessageBoxA`
// and used by `MH_CreateHook`
fnMessageBoxA g_pMessageBoxA = NULL;

// the function that will run instead MessageBoxA when hooked
INT WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {

	printf("[+] Original Parameters : \n");
	printf("\t - lpText	: %s\n", lpText);
	printf("\t - lpCaption	: %s\n", lpCaption);

	return g_pMessageBoxA(hWnd, "Malware Development Is Cool", "Hooked MsgBox", uType);
}

//	MINHOOK HOOKING ROUTINE:
BOOL InstallHook() {
	
	DWORD		dwMinHookErr = NULL;

	if ((dwMinHookErr = MH_Initialize()) != MH_OK) {
		printf("[!] MH_Initialize Failed With Error : %d \n", dwMinHookErr);
		return FALSE;
	}

	if ((dwMinHookErr = MH_CreateHook(&MessageBoxA, &MyMessageBoxA, &g_pMessageBoxA)) != MH_OK) {
		printf("[!] MH_CreateHook Failed With Error : %d \n", dwMinHookErr);
		return FALSE;
	}

	if ((dwMinHookErr = MH_EnableHook(&MessageBoxA)) != MH_OK) {
		printf("[!] MH_EnableHook Failed With Error : %d \n", dwMinHookErr);
		return -1;
	}

	return TRUE;
}

//	MINHOOK UNHOOKING ROUTINE:

BOOL Unhook() {
	
	DWORD		dwMinHookErr = NULL;

	if ((dwMinHookErr = MH_DisableHook(&MessageBoxA)) != MH_OK) {
		printf("[!] MH_DisableHook Failed With Error : %d \n", dwMinHookErr);
		return -1;
	}

	if ((dwMinHookErr = MH_Uninitialize()) != MH_OK) {
		printf("[!] MH_Uninitialize Failed With Error : %d \n", dwMinHookErr);
		return -1;
	}
}

int main() {

	// will run
	MessageBoxA(NULL, "Hello", "Original MsgBox", MB_OK | MB_ICONQUESTION);

//------------------------------------------------------------------
//  hooking
	printf("[i] Installing The Hook ... ");
	if (!InstallHook()) {
		return -1;
	}
	printf("[+] DONE \n");

//------------------------------------------------------------------	
//  wont run - hooked

	MessageBoxA(NULL, "Not hello", "Original MsgBox", MB_OK | MB_ICONWARNING);

//------------------------------------------------------------------
//  unhooking
	printf("[i] Removing The Hook ... ");
	if (!Unhook()) {
		return -1;
	}
	printf("[+] DONE \n");

//------------------------------------------------------------------
//  will run - hook disabled

	MessageBoxA(NULL, "Normal MsgBox Again", "Original MsgBox", MB_OK | MB_ICONINFORMATION);

	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

Custom Trampoline Shellcode

One of the ways to hook a function is to overwrite its first few instructions with new ones. These new instructions are the trampoline which is responsible for altering the execution flow of the function to the replacement function. This trampoline is typically a small jump shellcode that executes a jmp instruction to the address of the function to be executed.

#include <Windows.h>
#include <stdio.h>

// if compiling as 64-bit
#ifdef _M_X64
#define TRAMPOLINE_SIZE			13
#endif // _M_X64

// if compiling as 32-bit
#ifdef _M_IX86
#define TRAMPOLINE_SIZE			7
#endif // _M_IX86

typedef unsigned long long uint64_t;
typedef unsigned int       uint32_t;
typedef unsigned char      uint8_t;


typedef struct _HookSt{

	PVOID	pFunctionToHook;						// address of the function to hook
	
	PVOID	pFunctionToRun;							// address of the function to run instead
	
	BYTE	pOriginalBytes[TRAMPOLINE_SIZE];		// buffer to keep some original bytes (needed for cleanup)
	
	DWORD	dwOldProtection;						// holds the old memory protection of the "function to hook" address (needed for cleanup)

}HookSt, *PHookSt;


BOOL InitializeHookStruct(IN PVOID pFunctionToHook, IN PVOID pFunctionToRun, OUT PHookSt Hook) {

	// checking if null
	if (pFunctionToHook == NULL || pFunctionToRun == NULL || Hook == NULL)
		return FALSE;
	
	// filling up the struct
	Hook->pFunctionToHook	= pFunctionToHook;
	Hook->pFunctionToRun	= pFunctionToRun;

	// save original bytes of the same size that we will overwrite (that is TRAMPOLINE_SIZE)
	// this is done to be able to do cleanups when done
	memcpy(Hook->pOriginalBytes, pFunctionToHook, TRAMPOLINE_SIZE);

	// changing the protection to RWX so that we can modify the bytes 
	// we are saving the old protection to the struct (to re-place it at cleanup)
	if (!VirtualProtect(pFunctionToHook, TRAMPOLINE_SIZE, PAGE_EXECUTE_READWRITE, &Hook->dwOldProtection)) {
		printf("[!] VirtualProtect Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	return TRUE;
}


BOOL InstallHook (IN PHookSt Hook) {

	// checking if null
	if (Hook == NULL || Hook->dwOldProtection == NULL || Hook->pFunctionToHook == NULL || Hook->pFunctionToRun == NULL || Hook->pOriginalBytes == NULL)
		return FALSE;

#ifdef _M_X64

	// 64-bit trampoline
	uint8_t		uTrampoline [] = {
			0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, pFunctionToRun
			0x41, 0xFF, 0xE2                                            // jmp r10
	};

	// patching the shellcode with the address to jump to (pFunctionToRun)
	uint64_t uPatch = (uint64_t)(Hook->pFunctionToRun);
	// copying the address of the function to jump to, to the offset '2' in uTrampoline
	memcpy(&uTrampoline[2], &uPatch, sizeof(uPatch));

#endif // _M_X64

#ifdef _M_IX86

	// 32-bit trampoline
	uint8_t		uTrampoline[] = {
	   0xB8, 0x00, 0x00, 0x00, 0x00,     // mov eax, pFunctionToRun
	   0xFF, 0xE0                        // jmp eax
	};
	
	// patching the shellcode with the address to jump to (pFunctionToRun)
	uint32_t uPatch = (uint32_t)(Hook->pFunctionToRun);
	// copying the address of the function to jump to, to the offset '1' in uTrampoline
	memcpy(&uTrampoline[1], &uPatch, sizeof(uPatch));
#endif // _M_IX86

	
	// placing the trampoline function - installing the hook
	memcpy(Hook->pFunctionToHook, uTrampoline, sizeof(uTrampoline));

	return TRUE;
}


BOOL RemoveHook (IN PHookSt Hook) {

	// checking if null
	if (Hook == NULL || Hook->dwOldProtection == NULL || Hook->pFunctionToHook == NULL || Hook->pOriginalBytes == NULL)
		return FALSE;

	DWORD	dwOldProtection		= NULL;

	// copying the original bytes over
	memcpy(Hook->pFunctionToHook, Hook->pOriginalBytes, TRAMPOLINE_SIZE);
	// cleaning up our buffer
	memset(Hook->pOriginalBytes, '\0', TRAMPOLINE_SIZE);
	// setting the old memory protection back to what it was before hooking 
	if (!VirtualProtect(Hook->pFunctionToHook, TRAMPOLINE_SIZE, Hook->dwOldProtection, &dwOldProtection)) {
		printf("[!] VirtualProtect Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// setting all to null
	Hook->pFunctionToHook	= NULL;
	Hook->pFunctionToRun	= NULL;
	Hook->dwOldProtection	= NULL;

	return TRUE;
}


// the function that will run instead MessageBoxA when hooked
INT WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {

	printf("[+] Original Parameters : \n");
	printf("\t - lpText	: %s\n", lpText);
	printf("\t - lpCaption	: %s\n", lpCaption);

	return MessageBoxW(hWnd, L"Malware Development Is Cool", L"Hooked MsgBox", uType);
}


int main() {

//------------------------------------------------------------------
// Initializing the structure (needed before installing/removing the hook)
	HookSt st = { 0 };

	if (!InitializeHookStruct(&MessageBoxA, &MyMessageBoxA, &st)) {
		return -1;
	}

//------------------------------------------------------------------
// will run

	MessageBoxA(NULL, "What Do You Think About Malware Development ?", "Original MsgBox", MB_OK | MB_ICONQUESTION);

//------------------------------------------------------------------
//  hooking
	printf("[i] Installing The Hook ... ");
	if (!InstallHook(&st)) {
		return -1;
	}
	printf("[+] DONE \n");
	
//------------------------------------------------------------------	
//  wont run - hooked
	MessageBoxA(NULL, "Malware Development Is Bad", "Original MsgBox", MB_OK | MB_ICONWARNING);

//------------------------------------------------------------------
//  unhooking
	printf("[i] Removing The Hook ... ");
	if (!RemoveHook(&st)) {
		return -1;
	}
	printf("[+] DONE \n");


//------------------------------------------------------------------
//  will run - hook disabled

	MessageBoxA(NULL, "Normal MsgBox Again", "Original MsgBox", MB_OK | MB_ICONINFORMATION);


	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

SetWindowsHookEx WinAPI

This API is employed to keep track of certain types of system events. It executes a callback function whenever a certain event is triggered. The type of events is limited to those provided by Windows.

Example of monitoring mouse clicks:

#include <Windows.h>
#include <stdio.h>

#define MONITOR_TIME   20000 // monitor mouse clicks for 20 seconds

/*
    - SetWindowsHookExW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw
    - CallNextHookEx: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-callnexthookex
    - UnhookWindowsHookEx: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unhookwindowshookex
    
    - GetMessageW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagew
    - DefWindowProcW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowprocw 
*/

// global hook handle variable
HHOOK g_hMouseHook      = NULL;


// the callback function that will be executed whenever the user clicked a mouse button
LRESULT HookCallback(int nCode, WPARAM wParam, LPARAM lParam){

    if (wParam == WM_LBUTTONDOWN){
        printf("[ # ] Left Mouse Click \n");
    }
    
    if (wParam == WM_RBUTTONDOWN) {
        printf("[ # ] Right Mouse Click \n");
    }
    
    if (wParam == WM_MBUTTONDOWN) {
        printf("[ # ] Middle Mouse Click \n");
    }
    
    // moving to the next hook in the hook chain
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

BOOL MouseClicksLogger(){
    
    MSG         Msg         = { 0 };

    // installing hook 
    g_hMouseHook = SetWindowsHookExW(
        WH_MOUSE_LL,
        (HOOKPROC)HookCallback,
        NULL,  
        NULL
    );
    if (!g_hMouseHook) {
        printf("[!] SetWindowsHookExW Failed With Error : %d \n", GetLastError());
        return FALSE;
    }

    // process unhandled events
    while (GetMessageW(&Msg, NULL, NULL, NULL)) {
        DefWindowProcW(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
    }

    /* 
    This is another way to process unhandled events  

    while (GetMessageW(&Msg, NULL, NULL, NULL)) {
        TranslateMessage(&Msg);
        DispatchMessageW(&Msg);
    }
    */
    
    return TRUE;
}

int main() {


    HANDLE  hThread         = NULL;
    DWORD   dwThreadId      = NULL;

    hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MouseClicksLogger, NULL, NULL, &dwThreadId);
    if (hThread) {
        printf("\t\t<<>> Thread %d Is Created To Monitor Mouse Clicks For %d Seconds <<>>\n\n", dwThreadId, (MONITOR_TIME / 1000));
        WaitForSingleObject(hThread, MONITOR_TIME);
    }


    if (g_hMouseHook && !UnhookWindowsHookEx(g_hMouseHook)) {
        printf("[!] UnhookWindowsHookEx Failed With Error : %d \n", GetLastError());
    }

    return 0;
}

Last updated