系统调用挂钩示例参数不正确 [英] System call hooking example arguments are incorrect
问题描述
我写了一个从Linux内核模块进行系统调用挂接的示例.
I wrote an example of system call hooking from our Linux Kernel module.
更新了系统调用表中的开放系统调用,以使用我的入口点代替默认值.
Updated open system call in system call table to use my entry point instead of the default.
#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);
我编写了一个简单的用户程序进行验证.
I wrote a simple user program to verify.
#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失败,地址错误
File gets created in my folder, but strncpy_user fails with bad address
[ 927.415905] my_open
[ 927.415906] copied:-14
上面的代码有什么错误?
What is the mistake in the above code?
推荐答案
OP可能正在使用使用系统调用包装器"的内核/体系结构,其中系统调用表包含一个包装器函数,该包装器函数调用实际的系统调用函数(可能是内联函数调用).从内核版本4.17开始,x86_64体系结构就使用了syscall包装器.
OP is probably using a kernel/architecture that uses "syscall wrappers" where the system call table contains a wrapper function that calls the real syscall function (possibly as an inline function call). The x86_64 architecture has used syscall wrappers since kernel version 4.17.
对于内核4.17或更高版本上的x86_64,sys_call_table[__NR_open]
指向__x64_sys_open
(带有原型asmlinkage long __x64_sys_open(const struct pt_regs *regs)
),该调用c static
函数__se_sys_open
(带有原型static long __se_sys_open(const __user *filename, int flags, umode_t mode)
),该函数调用内联函数static inline long __do_sys_open(const __user *filename, int flags, umode_t mode)
.所有这些都将由"fs/open.c"中的SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
宏调用以及该宏调用之后的函数体定义.
For x86_64 on kernel 4.17 or later, sys_call_table[__NR_open]
points to __x64_sys_open
(with prototype asmlinkage long __x64_sys_open(const struct pt_regs *regs)
), which calls static
function __se_sys_open
(with prototype static long __se_sys_open(const __user *filename, int flags, umode_t mode)
), which calls inline function __do_sys_open
(with prototype static inline long __do_sys_open(const __user *filename, int flags, umode_t mode)
. Those will all be defined by the SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
macro call in "fs/open.c" and the function body that follows the macro call.
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".
SYSCALL_DEFINE3
is defined in "include/linux/syscalls.h" and uses the SYSCALL_DEFINEx
macro in the same file, which uses the __SYSCALL_DEFINEx
macro. Since x86_64 defines CONFIG_ARCH_HAS_SYSCALL_WRAPPER
, the __SYSCALL_DEFINEx
macro is defined by #include <asm/syscall_wrapper.h>
, which maps to "arch/x86/include/asm/syscall_wrapper.h".
有关此更改的背景,请参见
For background on this change, see
- LWN:使用基于struct pt_regs的系统调用来调用x86-64
- LKML: [PATCH 000/109]删除对系统调用的内核调用 https://lkml.org/lkml/2018/3/29/409
- LWN: use struct pt_regs based syscall calling for x86-64
- LKML: [PATCH 000/109] remove in-kernel calls to syscalls https://lkml.org/lkml/2018/3/29/409
看来动机是只传递一个指向pt_regs
的指针,而不是在调用链的寄存器中拥有一堆用户空间值. (也许是通过减少小工具的使用来增强对Spectre攻击的抵抗力?)
It seems the motivation is to only pass a pointer to pt_regs
, instead of having a bunch of user-space values in registers down the call chain. (Perhaps to increase resistance to Spectre attacks by making gadgets less useful?)
即使包装器没有运行,为什么open
仍然可以工作:
Why open
still worked, even though the wrapper didn't:
如果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
.
If OP is indeed using x86_64 kernel 4.17 or later, and replacing the sys_call_table[__NR_open]
entry with a pointer to a function that uses a different prototype and calls the original function (pointed to by old_open
) with the same parameters, that explains why the call to strncpy_from_user(user_msg, filename, sizeof(user_msg))
failed. Although declared as const char * __user filename
, the filename
pointer is actually pointing to the original struct pt_regs
in kernel space.
在随后的对old_open(filename, flags, mode)
的调用中,第一个参数filename
仍指向原始的struct pt_regs
,因此旧功能(期望单个类型为struct pt_regs *
的参数)仍可以按预期运行.
In the subsequent call to old_open(filename, flags, mode)
, the first parameter filename
is still pointing to the original struct pt_regs
so the old function (which expects a single parameter of type struct pt_regs *
) still works as expected.
即该函数将第一个指针arg传递给它,尽管调用它的类型不同.
i.e. the function passed on its first pointer arg unchanged, despite calling it a different type.
这篇关于系统调用挂钩示例参数不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!