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;
}