系统调用挂钩示例参数不正确 [英] System call hooking example arguments are incorrect

查看:148
本文介绍了系统调用挂钩示例参数不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个从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: 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆