Explaining FKeylogger: The Rationale Behind Linux Keylogger Detection

Aug 8, 2025    #cybersec  

Intro

Keyloggers on Linux are pretty rare - I guess, but they still exist. Are they easy to detect? Well… maybe, maybe not. It really depends on the adversary and how much effort this guy put to hide it. Anyway, I built this project because at some point I decided, “Let’s build a tool to detect them” - and here we are - Github link

Disclaimer: Before we start please do understand that this project is just an exploration and built out of curiosity.

Now, how the hell do you even find this goddamn keylogger anyway? Well, you can’t just scan or search for the word “keylogger” in running processes (you may find it if the adversary is that retarded) - real attackers aren’t that stupid I guess..

So, if we want to find them, we first have to understand how input is handled on Linux systems. On Linux, we have different ways to read input, and attackers can hook into any of them.

/dev/input/event* (evdev interface)

/dev/hidraw* (raw HID interface)

evdev library / ioctl syscalls

virtual input (uinput)

higher layers (X11 / Wayland)

These are some of the ways that I could find to read inputs. The next step would be figuring out which process is using our/accessing input devices.

*Note: The below approaches described here are from the basis of how my tool detects suspicious activity.

Trying to find Keylogger’s

As a first step, I did was, to get the processes that have file descriptors open. This alone could already let us flag any suspicious processes that have access to input. For deeper analysis, we could trace syscalls like read or ioctl using eBPF (which I will cover later in this blog).

Pseudocode: Detect processes accessing input devices(FD)

  1. Get list of active input devices in /dev/input/ (those starting with event*).

  2. For each process:

    • Check if its /proc/<pid>/fd/ directory exists.
    • For each file descriptor of the process:
      • Resolve the real path of the descriptor.
      • If it matches an input device path:
        • Record that this PID is using the device.
        • Stop checking further descriptors for this process.
  3. Return a mapping of:

    • { pid - set of input devices it is accessing }

      Example: { 1234: {/dev/input/event2}, 5678: {/dev/input/event3, /dev/input/event4} }
      

2) Detecting Processes Accessing Input via X11

Not all keyloggers open those files directly - some use higher-level APIs like X11 to capture keystrokes, so we should also get the processes who are connecting to X11 server and might be reading input events through it.

The logic I used is pretty simple:

Pseudocode: Detect processes accessing input via X11

  1. For each process:

    • Collect open file descriptors.
    • Check if any are sockets connected to /tmp/.X11-unix/X0.
    • If yes, increase confidence score and note the number of sockets.
  2. Check environment variables:

    • If XAUTHORITY exists - increase confidence.
    • If DISPLAY exists - increase confidence.
  3. Check loaded libraries:

    • If process has loaded libX11.so or libXt.so → increase confidence.
  4. Return:

    • (confidence, access_rate)
       Example: Process 405 - confidence=3, access_rate=1
      

3) eBPF Approach

Another way to detect keyloggers is using eBPF . Instead of scanning /proc, we can attach eBPF programs directly in the kernel to monitor access to input devices - for example, syscalls like open/openat (to see who is trying to open /dev/input/event*) or kernel functions like vfs_read/vfs_ioctl (to see who is actually reading keystrokes).” This way, whenever a process tries to access /dev/input/event*, we can capture that information in real time - which makes it more reliable and accurate.

eBPF Hooks That I Used

Other Approaches

Below are some other ways we could find processes that access input devices:

Now that we have some base and are able to find processes that access input devices, we have the most important part - Differentiate between a legitimate process accessing input and the one which is suspicious.

For example, legit processes like Xorg, browsers, or text editors must open /dev/input/event* to read the input, contrast to that a process that is running from a /tmp/ directory with no GUI accessing your input is much more suspicious, so…

The Strategy: How We Separate Legitimate and Suspicious Processes

For this, I have gone through multiple levels of checks; some are pretty basic, and some are very specific.

These checks will be done on processes that have input access.

Why so many checks?

Because no single check is bulletproof. If we want to catch keyloggers, one need to think like someone actually trying to hide one - not just flag every text editor or Xorg instance that accesses /dev/input/.

Note: That doesn’t mean that we will be not having ANY false positives, I tried to tackle this using the Trust/Untrust binary option, see --help

Okay, now that we differentiated legit and not so legit processes accessing input, we just have to show the output to the user.

Displaying the findings

When running the tool without any extra flags, it will go through a set of basic checks that can help to find potential keylogger activity or persistence tricks. These include:

And other three main approaches:

Single PID Mode (--p)

Quick Scan Mode (--scan)

Monitor Mode (--monitor)

Some Other Things

Active User Input Prompt

During Single PID or Quick Scan monitoring, the tool will prompt the user to type a random sequence on the keyboard (it doesn’t matter whether you typed it wrong or right, we just need you to use your keyboard).

Trust / Untrust Binary Option (--modify_trust)

This option allows the user to manually mark a binary as trusted or untrusted.

Conclusion

Remember, no tool can guarantee 100% detection. That’s it, that’s the conclusion. Sankyou