在不导出 kallsyms_lookup_name 的最新内核中获取 sys_call_table 地址的正确方法

Proper way of getting address of sys_call_table in recent kernels that do not export kallsyms_lookup_name

我目前正在开发一个 LKM 来拦截一些系统调用,以便在系统范围内打印有关它们的统计信息。

我遇到过获取 sys_call_table 地址的不同方法,但尚未找到适用于较新内核 (5.11) 的方法。以前我们不会用kallsyms_lookup_name吗?但看起来该符号不再导出。

我可以看看 /proc/kallsyms 但这似乎是个坏主意,不能一概而论。

感谢您提供任何指导或建议的替代方案!

Before wouldn't we have used kallsyms_lookup_name? But it looks like that symbol is no longer exported.

是的,这就是你在 5.7.0 之前使用的,当符号 stopped being exported 因为没有人在核心内核代码之外使用它,它只是被模块滥用来查找和使用其他 non-exported 个符号。

您没有太多选择(这些只是“技巧”):

  1. 如果您已经在编译内核,只需 re-add kernel/kallsyms.c 中的函数之后的导出指令。
  2. 如果您只是为了教育目的而玩弄游戏,您可以使用 unsigned long module parameter 或者在编译之前简单地在模块中硬编码符号地址(从 /proc/kallsyms 中获取)然后将其转换为适当的类型。
  3. 您也可以 re-implement 自己在模块中查看 kernel/kallsyms.c 的功能,了解它是如何工作的。
  4. 从技术上讲,您还可以使用 filp_open() 从内核 space 打开并读取 /proc/kallsyms,尽管说实话这有点疯狂。
  5. 如果您正在编写一个实际的严肃内核模块,请避免使用该函数(或 re-implementing 它)。无论如何你都不应该使用它。

我们还可以使用 kprobes.

找到 kallsyms_lookup_name 函数的地址

Quotes taken from here (kprobes)

Kprobes enables you to dynamically break into any kernel routine and collect debugging and performance information non-disruptively. You can trap at almost any kernel code address

要注册一个 kprobe,首先需要用需要捕获的符号的名称初始化一个 kprobe 结构。我们可以通过在 kprobe 结构中设置 symbol_name 来做到这一点。

#include <linux/kprobes.h>
static struct kprobe kp = {
    .symbol_name = "kallsyms_lookup_name"
};

kprobe 结构中包含以下元素(为简洁起见缩短):

struct kprobe {
    ...
    /* location of the probe point */
    kprobe_opcode_t *addr;

    /* Allow user to indicate symbol name of the probe point */
    const char *symbol_name;
    ...
}

With the introduction of the “symbol_name” field to struct kprobe, the probepoint address resolution will now be taken care of by the kernel.

设置symbol_name后,探测点的地址由内核决定。 所以,现在剩下要做的就是注册探针,提取探针点地址,然后注销它:

typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
kallsyms_lookup_name_t kallsyms_lookup_name;
register_kprobe(&kp);
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
unregister_kprobe(&kp);

我们现在有了 kallsyms_lookup_name 地址。使用它我们可以找到 sys_call_table 地址 old-fashioned 方式:

kallsyms_lookup_name("sys_call_table");

Source for kprobe struct

Source for kprobe technique