使用kprobe挂钩connect系统调用时出现空指针解引用问题
解决Debian 5.10内核中kprobe挂钩connect()时的NULL指针解引用问题
问题分析
你遇到的NULL pointer dereference错误来自两个关键问题:
寄存器字段名错误
x86_64架构的pt_regs结构体中,对应rsi寄存器的字段是regs->rsi,而非你代码里写的regs->si。错误的字段导致你拿到无效地址,最终解引用时触发NULL指针访问(错误地址0x2a是尝试访问addr->sa_family时,NULL指针偏移0x2a的结果)。直接访问用户态内存
系统调用的sockaddr参数是用户空间指针,内核态不能直接解引用用户态地址——内核有独立的地址空间,直接访问会触发内存访问错误,必须使用内核提供的安全内存拷贝函数。
修正后的代码
#include <linux/kernel.h> #include <linux/module.h> #include <linux/kprobes.h> #include <linux/socket.h> #include <linux/uaccess.h> static struct kprobe kp = { .symbol_name = "__x64_sys_connect", }; static int handler_pre(struct kprobe *p, struct pt_regs *regs) { // x86_64系统调用参数规则:rdi=文件描述符, rsi=sockaddr指针, rdx=地址长度 struct sockaddr sa; // 仅读取sa_family字段即可,无需拷贝整个结构体 if (copy_from_user(&sa.sa_family, (const void __user *)regs->rsi, sizeof(sa.sa_family))) { // 拷贝失败,直接返回避免后续错误 return 0; } if (sa.sa_family == AF_INET) { printk(KERN_INFO "Hurray! IPv4 connect detected\n"); } return 0; } static int __init my_hook_init(void) { kp.pre_handler = handler_pre; int ret = register_kprobe(&kp); if (ret < 0) { printk(KERN_INFO "Kprobe failed %d\n", ret); return ret; } printk(KERN_INFO "Kprobe at %p\n", kp.addr); return 0; } static void __exit my_hook_exit(void) { unregister_kprobe(&kp); printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr); } module_init(my_hook_init); module_exit(my_hook_exit); MODULE_LICENSE("GPL");
关键修正点
- 替换
regs->si为regs->rsi,正确获取系统调用的第二个参数地址。 - 使用
copy_from_user安全读取用户态的sa_family字段,避免直接访问用户空间内存。 - 添加
linux/uaccess.h头文件,提供copy_from_user的函数定义。 - 增加
MODULE_LICENSE("GPL"),确保模块符合内核许可要求,避免加载失败。
内容的提问来源于stack exchange,提问作者GeoMldr
相关产品推荐
相关产品推荐

