前文手工处理了int3中断,实现了监控Linux系统键盘输入的效果:
https://blog.csdn.net/dog250/article/details/106481123
但是还是太复杂了。在这篇文章中,我深深地知道, int3替换了原来的单字节指令push %rbp,所以int3的处理中要想办法恢复原始指令执行。
于是,我在int3的处理中改变了RIP,将执行流拉入了一个stub函数,执行完push %rbp之后,再return回原始路径。
这无可厚非,但是不雅。
不就是一条push %rbp指令嘛,int3中断处理中直接模拟这条指令的效果不就好了嘛:
- RSP递减一个long型长度:RSP -= 8
- RBP的值塞入RSP指向的位置:*RSP = RBP
这还不简单:
#include <linux/module.h>
#include <linux/kdebug.h>
#include <linux/kallsyms.h>
#include <linux/tty.h>
#define DIE_INT3 2
unsigned long orig;
int int3_notify(struct notifier_block *self,
unsigned long val,void* data)
{
struct die_args *args = data;
struct pt_regs *regs = args->regs;
int ret = NOTIFY_DONE;
switch(val){
case DIE_INT3:
{
unsigned long *rsp;
struct tty_struct *tty;
char c;
// 三言两语完成push %rbp操作
rsp = (unsigned long *)regs->sp;
rsp --;
regs->sp = (unsigned long)rsp;
*rsp = regs->bp;
tty = (struct tty_struct *)regs->di;
c = regs->si;
printk("raw %c %s\n", c, tty->name);
ret = NOTIFY_STOP;
break;
}
default:
break;
}
return ret;
}
static struct notifier_block int3_nb = {
.notifier_call = int3_notify,
.priority =0x7fffffff,
};
unsigned char *p, old;
unsigned long cr0;
static int __init int3hook_init(void)
{
int ret;
ret = register_die_notifier(&int3_nb);
if (ret) {
printk("register_die_notifier failed %d\n", ret);
return ret;
}
orig = (unsigned long)kallsyms_lookup_name("n_tty_receive_char");
p = (unsigned char *)orig;
old = *p;
cr0 = read_cr0();
clear_bit(16, &cr0);
memset(p, 0xcc, 1);
set_bit(16, &cr0);
write_cr0(cr0);
return 0;
}
// exit函数也可以不提供
static void __exit int3hook_exit(void)
{
cr0 = read_cr0();
clear_bit(16, &cr0);
memset(p, old, 1);
set_bit(16, &cr0);
write_cr0(cr0);
unregister_die_notifier(&int3_nb);
}
module_init(int3hook_init)
module_exit(int3hook_exit)
MODULE_LICENSE("GPL");
你看,没有了stub函数,是不是清爽了很多呢?
看看效果:
[root@localhost probe]# insmod ./2int3hook.ko
[root@localhost probe]# dmesg
[ 528.960858] raw d pts0
[ 529.080784] raw m pts0
[ 529.217513] raw e pts0
[ 529.392473] raw s pts0
[ 529.593502] raw pts0
pts0.104994] raw
dmesg和回车被记录了下来。
浙江温州皮鞋湿,下雨进水不会胖。