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