在不导出 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 个符号。
您没有太多选择(这些只是“技巧”):
- 如果您已经在编译内核,只需 re-add
kernel/kallsyms.c
中的函数之后的导出指令。
- 如果您只是为了教育目的而玩弄游戏,您可以使用
unsigned long
module parameter 或者在编译之前简单地在模块中硬编码符号地址(从 /proc/kallsyms
中获取)然后将其转换为适当的类型。
- 您也可以 re-implement 自己在模块中查看
kernel/kallsyms.c
的功能,了解它是如何工作的。
- 从技术上讲,您还可以使用
filp_open()
从内核 space 打开并读取 /proc/kallsyms
,尽管说实话这有点疯狂。
- 如果您正在编写一个实际的严肃内核模块,请避免使用该函数(或 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");
我目前正在开发一个 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 个符号。
您没有太多选择(这些只是“技巧”):
- 如果您已经在编译内核,只需 re-add
kernel/kallsyms.c
中的函数之后的导出指令。 - 如果您只是为了教育目的而玩弄游戏,您可以使用
unsigned long
module parameter 或者在编译之前简单地在模块中硬编码符号地址(从/proc/kallsyms
中获取)然后将其转换为适当的类型。 - 您也可以 re-implement 自己在模块中查看
kernel/kallsyms.c
的功能,了解它是如何工作的。 - 从技术上讲,您还可以使用
filp_open()
从内核 space 打开并读取/proc/kallsyms
,尽管说实话这有点疯狂。 - 如果您正在编写一个实际的严肃内核模块,请避免使用该函数(或 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");