You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用kprobe挂钩connect系统调用时出现空指针解引用问题

解决Debian 5.10内核中kprobe挂钩connect()时的NULL指针解引用问题

问题分析

你遇到的NULL pointer dereference错误来自两个关键问题:

  1. 寄存器字段名错误
    x86_64架构的pt_regs结构体中,对应rsi寄存器的字段是regs->rsi,而非你代码里写的regs->si。错误的字段导致你拿到无效地址,最终解引用时触发NULL指针访问(错误地址0x2a是尝试访问addr->sa_family时,NULL指针偏移0x2a的结果)。

  2. 直接访问用户态内存
    系统调用的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:13:09