Keylogging in Linux (Part 1): Understanding Attacks and Defenses
Keylogging turns up more often than people think. You see it in audits, red team work, and during investigations where credentials quietly leak through input streams. This piece breaks down how it actually happens on Linux — where keystrokes travel, how the system reports them, and how simple code can listen in.
We’ll trace that path from keyboard to kernel to userspace, using short examples built from real testing. You’ll see how to spot legitimate keyboard devices under /dev/input, read their events, and interpret what the data means.
If you work in detection or response, start here. Run the examples in a lab, watch the data move, and learn what normal looks like before you hunt for keyloggers in the wild.
What Is a Keylogger Attack?
A keylogger attack happens when software records keyboard input without the user knowing. The keylogger captures each key event as it’s generated, often before the system turns it into text. Where it runs changes what it can see; some operate in user space by reading device files, others sit inside X or Wayland, and a few hook deeper in the kernel. On Linux, the evdev layer makes visibility complicated since hardware events are abstracted, and several points can be tapped quietly.
Attackers use keyloggers to collect credentials, monitor privileged users, or track insider activity. They’ve been found pulling SSH passphrases, sudo prompts, and desktop logins during real investigations. Keylogging also has legitimate uses in red team work, where it helps measure exposure and test how well monitoring tools detect input capture.
Ethical use matters. Run this kind of testing only in a controlled lab or under written authorization. Capture baseline input traces, record what was collected, and leave an audit trail. The goal is to understand how keylogging works on Linux so you can recognize it, not exploit it.
Purpose of Keylogging in Linux SystemsBusiness Cybersecurity Esm W400Business Cybersecurity Esm W400Business Cybersecurity Esm W400
Keylogging on Linux can serve two very different purposes. Attackers use a keylogger to capture credentials or session data from users with elevated access. It’s one of the quieter ways to collect passwords or tokens from terminal sessions, hypervisors, or remote desktops. The same mechanics that make it stealthy also make it useful in controlled testing.
In red team work, keylogging helps show how exposed a system really is. Running a basic keylogger during a simulated breach confirms whether monitoring tools catch the activity or miss it. It also exposes where input events leak across layers — from kernel space, through evdev, up to X or Wayland — and how permissions affect visibility.
Blue teams approach it from the other side. By studying how a keylogger reads from devices or intercepts events, they learn what indicators to watch for in logs and memory. Both views matter. Keylogging gives offensive teams a way to measure detection coverage and gives defenders the patterns they need to spot it before real attackers do.
Userspace vs Kernel Space Keylogging
Where a keylogger runs changes everything: privileges, stealth, how you detect it, and how long it survives. Userspace loggers read device files or hook into X11 or Wayland. They need fewer privileges to run, and they are easier to debug. They also leave obvious signals: open file descriptors to /dev/input, suspicious processes holding device handles, and readable event streams you can trace in a lab. Detection is straightforward if you know what to look for, but noisy environments make false positives common.
Kernel space keylogging sits deeper and lasts longer. Kernel modules, patched drivers, or hypervisor-level hooks capture input before userspace sees it. That gives the attacker higher persistence and fewer visible artifacts in userland. It also raises the cost and risk for the attacker, because loading or modifying kernel code often requires elevated privileges or an exploit. Detection shifts to different signals: unexpected kernel modules, unsigned or out-of-tree drivers, altered interrupt handlers, and anomalous memory allocations around input subsystems. These are harder to spot, and they demand different tooling.
Keylogging behavior crosses those boundaries. A kernel-level implant can hand-parsed events to a userspace forwarder, or a userspace keylogger can escalate by loading a helper module. The detection approach must reflect that reality. Watch for unexpected FDs to /dev/input, spikes in input event rates that do not match human activity, and kernel modules that show up without corresponding package installs. For kernel-level detection and driver hardening, see Part 3: Kernel & Driver Defenses.
How Keyboard Events Are Exposed on Linux
Linux exposes hardware key events through the evdev subsystem, which presents them as device files under /dev/input/eventX. GUI stacks such as X11 or Wayland sit above evdev and translate scan codes into key symbols for applications. Understanding that the stack is the baseline for any keylogging analysis.
Here is a basic overview of how a keyboard fits in a larger scheme:
/-----------+-----------\ /-----------+-----------\
| app 1 | app 2 | | app 3 | app 4 |
\-----------+-----------/ \-----------+-----------/
^ ^
| |
+-------+ |
| |
| key symbol keycode |
| + modifiers |
| |
| |
+---+-------------+ +-----------+-------------+
+ X server | | /dev/input/eventX |
+-----------------+ +-------------------------+
^ ^
| keycode / scancode |
+---------------+---------------+
|
|
+---------------+--------------+ interrupt
| kernel | <--------=-------+ +------------------------------+ | | +----------+ USB, PS/2 +-------------+ PCI, ... +-----+ | keyboard |------------------->| motherboard |----------->| CPU |
+----------+ key up/down +-------------+ +-----+
The short path is: keyboard hardware produces scan codes, the kernel converts those into input events and surfaces them on /dev/input/eventX, and the display layer or a daemon maps them to characters. Scan codes are the raw bytes the device sends on key down and key up. These become input_event records that include a timestamp, a type, a code, and a value that says press, release, or autorepeat.
This is where keylogger implementations attach. Some read /dev/input/eventX directly and parse input_event records. Others ask the display server for higher-level events. That difference determines what the logger sees and what the defender can observe. Knowing the exact path lets you pick the right indicators and avoid chasing noise. This section is the foundation for the examples and the CODE 6 logger that follows.
Identifying the Keyboard Device on Linux
Before a keylogger can read input, it has to find which event file actually belongs to a keyboard. On Linux, every input device under /dev/input/ exposes its own event file, and not all of them are keyboards — barcode scanners, USB hubs, and even touchpads appear there too. The discovery process checks for character devices, confirms that they report key events, and filters out hardware that only mimics keyboard behavior.
std::string get_kb_device()
{
std::string kb_device = "";
for (auto &p : std::filesystem::directory_iterator("/dev/input/"))
{
std::filesystem::file_status status = std::filesystem::status(p);
if (std::filesystem::is_character_file(status))
{
kb_device = p.path().string();
}
}
return kb_device;
}
Legitimate security tools use this same logic for diagnostics and auditing. Direct reads from input devices require authorization and should always be done in an isolated lab. Running this in production without proper controls can expose user data and violate privacy policies.
Check that the file is a keyboard and supports actual keys. However, this can be more involved, so observe the scheme here:
std::string filename = p.path().string();
int fd = open(filename.c_str(), O_RDONLY);
if(fd == -1)
{
std::cerr << "Error: " << strerror(errno) << std::endl; continue; } int32_t event_bitmap = 0; int32_t kbd_bitmap = KEY_A | KEY_B | KEY_C | KEY_Z; ioctl(fd, EVIOCGBIT(0, sizeof(event_bitmap)), &event_bitmap); if((EV_KEY & event_bitmap) == EV_KEY) { ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(event_bitmap)), &event_bitmap); if((kbd_bitmap & event_bitmap) == kbd_bitmap) { // The device supports A, B, C, Z keys, so it probably is a keyboard kb_device = filename; close(fd); break; } } close(fd);
The first snippet scans /dev/input/ using C++17 filesystem utilities to locate character devices. The second opens each file, queries its supported event types with EVIOCGBIT, and verifies that it reports the standard keyboard keys. This prevents false positives from devices that act like keyboards but aren’t one. In modern tooling, libudev is preferred for cleaner enumeration and safer device filtering, but the same keylogging principles apply regardless of the interface.
Reading Keyboard Events in Linux
Once the correct device is identified, a keylogger reads raw input data from it. Each keypress or release appears as an EV_KEY event — a small packet the kernel sends through the evdev interface. These packets describe what happened and when, letting any process with the right permissions capture and interpret keystrokes.
Each EV_KEY event contains:
- Timestamp – when the key action occurred.
- Type – the category of event, EV_KEY for key activity.
- Code – the specific key’s scan code from the kernel header input-event-codes.h.
- Value – the key’s state: 1 for press, 0 for release, 2 for autorepeat.
Here is how to define the "input_event." (Requires elevated privileges in a lab only.):
struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
unsigned int __usec;
unsigned int __pad;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;
__u16 code;
__s32 value;
}
The kernel exposes these structures consistently across devices, so a keylogger can map each event’s code to a readable key name. The example below shows a simple vector of mapped keycodes used for this translation.
Use the following map to run key name scan codes in a basic format. (Requires elevated privileges in a lab only.):
std::vector keycodes = {
"RESERVED",
"ESC",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
"MINUS",
"EQUAL",
"BACKSPACE",
"TAB",
"Q",
"W",
"E",
"R",
"T",
"Y",
"U",
"I",
"O",
"P",
"LEFTBRACE",
"RIGHTBRACE",
"ENTER",
"LEFTCTRL",
"A",
"S",
"D",
"F",
"G",
"H",
"J",
"K",
"L",
"SEMICOLON",
"APOSTROPHE",
"GRAVE",
"LEFTSHIFT",
"BACKSLASH",
"Z",
"X",
"C",
"V",
"B",
"N",
"M",
"COMMA",
"DOT",
"SLASH",
"RIGHTSHIFT",
"KPASTERISK",
"LEFTALT",
"SPACE",
"CAPSLOCK",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"NUMLOCK",
"SCROLLLOCK"
};
Frequently asked questions
Q: What is a keylogger on Linux?
A keylogger is software that records keyboard input events as they happen. On Linux, it usually reads from /dev/input/eventX through the evdev interface, though some versions hook into the X11 or Wayland layers. Understanding what a keylogger is in this context means knowing which process is reading those event files and how the kernel exposes them.
Q: Can keyloggers run without root access?
Generally no. Reading raw input devices requires elevated privileges because those files are protected under /dev/input/. A few user-level loggers can still capture keystrokes inside their own session by listening to the GUI stack, but they can’t access global keyboard input without root or similar permissions.
Q: How can I detect keylogging activity?
Start with open file descriptors. Run lsof /dev/input/event* or check proc/*/fd for handles pointing to input devices. Watch for unexpected reads from evdev or unsigned kernel modules that register input hooks. In GUI environments, review compositor logs for injected listeners or repeated event subscriptions. Reliable detection depends on baseline knowledge—know what normally touches those devices first.
Q: Does Wayland prevent keylogging?
Wayland limits global input capture by design, so traditional keylogging through the display stack is harder. Still, privileged processes can access hardware-level events if the system isn’t locked down. It’s not immunity, just another layer of isolation. Always assume anything with device-level access can still log keys.
Q: Where can I safely test keylogger code?
Only in an isolated lab VM or sandbox. Never on production systems or with live credentials. Snapshot the VM, run short captures with test input, and record evidence for reference. That keeps research clean and compliant while still showing what keylogging activity looks like in Linux.
Conclusion: Strengthening Defenses Against Keylogging in Linux
Understanding how a keylogger captures and processes input reveals where Linux defenses often fail. It’s about visibility — who’s reading keystrokes, through which layers, and with what permissions. Once that picture’s clear, defensive priorities shift from reaction to prevention.
Keylogging research should stay inside authorized testing environments. Those same techniques that expose vulnerabilities can easily cross into privacy violations if used outside of scope. Keeping the boundary tight protects both systems and professionals.
For deeper coverage of kernel and keyboard-level keylogging, review how Linux input subsystems handle device drivers and privilege transitions. Proactive monitoring, strict privilege controls, and routine auditing remain the most effective defenses against keylogging. They limit what attackers can see, how long they can stay hidden, and how deep they can go once inside.