Capturing keystrokes in GNU/Linux in C without X Window
Asked Answered
E

4

16

If I am working in an application and I press a key from the keyboard, how can I capture that key (or string), including the source application's name, in C, under GNU/LINUX, in userland, without X Window?

Excommunicate answered 28/9, 2009 at 2:14 Comment(5)
You should [1] state you problem a little more clearly (Can you access the source of the application? The shell? The OS? Either way where exactly are you hung up) and [2] convince us that we'd be doing something good for the world, rather than helping you to write another rootkit (you see, your handle isn't doing you any favors in this regard).Heterosexuality
So he wants to write the 52nd keylogger for linux. Big deal. Your morality is not going to stop him. At least he isn't over at superuser asking where to download one. ;)Cortisone
@dmckee a keylogger assignment is a common C/Assembler assignment at college. He might very well be a script kiddy looking to piss people off, but so what? He's asked a question, and he deserves an answer, regardless of his intent.Fourlegged
I just want to code something interesting :) Not a sk1dd13 and not in college. Thanks for your kind words.Excommunicate
Dominic: I actually want to write one to do timing and n-gram analysis of my keystrokes while programming, to catch common mistakes and suggest shortcuts.Burnette
V
21

Well, without X Window this problem is way more difficult.

For the keystroke part, you can use code similar to this one, but you have to pass the device as an argument that you are reading (keyboard, usually /dev/input/event0)

#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd;
    if(argc < 2) {
        printf("usage: %s <device>\n", argv[0]);
        return 1;
    }

    fd = open(argv[1], O_RDONLY);
    struct input_event ev;

    while (1)
    {
        read(fd, &ev, sizeof(struct input_event));

        if(ev.type == 1)
            printf("key %i state %i\n", ev.code, ev.value);

        }
    }

Credits do not go to me. This code is taken from the Ventriloctrl hack to get keystrokes - http://public.callutheran.edu/~abarker/ventriloctrl-0.4.tar.gz.

Vaticination answered 28/9, 2009 at 17:21 Comment(2)
note that such a program would have to be run as root, as the /dev/input/event* files are owned and restricted to root.Todhunter
how can it handle multiple key combination, ex: ctrl+c etc. ?Reynolds
C
3

You can read data from one of the files in /dev/input. Which one depends on your system. It might be /dev/input/event0 or /dev/input/by-path/platform-i8042-serio-0-event-kbd or something else. The format is specified in the kernel header input.h. It is

struct input_event {
        struct timeval time;
        __u16 type;
        __u16 code;
        __s32 value;
};

You can run

od -tx2 FILENAME

and type something to see what happens.

As for finding out which application received the key event, I'm not sure. You could try checking which one is reading from the main tty.

Cortisone answered 28/9, 2009 at 3:58 Comment(1)
how do I find out what file stands for the keyboard?Nibble
M
2

For a pretty good example, have a look at showkey's code.

In particular, here's the main loop. All it does is get the terminal, copy it, turn the copied one to raw mode, and until a 'quit' or 'interrupt' key sequence is given, it merely prints out what key was given to the terminal.

/*
 * showkey.c -- display cooked key sequences
 *
 * Invoke this (no arguments needed) to see keycap-to-keystrokes mappings.
 *
 * by Eric S. Raymond <[email protected]>, 1 Nov 88
 * - fix for little-endian machines (version 1.1), 21 Oct 1996.
 * - cleanup and modern packaging (version 1.2), 1 Aug 2002.
 * - changed to use termios (version 1.3), 26 Aug 2002.
 * See the RPM spec file changelog for more recent stuff.
 */
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdbool.h>
static int signalled;

// ...

main()
{
    struct termios  cooked, raw;
    unsigned char   c;
    unsigned int    i, timeouts;
    char intrchar[32], quitchar[32];

    for (i = SIGHUP; i <= SIGIO; i++)
    (void) signal(c, catcher);

    // Get the state of the tty 
    (void) tcgetattr(0, &cooked);
    // Make a copy we can mess with
    (void) memcpy(&raw, &cooked, sizeof(struct termios));
    // Turn off echoing, linebuffering, and special-character processing,
    // but not the SIGINT or SIGQUIT keys.
    raw.c_lflag &=~ (ICANON | ECHO);
    // Ship the raw control blts
    (void) tcsetattr(0, TCSANOW, &raw);

    (void) printf("Type any key to see the sequence it sends.\n");
    visualize(raw.c_cc[VINTR], intrchar);
    visualize(raw.c_cc[VQUIT], quitchar);
    (void) printf("Terminate with your shell interrupt %s or quit %s character.\n",
          intrchar, quitchar);
    signalled = 0;
    while (!signalled)
    {
    char cbuf[32];

    read(0, &c, 1);
    visualize(c, cbuf);
    (void)fputs(cbuf, stdout);
    (void) fflush(stdout);
    }

    (void) printf("\nBye...\n");
    // Restore the cooked state
    (void) tcsetattr(0, TCSANOW, &cooked);
}
Massa answered 23/1, 2014 at 18:28 Comment(0)
W
1

One possibility: find and take a look at the source for 'sudosh', the 'sudo shell' (or one of its replacements since that has not been modified in a while; Google is your friend).

It messes with pseudo-ttys and tracks all the input and output by also recording the information to file.

Whether that is precise enough for you is perhaps more debatable; it would log all keystrokes for all applications. I'm also not certain how it works with X11 - if it works with X11.

Wellchosen answered 28/9, 2009 at 3:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.