【发布时间】:2022-01-07 02:44:12
【问题描述】:
我从我们的 Linux 内核模块中编写了一个系统调用挂钩示例。
更新了系统调用表中的开放系统调用以使用我的入口点而不是默认入口点。
#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)
{
char user_msg[256];
pr_info("%s\n",__func__);
memset(user_msg, 0, sizeof(user_msg));
long copied = strncpy_from_user(user_msg, filename, sizeof(user_msg));
pr_info("copied:%ld\n", copied);
pr_info("%s\n",user_msg);
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];
// Temporarily disable write protection
write_cr0(read_cr0() & (~0x10000));
sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;
// Re-enable write protection
write_cr0(read_cr0() | 0x10000);
return 0;
}
static void __exit hello_exit(void)
{
// Temporarily disable write protection
write_cr0(read_cr0() & (~0x10000));
sys_call_table[__NR_open] = (sys_call_ptr_t)old_open;
// Re-enable write protection
write_cr0(read_cr0() | 0x10000);
}
module_init(hello_init);
module_exit(hello_exit);
我写了一个简单的用户程序来验证。
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd = syscall(__NR_open, "hello.txt", O_RDWR|O_CREAT, 0777);
exit(EXIT_SUCCESS);
}
在我的文件夹中创建了文件,但 strncpy_user 因地址错误而失败
[ 927.415905] my_open
[ 927.415906] copied:-14
上面的代码有什么错误?
【问题讨论】:
-
风格:你为什么想要一个非
const/非静态全局char *sym_name?此外,char user_msg[256] = {0}会比 memset 更容易。同样对于一次性实验,我可能只是假设系统调用表条目是sys_open,而不是保存旧指针。 (由于某种原因,非静态变量。) -
strncpy_from_user返回的-14是-EFAULT。你确定你的syscall包装器实际上是在传递一个指向有效字符串的指针吗?检查strace。 -
来自 strace:open("hello.txt", O_RDWR|O_CREAT, 0777) = 3 。文件创建成功
-
哦,还有一个潜在的缓冲区超读错误:如果源字符串太大而无法容纳,
strncpy不会 0 终止目标。提前清零缓冲区是没有用的;相反,您需要检查返回值是否为非错误且 user_msg[sizeof(user_msg)-1] = 0 始终将最后一个字节归零,复制之前的第一个字节。由于它很小并且在堆栈上,所以这对性能来说甚至还不错。 -
你用不同的签名的函数替换原来的函数一个签名,然后问为什么你得到了错误的参数?真的吗?显式转换为
(sys_call_ptr_t)用于克服编译器的警告/错误是“出现问题”的直接信号。
标签: c linux-kernel x86 linux-device-driver system-calls