【问题标题】:Proper way of getting address of an unexported symbol in recent kernels that do not export kallsyms_lookup_name在不导出 kallsyms_lookup_name 的最新内核中获取未导出符号地址的正确方法
【发布时间】:2022-06-16 04:29:31
【问题描述】:

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

我遇到了获取sys_call_table 符号地址的不同方法,但还没有找到适用于较新内核(例如 5.11)的方法。在较旧的内核上,我们不会使用kallsyms_lookup_name 吗?看起来该符号已不再导出。

我可以看看/proc/kallsyms,但这似乎是个坏主意,而且不可推广。还有什么其他选择?

【问题讨论】:

  • “我可以看看/proc/kallsyms,但这似乎是个坏主意,不能推广。” - 仅供参考,替换系统调用也是一种不好的模式。它被一些初学者用作他们第一个内核模块的“任务”。但是 Linux 内核从不鼓励替换系统调用。

标签: c linux-kernel system-calls


【解决方案1】:

在旧内核上,我们不会使用kallsyms_lookup_name吗?

是的,这就是您在 5.7.0 之前使用的,当时符号 stopped being exported 因为没有人在核心内核代码之外使用它,它只是被模块滥用以查找和使用其他非导出符号。

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

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

【讨论】:

    【解决方案2】:

    我们还可以使用kprobes 找到kallsyms_lookup_name 函数的地址。

    Quotes taken from here (kprobes)

    Kprobes 使您能够动态地中断任何内核例程并无中断地收集调试和性能信息。您几乎可以在任何内核代码地址处捕获

    要注册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;
        ...
    }
    

    在 struct kprobe 中引入“symbol_name”字段后,探测点地址解析现在将由内核负责。

    一旦设置了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 地址:

    kallsyms_lookup_name("sys_call_table");
    

    Source for kprobe struct

    Source for kprobe technique

    【讨论】:

      猜你喜欢
      • 2015-03-16
      • 2012-04-21
      • 2012-02-26
      • 2016-01-03
      • 2021-06-13
      • 2016-01-25
      • 2012-04-14
      • 2014-07-29
      • 1970-01-01
      相关资源
      最近更新 更多