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

无法从kprobe处理器读取syscall参数问题求助

问题:kprobe监控__x64_sys_kill时获取的PID与信号值异常

我在__x64_sys_kill上注册kprobe,目的是监控调用时的目标PID和信号编号。kprobe成功注册,dmesg能输出日志,但日志中的PID和信号值完全不符合实际,无法解读。

我的代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/sched.h>

static struct kprobe kp;


static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
    pid_t pid = (pid_t)regs->di;
    int sig = (int)regs->si; 

    printk(KERN_INFO "kprobe pre_handler: kill syscall - PID: %d, Signal: %d\n", pid, sig);

    return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
    pid_t pid = (pid_t)regs->di;
    int sig = (int)regs->si;  

    printk(KERN_INFO "kprobe post_handler: kill syscall - PID: %d, Signal: %d\n", pid, sig);
}


static int __init kprobe_init(void)
{
    kp.symbol_name = "__x64_sys_kill";
    kp.pre_handler = handler_pre;
    kp.post_handler = handler_post;

    int ret = register_kprobe(&kp);
    if (ret < 0) {
        printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
        return ret;
    }
    printk(KERN_INFO "kprobe registered for __x64_sys_kill\n");
    return 0;
}

static void __exit kprobe_exit(void)
{
    unregister_kprobe(&kp);
    printk(KERN_INFO "kprobe unregistered\n");
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

输出结果

kernel: [ 2605.007657] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007658] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007660] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007661] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007662] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007663] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007666] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007667] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007668] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007669] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007671] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007672] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007674] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007675] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007676] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007677] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62

已尝试的方法

  • 改用kretprobes,得到完全相同的异常结果
  • 尝试通过regs->orig_ax与__NR_kill匹配来确认是否为kill系统调用,但代码从未进入判断分支:
long syscall_nr = regs->orig_ax;

    // Check if it is the kill syscall (syscall number 62 on x86_64)
    if (syscall_nr == __NR_kill) {
        // NEVER GOT HERE
    }

我只是为了学习内核编程与Linux内核原理,并非项目开发,特此求助。


问题分析与修复方案

核心问题

  1. 参数获取方式错误:x86_64架构下,直接访问regs->di/regs->si获取系统调用参数不可靠——内核的struct pt_regs寄存器布局可能随版本或编译选项变化,且内核内部调用系统调用入口时,寄存器中的值并非用户传递的参数。
  2. 未过滤内核内部调用:__x64_sys_kill不仅会被用户空间的kill系统调用触发,还可能被内核其他路径直接调用,这类调用不会设置orig_ax,导致你检查系统调用号的逻辑失效,同时输出无效参数值。

修复后的代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/syscalls.h>

static struct kprobe kp;

static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
    // 仅处理用户空间发起的系统调用
    if (user_mode(regs)) {
        pid_t pid = (pid_t)syscall_get_arg(regs, 0);
        int sig = (int)syscall_get_arg(regs, 1);
        printk(KERN_INFO "kprobe pre_handler: user kill syscall - PID: %d, Signal: %d\n", pid, sig);
    }
    return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
    if (user_mode(regs)) {
        pid_t pid = (pid_t)syscall_get_arg(regs, 0);
        int sig = (int)syscall_get_arg(regs, 1);
        printk(KERN_INFO "kprobe post_handler: user kill syscall - PID: %d, Signal: %d\n", pid, sig);
    }
}

static int __init kprobe_init(void)
{
    kp.symbol_name = "__x64_sys_kill";
    kp.pre_handler = handler_pre;
    kp.post_handler = handler_post;

    int ret = register_kprobe(&kp);
    if (ret < 0) {
        printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
        return ret;
    }
    printk(KERN_INFO "kprobe registered for __x64_sys_kill\n");
    return 0;
}

static void __exit kprobe_exit(void)
{
    unregister_kprobe(&kp);
    printk(KERN_INFO "kprobe unregistered\n");
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

关键修改点

  • user_mode(regs):判断当前上下文是否来自用户空间,过滤内核内部对__x64_sys_kill的调用,避免无效输出。
  • syscall_get_arg():使用内核提供的标准接口获取系统调用参数,该接口会根据当前架构和内核版本正确解析寄存器中的参数值,兼容性和可靠性远高于直接访问寄存器字段。
  • 若需要验证系统调用号,可在user_mode(regs)判断后添加:
    if (syscall_get_nr(current, regs) == __NR_kill) {
        // 处理逻辑
    }
    

内容的提问来源于stack exchange,提问作者Jelal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:13:09