Linux 内核:如何捕获按键并将其替换为另一个按键?
Linux Kernel: How to capture a key press and replace it with another key?
我正在尝试涉足低级编程。我的目标是让用户在终端中键入一个密钥,捕获该密钥并输出另一个密钥。因此,例如,如果用户输入 "a" 我会输入 "b",如果他输入 "b" 我输出 "c",等等
这样做的步骤是什么?我已经熟悉如何访问 Linux 内核源代码、编译和使用它。
谢谢。
考虑下一个简单的内核模块:
#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. It can be easily reworked for replacing of scan code。
免责声明
- 这个模块不是跨平台的(只能在 x86 架构上工作,因为它使用
inb()
函数)
- 我认为它只适用于 PS/2 键盘(不适用于 USB 键盘)
- 它在硬件 IRQ 处理程序中执行缓慢的 I/O 操作(我的意思是
pr_info()
),应该避免这种情况(理想情况下应该使用 threaded IRQs)。
但我认为它对教育目的有好处——它真的很小并且很好地展示了这个想法(没有像 input_dev
、input_register_device()
、[=19= 那样混淆 API ], input_event()
, input_report_key()
, 等等).
详情
真正的中断处理程序(在keyboard driver) requested as shared interrupt, which allows us also request that interrupt and thus handle it also in our ISR中(除了原始键盘驱动程序中的ISR)。中断请求在kbd2_init()
中完成。
该模块的工作原理如下:
- 捕获按键事件(为每个按键事件调用硬件中断处理程序
kbd2_isr()
)
- 读取按键的扫描码(通过
inb()
函数)
- 并通过
pr_info()
打印出来
现在,您想替换那个扫描码。我相信您可以为此使用 outb()
函数(在 x86 上)。所以我留给你了。
如果您想知道为什么我们请求编号为 1 的 IRQ,请参阅 drivers/input/serio/i8042-io.h:
#else
# define I8042_KBD_IRQ 1
还要确保检查此 IRQ 是否在 drivers/input/serio/i8042.c:
中共享
error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
"i8042", i8042_platform_device);
这是 i8042 键盘控制器的文档:AT keyboard controller。
有用的常量
为避免magic numbers,您可以使用下一个定义。
来自 drivers/input/serio/i8042-io.h:
/*
* Register numbers.
*/
#define I8042_COMMAND_REG 0x64
#define I8042_STATUS_REG 0x64
#define I8042_DATA_REG 0x60
/*
* Status register bits.
*/
#define I8042_STR_PARITY 0x80
#define I8042_STR_TIMEOUT 0x40
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01
我正在尝试涉足低级编程。我的目标是让用户在终端中键入一个密钥,捕获该密钥并输出另一个密钥。因此,例如,如果用户输入 "a" 我会输入 "b",如果他输入 "b" 我输出 "c",等等
这样做的步骤是什么?我已经熟悉如何访问 Linux 内核源代码、编译和使用它。
谢谢。
考虑下一个简单的内核模块:
#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. It can be easily reworked for replacing of scan code。
免责声明
- 这个模块不是跨平台的(只能在 x86 架构上工作,因为它使用
inb()
函数) - 我认为它只适用于 PS/2 键盘(不适用于 USB 键盘)
- 它在硬件 IRQ 处理程序中执行缓慢的 I/O 操作(我的意思是
pr_info()
),应该避免这种情况(理想情况下应该使用 threaded IRQs)。
但我认为它对教育目的有好处——它真的很小并且很好地展示了这个想法(没有像 input_dev
、input_register_device()
、[=19= 那样混淆 API ], input_event()
, input_report_key()
, 等等).
详情
真正的中断处理程序(在keyboard driver) requested as shared interrupt, which allows us also request that interrupt and thus handle it also in our ISR中(除了原始键盘驱动程序中的ISR)。中断请求在kbd2_init()
中完成。
该模块的工作原理如下:
- 捕获按键事件(为每个按键事件调用硬件中断处理程序
kbd2_isr()
) - 读取按键的扫描码(通过
inb()
函数) - 并通过
pr_info()
打印出来
现在,您想替换那个扫描码。我相信您可以为此使用 outb()
函数(在 x86 上)。所以我留给你了。
如果您想知道为什么我们请求编号为 1 的 IRQ,请参阅 drivers/input/serio/i8042-io.h:
#else
# define I8042_KBD_IRQ 1
还要确保检查此 IRQ 是否在 drivers/input/serio/i8042.c:
中共享error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
"i8042", i8042_platform_device);
这是 i8042 键盘控制器的文档:AT keyboard controller。
有用的常量
为避免magic numbers,您可以使用下一个定义。
来自 drivers/input/serio/i8042-io.h:
/*
* Register numbers.
*/
#define I8042_COMMAND_REG 0x64
#define I8042_STATUS_REG 0x64
#define I8042_DATA_REG 0x60
/*
* Status register bits.
*/
#define I8042_STR_PARITY 0x80
#define I8042_STR_TIMEOUT 0x40
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01