在GNU/Linux中使用C语言捕获按键输入,无需X窗口

16

如果我正在使用一个应用程序并按下键盘上的某个键,我如何在GNU/Linux的用户空间中,使用C语言捕获该按键(或字符串),包括源应用程序的名称,而且不需要X Window?


1
你应该[1]更清晰地陈述你的问题(你能访问应用程序的源代码吗?可以访问shell或操作系统吗?无论如何,你卡在哪里)并且[2]说服我们,我们为世界做了一件好事,而不是帮你写另一个 rootkit(你看,你的 handle 在这方面没有任何帮助)。 - dmckee --- ex-moderator kitten
2
所以他想要为Linux编写第52个键盘记录器。没什么大不了的。你的道德观念并不能阻止他。至少他没有在超级用户上问从哪里下载一个。;) - Kim Stebel
4
@dmckee,键盘记录器作业是大学常见的C/汇编作业。他很可能只是一个想要惹恼人们的脚本小子,但又怎样呢?他提出了问题,无论他的意图如何,他都应该得到答案。 - Dominic Bou-Samra
3
谢谢您的赞扬。我只是想编写一些有趣的代码 :) 不是一个网络安全新手,也不在上大学。 - r00t3r
Dominic:实际上我想写一个程序来计时和分析我在编程时的n-gram键击,以捕捉常见错误并建议快捷方式。 - brice
4个回答

21

如果没有X Window,这个问题就更加困难了。

对于按键部分,你可以使用类似于以下代码的代码,但是你必须将你正在读取的设备(通常是键盘/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);

        }
    }

这些代码不是我写的。这些代码来自于Ventriloctrl hack用来获取按键的代码 - http://public.callutheran.edu/~abarker/ventriloctrl-0.4.tar.gz


9
请注意,这样的程序必须以 root 权限运行,因为 /dev/input/event* 文件归属于 root 并受其限制。 - Jeremy Powell
它如何处理多个键组合,例如:ctrl+c等? - anurag-jain

3

您可以从/dev/input目录下的一个文件中读取数据。具体是哪个文件取决于您的系统,可能是/dev/input/event0或/dev/input/by-path/platform-i8042-serio-0-event-kbd等。格式在内核头文件input.h中指定。

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

您可以运行

od -tx2 FILENAME

请在此输入内容并查看发生了什么。

至于如何找出哪个应用程序接收了按键事件,我不确定。您可以尝试检查哪个应用程序正在从主tty读取。


如何找出键盘对应的文件是什么? - Incerteza

2

一个相当不错的例子,请看showkey的代码。

特别是,这里是主循环。它所做的就是获取终端,复制它,将复制的终端转换为原始模式,并且在给定“quit”或“interrupt”键序列之前,仅仅打印出终端接收到的按键。

/*
 * showkey.c -- display cooked key sequences
 *
 * Invoke this (no arguments needed) to see keycap-to-keystrokes mappings.
 *
 * by Eric S. Raymond <esr@snark.thyrsus.com>, 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);
}

1
一种可能性:找到并查看 'sudosh' 的源代码,这是 'sudo shell'(或其替代品之一,因为它已经有一段时间没有被修改了;Google 是你的朋友)。
它会干扰伪终端并通过记录信息到文件来跟踪所有输入和输出。
这是否足够精确,或许更有争议;它将记录所有应用程序的按键。我也不确定它如何与 X11 一起工作 - 如果它能与 X11 一起工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接