1

我正在尝试挂钩一些内核函数以用于学习目的,我在下面编写了简单的内核模块,但由于某些原因,register_kprobe总是返回 -2。我没有发现它所说的这个错误意味着什么,也不知道如何继续。起初我以为是因为它list_add是一个内联函数,所以我尝试将其替换为kvm_create_vm并得到相同的结果。然后我检查/proc/kallsyms并发现两者都没有出现在那里。所以我选择了kvm_alloc哪个是导出的,但我仍然得到错误-2。我也试过alloc_uid,但这工作得很好。

我的问题:可以连接什么样的功能kprobes

#undef __KERNEL__
#define __KERNEL__

#undef MODULE
#define MODULE

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

MODULE_LICENSE("GPL");

static int pre(struct kprobe *kp, struct pt_regs *regs){
    printk(KERN_INFO "It is working!\n");
    return 0;
}

static struct kprobe kp = {
    .symbol_name = "list_add",
    .pre_handler = pre,
    .post_handler = NULL,
    .fault_handler = NULL
};

int init_module(void){
    printk(KERN_INFO "Hi\n");
    printk(KERN_INFO "register_kprobe: %d\n" , register_kprobe(&kp));
    return 0;
}

void cleanup_module(void){
    unregister_kprobe(&kp);
    printk(KERN_INFO "Bye\n");
}

编辑

我划过的线是我感到困惑的主要原因。我错过了拼写kvm_alloc,它应该kvmalloc没有下划线。这个功能很好地被钩住了。

4

2 回答 2

1

要探测内联函数,您需要找到其内联实例所在的所有 PC 地址,并将这些地址放入 struct kprobes .addr 字段中。诸如 systemtap 之类的工具会在 DWARF 调试信息中搜索此类内联函数来计算 PC 地址。见 readelf -w vmlinux;DW_TAG_inlined_subroutine、DW_AT_low_pc 等。

于 2014-06-25T13:49:27.023 回答
0

负返回值通常可以解释为否定errno值。看看http://www.virtsync.com/c-error-codes-include-errno左右:

#define ENOENT       2  /* No such file or directory */

所以问题似乎是register_kprobe找不到东西,可能是list_add符号。让我们深入研究源代码以找出为什么会这样。

register_kprobe调用kprobe_addr来解析符号名称,而符号名称又调用kprobe_lookup_name,这是一个#definefor kallsyms_lookup_name。因此,您似乎需要获取要挂接到 kallsyms 的符号才能使其正常工作。

有关 kprobes 的文档,请查看内核源代码树中的Documentation/kprobes.txt。关于 kprobe 的内联函数,它说:

如果您在可内联函数中安装探针,Kprobes 不会尝试追踪该函数的所有内联实例并在那里安装探针。gcc 可能会在不被询问的情况下内联函数,因此如果您没有看到预期的探测命中,请记住这一点。

因此,它实际上不适用于内联函数。

既然我们已经解决了问题,让我们寻找解决方案。不过,您可能需要为此重新编译内核。

首先,确保内核配置选项CONFIG_KALLSYMS_ALL已打开——确保 kallsyms 知道更多符号。然后,尝试将实现移动list_add到一个单独的.c文件中并添加__attribute__ ((noinline))到它。新的内核构建会更慢,但我认为你的 kprobe 模块应该可以使用它。

于 2014-04-27T12:01:23.830 回答