如何用我自己的函数覆盖系统调用 table 条目?
How can I override a system call table entry with my own function?
我想更新系统调用 table 以使用我的自定义打开功能。我写了下面的代码:
#include <linux/module.h>
#include <linux/kallsyms.h>
MODULE_LICENSE("GPL");
char *sym_name = "sys_call_table";
typedef asmlinkage long (*sys_call_ptr_t)(const struct pt_regs *);
static sys_call_ptr_t *sys_call_table;
typedef asmlinkage long (*custom_open) (const char __user *filename, int flags, umode_t mode);
custom_open old_open;
static asmlinkage long my_open(const char __user *filename, int flags, umode_t mode)
{
pr_info("%s\n",__func__);
return old_open(filename, flags, mode);
}
static int __init hello_init(void)
{
sys_call_table = (sys_call_ptr_t *)kallsyms_lookup_name(sym_name);
old_open = (custom_open)sys_call_table[__NR_open];
sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;
return 0;
}
static void __exit hello_exit(void)
{
sys_call_table[__NR_open] = (sys_call_ptr_t)old_open;
}
module_init(hello_init);
module_exit(hello_exit);
加载模块时,出现以下错误:
[69736.192438] BUG: unable to handle page fault for address: ffffffff98e001d0
[69736.192441] #PF: supervisor write access in kernel mode
[69736.192442] #PF: error_code(0x0003) - permissions violation
[69736.192443] PGD 10460e067 P4D 10460e067 PUD 10460f063 PMD 80000001040000e1
[69736.192461] Oops: 0003 [#1] SMP PTI
[69736.192463] CPU: 0 PID: 45249 Comm: insmod Tainted: G OE 5.2.8 #6
我可以更新系统调用table吗?我该如何解决此类错误?
你快到了。在 Intel x86 CPUs 中,Control Register CR0
有一个特殊位(称为写保护位),用于控制 CPU 是否可以在 运行ning 期间写入只读页面在特权级别 0(内核代码在特权级别 0 执行 运行)。
由于系统调用 table 位于只读页面内,并且默认情况下设置了 "Write Protect" 位,因此您无法对其进行写入。因此,如果您尝试这样做:
sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;
你会让一切崩溃。
为了正确劫持系统调用,您需要在覆盖 table 之前将 CR0
寄存器的 "Write Protect" 位设置为 0
来禁用写保护输入,并在完成后重新启用它。 read_cr0()
和 write_cr0()
这两个宏正是用于操作所述寄存器。
正确代码如下:
// Temporarily disable write protection
write_cr0(read_cr0() & (~0x10000));
// Overwrite the syscall table entry
sys_call_table[__NR_open] = /* whatever */;
// Re-enable write protection
write_cr0(read_cr0() | 0x10000);
上面使用了掩码 0x10000
,因为 "Write Protect" 位是寄存器的第 17 个最低有效位。
请注意,上述步骤需要在模块的 init
和 exit
函数中完成。
我想更新系统调用 table 以使用我的自定义打开功能。我写了下面的代码:
#include <linux/module.h>
#include <linux/kallsyms.h>
MODULE_LICENSE("GPL");
char *sym_name = "sys_call_table";
typedef asmlinkage long (*sys_call_ptr_t)(const struct pt_regs *);
static sys_call_ptr_t *sys_call_table;
typedef asmlinkage long (*custom_open) (const char __user *filename, int flags, umode_t mode);
custom_open old_open;
static asmlinkage long my_open(const char __user *filename, int flags, umode_t mode)
{
pr_info("%s\n",__func__);
return old_open(filename, flags, mode);
}
static int __init hello_init(void)
{
sys_call_table = (sys_call_ptr_t *)kallsyms_lookup_name(sym_name);
old_open = (custom_open)sys_call_table[__NR_open];
sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;
return 0;
}
static void __exit hello_exit(void)
{
sys_call_table[__NR_open] = (sys_call_ptr_t)old_open;
}
module_init(hello_init);
module_exit(hello_exit);
加载模块时,出现以下错误:
[69736.192438] BUG: unable to handle page fault for address: ffffffff98e001d0
[69736.192441] #PF: supervisor write access in kernel mode
[69736.192442] #PF: error_code(0x0003) - permissions violation
[69736.192443] PGD 10460e067 P4D 10460e067 PUD 10460f063 PMD 80000001040000e1
[69736.192461] Oops: 0003 [#1] SMP PTI
[69736.192463] CPU: 0 PID: 45249 Comm: insmod Tainted: G OE 5.2.8 #6
我可以更新系统调用table吗?我该如何解决此类错误?
你快到了。在 Intel x86 CPUs 中,Control Register CR0
有一个特殊位(称为写保护位),用于控制 CPU 是否可以在 运行ning 期间写入只读页面在特权级别 0(内核代码在特权级别 0 执行 运行)。
由于系统调用 table 位于只读页面内,并且默认情况下设置了 "Write Protect" 位,因此您无法对其进行写入。因此,如果您尝试这样做:
sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;
你会让一切崩溃。
为了正确劫持系统调用,您需要在覆盖 table 之前将 CR0
寄存器的 "Write Protect" 位设置为 0
来禁用写保护输入,并在完成后重新启用它。 read_cr0()
和 write_cr0()
这两个宏正是用于操作所述寄存器。
正确代码如下:
// Temporarily disable write protection
write_cr0(read_cr0() & (~0x10000));
// Overwrite the syscall table entry
sys_call_table[__NR_open] = /* whatever */;
// Re-enable write protection
write_cr0(read_cr0() | 0x10000);
上面使用了掩码 0x10000
,因为 "Write Protect" 位是寄存器的第 17 个最低有效位。
请注意,上述步骤需要在模块的 init
和 exit
函数中完成。