通过内核模块替换write系统调用按键输入及终端崩溃问题问询
崩溃原因
你的代码核心错误是混淆了write系统调用的参数含义:
x86_64架构下系统调用的参数通过寄存器传递,write的原型为ssize_t write(int fd, const void *buf, size_t count),对应寄存器规则是:
- rdi:第一个参数,文件描述符fd
- rsi:第二个参数,用户态缓冲区的内存指针
- rdx:第三个参数,写入长度
你直接将regs->si(也就是rsi寄存器的值)赋值为0x62(字符'b'的ASCII值),等于强制让write系统调用从地址为0x62的非法低地址读取数据,直接触发内存访问错误,导致关联的终端进程崩溃。同时你直接解引用用户态指针buf[0]也存在风险,如果用户态传入非法指针,会直接触发内核oops。
正确实现思路
要在hook的write系统调用中修改写入内容,需遵循内核态访问用户态内存的安全规则,不要直接修改指针本身,而是修改指针指向的缓冲区内容,或者使用内核临时缓冲区中转:
- 先安全校验并读取用户态缓冲区的内容,禁止直接解引用用户态指针
- 确认需要替换内容后,安全写入修改后的值到用户态缓冲区,或者使用内核临时缓冲区中转后调用原write逻辑
修正后示例代码
unsigned long hacked_write_test(struct pt_regs *regs) { int r; unsigned int fd = regs->di; char __user *buf = (char __user *)regs->si; char tmp; // 仅处理标准输出等目标fd,可优化为判断fd对应是否为tty设备,兼容性更好 if (fd == 1 || fd == 2 || fd == 3) { // 安全读取用户态buf的第一个字符,访问失败直接跳过修改 if (get_user(tmp, buf) == 0) { if (tmp == 0x61) { printk("in write: A received"); // 安全将修改后的字符写回用户态缓冲区 put_user(0x62, buf); } } } r = original_write(regs); return r; }
注意事项
- 上述实现会直接修改用户态传入的缓冲区内容,如果业务不允许修改原用户态内存,可以在内核栈申请临时缓冲区,拷贝全部写入内容修改后,调整rsi寄存器指向临时缓冲区,同时调用
set_fs(KERNEL_DS)(仅旧内核可用,5.10+内核已废弃该接口)让write可以读取内核态地址,调用完成后恢复原fs值即可 - 直接hook系统调用的兼容性较差,更推荐hook tty层的
receive_buf或write回调实现按键替换,稳定性更高
内容的提问来源于stack exchange,提问作者tarun14110
相关产品推荐
相关产品推荐

