Linux内核:如何捕获按键并用另一个键替换它?

时间:2015-11-20 21:41:41

标签: linux linux-kernel

我试图涉足低级编程。我的目标是让用户在终端中键入密钥,捕获并输出另一个密钥。因此,例如,如果用户输入“a”,我会输入“b”,如果他输入“b”,我输出“c”等。

这样做的步骤是什么?我已经熟悉如何访问Linux内核源代码,编译它并使用它。

感谢。

1 个答案:

答案 0 :(得分:11)

考虑下一个简单的内核模块:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>

#define KBD_IRQ             1       /* IRQ number for keyboard (i8042) */
#define KBD_DATA_REG        0x60    /* I/O port for keyboard data */
#define KBD_SCANCODE_MASK   0x7f
#define KBD_STATUS_MASK     0x80

static irqreturn_t kbd2_isr(int irq, void *dev_id)
{
    char scancode;

    scancode = inb(KBD_DATA_REG);
    /* NOTE: i/o ops take a lot of time thus must be avoided in HW ISRs */
    pr_info("Scan Code %x %s\n",
            scancode & KBD_SCANCODE_MASK,
            scancode & KBD_STATUS_MASK ? "Released" : "Pressed");

    return IRQ_HANDLED;
}

static int __init kbd2_init(void)
{
    return request_irq(KBD_IRQ, kbd2_isr, IRQF_SHARED, "kbd2", (void *)kbd2_isr);
}

static void __exit kbd2_exit(void)
{
    free_irq(KBD_IRQ, (void *)kbd2_isr);
}

module_init(kbd2_init);
module_exit(kbd2_exit);

MODULE_LICENSE("GPL");

这是最小和最原始的key-logger。它可以很容易地重新替换scan code

免责声明

  • 此模块不是跨平台的(仅适用于x86架构,因为它使用inb()功能)
  • 我相信它只适用于PS / 2键盘(不适用于USB键盘)
  • 它在硬件IRQ处理程序中执行慢速I / O操作(我的意思是pr_info()),应该避免(理想情况下应该使用threaded IRQs)。)

但我觉得这对于教育目的有好处 - 它非常小并且很好地展示了这个想法(没有像input_devinput_register_device()serio_write(),{{1}那样混淆API },input_event()等。)

详细

真实的中断处理程序(在keyboard driver中)被请求为共享中断,这允许我们也请求中断并因此在我们的ISR中处理它(除了ISR之外)在原始的键盘驱动程序)。中断请求在input_report_key()中完成。

此模块的工作原理如下:

  1. 捕获按键事件(为每个按键事件调用硬件中断处理程序kbd2_init()
  2. 读取按下的键的扫描码(通过kbd2_isr()功能)
  3. 并通过inb()
  4. 打印

    现在,您要替换该扫描代码。我相信你可以使用pr_info()函数(在x86上)。所以我留给你。

    如果您想知道我们为什么要求1号IRQ,请参阅drivers/input/serio/i8042-io.h

    outb()

    另外请务必检查drivers/input/serio/i8042.c中是否共享此IRQ:

    #else
    # define I8042_KBD_IRQ  1
    

    以下是i8042键盘控制器的文档:AT keyboard controller

    有用的常量

    要避免magic numbers,您可以使用下一个定义。

    来自drivers/input/serio/i8042-io.h

    error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
                        "i8042", i8042_platform_device);
    

    来自include/linux/i8042.h

    /*
     * Register numbers.
     */
    
    #define I8042_COMMAND_REG       0x64
    #define I8042_STATUS_REG        0x64
    #define I8042_DATA_REG          0x60