md.*_*mal 5 c x86 system-calls linux-device-driver linux-kernel
我从我们的 Linux 内核模块中编写了一个系统调用挂钩示例。
更新了系统调用表中的 open 系统调用以使用我的入口点而不是默认值。
#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);
Run Code Online (Sandbox Code Playgroud)
我写了一个简单的用户程序来验证。
#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);
}
Run Code Online (Sandbox Code Playgroud)
文件在我的文件夹中创建,但 strncpy_user 因地址错误而失败
[ 927.415905] my_open
[ 927.415906] copied:-14
Run Code Online (Sandbox Code Playgroud)
上面的代码有什么错误?
OP 可能正在使用使用“系统调用包装器”的内核/架构,其中系统调用表包含一个调用真正系统调用函数(可能作为内联函数调用)的包装器函数。x86_64 架构从内核版本 4.17 开始使用系统调用包装器。
对于内核 4.17 或更高版本上的 x86_64,sys_call_table[__NR_open]
指向__x64_sys_open
(带原型asmlinkage long __x64_sys_open(const struct pt_regs *regs)
),它调用static
函数__se_sys_open
(带原型static long __se_sys_open(const __user *filename, int flags, umode_t mode)
),调用内联函数__do_sys_open
(带原型static inline long __do_sys_open(const __user *filename, int flags, umode_t mode)
。这些都将由SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
“fs/open.c”中的宏调用和宏调用之后的函数体。
SYSCALL_DEFINE3
在“include/linux/syscalls.h”中定义并在使用SYSCALL_DEFINEx
宏的同一文件中使用__SYSCALL_DEFINEx
宏。由于 x86_64 定义了CONFIG_ARCH_HAS_SYSCALL_WRAPPER
,__SYSCALL_DEFINEx
宏由 定义#include <asm/syscall_wrapper.h>
,它映射到“arch/x86/include/asm/syscall_wrapper.h”。
有关此更改的背景,请参阅
似乎动机是只传递一个指向 的指针pt_regs
,而不是在调用链中的寄存器中包含一堆用户空间值。(也许是通过让小工具变得不那么有用来增加对幽灵攻击的抵抗力?)
为什么open
仍然有效,即使包装器没有:
如果 OP 确实使用 x86_64 内核 4.17 或更高版本,并用sys_call_table[__NR_open]
指向使用不同原型并old_open
使用相同参数调用原始函数(由 指向)的函数的指针替换条目,这解释了调用strncpy_from_user(user_msg, filename, sizeof(user_msg))
失败的原因。尽管声明为const char * __user filename
,但filename
指针实际上指向struct pt_regs
内核空间中的原始指针。
在对 的后续调用中old_open(filename, flags, mode)
,第一个参数filename
仍指向原始参数,struct pt_regs
因此旧函数(需要一个类型为 的单个参数struct pt_regs *
)仍按预期工作。
即传递给它的第一个指针 arg 的函数没有改变,尽管调用了不同的类型。
归档时间: |
|
查看次数: |
1614 次 |
最近记录: |