简体   繁体   English

linux-kernel模块中的系统调用拦截(内核3.5)

[英]System call interception in linux-kernel module (kernel 3.5)

I need to replace a standard system call (eg SYS_mkdir) with my own implementation. 我需要用自己的实现替换标准系统调用(例如SYS_mkdir)。

As I read in some sources, including this question on Stackoverflow, the sys_call_table is not exported symbol since kernel version 2.6 . 正如我在一些来源中读到的,包括Stackoverflow上的这个问题sys_call_table自内核版本2.6以来不是导出符号。

I tried the following code: 我尝试了以下代码:

    #include <linux/module.h> 
    #include <linux/kernel.h> 
    #include <linux/unistd.h> 
    #include <asm/syscall.h> 

    int (*orig_mkdir)(const char *path); 

    ....

    int init_module(void) 
    { 
            orig_mkdir=sys_call_table[__NR_mkdir]; 
            sys_call_table[__NR_mkdir]=own_mkdir;  
            printk("sys_mkdir replaced\n"); 
            return(0); 
    } 

    ....

Unfortunately I receive compiler error: 不幸的是我收到编译错误:

 error: assignment of read-only location ‘sys_call_table[83]’

How can I replace the system call? 如何更换系统调用?

EDIT: Is there any solution without kernel patching? 编辑:有没有内核修补的解决方案?

this works for me. 这对我有用。

See Linux Kernel: System call hooking example and https://bbs.archlinux.org/viewtopic.php?id=139406 请参阅Linux内核:系统调用挂钩示例https://bbs.archlinux.org/viewtopic.php?id=139406

asmlinkage long (*ref_sys_open)(const char __user *filename, int flags, umode_t mode);
asmlinkage long new_sys_open(const char __user *filename, int flags, umode_t mode)
{
  return ref_sys_open(filename, flags, mode);
}

static unsigned long **aquire_sys_call_table(void)
{
  unsigned long int offset = PAGE_OFFSET;
  unsigned long **sct;

  while (offset < ULLONG_MAX) {
    sct = (unsigned long **)offset;

    if (sct[__NR_close] == (unsigned long *) sys_close) 
      return sct;

    offset += sizeof(void *);
  }
  print("Getting syscall table failed. :(");
  return NULL;
}


// Crazy copypasted asm stuff. Could use linux function as well...
// but this works and will work in the future they say.
static void disable_page_protection(void) 
{
  unsigned long value;
  asm volatile("mov %%cr0, %0" : "=r" (value));

  if(!(value & 0x00010000))
    return;

  asm volatile("mov %0, %%cr0" : : "r" (value & ~0x00010000));
}

static void enable_page_protection(void) 
{
  unsigned long value;
  asm volatile("mov %%cr0, %0" : "=r" (value));

  if((value & 0x00010000))
    return;

  asm volatile("mov %0, %%cr0" : : "r" (value | 0x00010000));
}


static int __init rootkit_start(void) 
{

  //Hide me

  print("loaded");

  if(!(sys_call_table = aquire_sys_call_table()))
    return -1;

  disable_page_protection(); 
  {
    ref_sys_open = (void *)sys_call_table[__NR_open];
    sys_call_table[__NR_open] = (unsigned long *)new_sys_open;
  }
  enable_page_protection();
  return 0;
}

static void __exit rootkit_end(void) 
{
  print("exiting");

  if(!sys_call_table) {
    return;
  }

  disable_page_protection();
  {
    sys_call_table[__NR_open] = (unsigned long *)ref_sys_open;
  }
  enable_page_protection();
}

Yes there is a solution without patching/rebuilding the kernel. 是的,没有修补/重建内核的解决方案。 Use the Kprobes infrastructure (or SystemTap). 使用Kprobes基础结构(或SystemTap)。

This will allow you to place "probes" (functions) at any point(s) within the kernel, using a kernel module. 这将允许您使用内核模块在内核中的任何位置放置“探测器”(函数)。

Doing similar stuff by modifying the sys_call_table is now prevented (it's read-only) & is considered a dirty hack! 现在可以通过修改sys_call_table来执行类似的操作(它是只读的)并被视为脏黑客! Kprobes/Jprobes/etc are a "clean" way to do so..Also, the documentation and samples provided in the kernel source tree is excellent (look under the kernel src tree- Documentation/kprobes.txt ). Kprobes / Jprobes / etc是一种“干净”的方式。此外,内核源代码树提供的文档和示例非常好(在内核src树下看 - Documentation / kprobes.txt )。

First, you need to determine the location of sys_call_table. 首先,您需要确定sys_call_table的位置。 See here . 看到这里

Before writing into the just located system table, you have to make its memory pages writable. 在写入刚刚找到的系统表之前,必须使其内存页面可写。 For that check here and if that doesn't work, try this . 对于检查这里 ,如果还是不行,请尝试

Use LSM infrustructure. 使用LSM infrustructure。

Look at LSM hooks path_mkdir or inode_mkdir for details. 有关详细信息,请查看LSM钩子path_mkdirinode_mkdir One question that needs to be solved is how to register your own LSM module while the system don't allow it explicitly. 需要解决的一个问题是如何在系统不明确允许的情况下注册您自己的LSM模块。 See the answer for details here: 有关详细信息,请参阅答案:

How can I implement my own hook function with LSM? 如何用LSM实现自己的钩子函数?

The problem is caused due to the fact that sys_call_table is read only. 该问题是由于sys_call_table是只读的。 In order to avoid the error, before manipulating the sys_call_table, you have to make it writable as well. 为了避免错误,在操作sys_call_table之前,你必须使它也可写。 The kernel provides a function to achieve it. 内核提供了实现它的功能。 And that function is given as set_mem_rw() . 该函数以set_mem_rw()的形式给出。

Just add the below code snippet before manipulating the sys_call_table 在操作sys_call_table之前,只需添加以下代码片段即可

set_mem_rw((long unsigned int)sys_call_table,1);

In the exit function of the kernel module,please do not forget to revert back the sys_call_table back to read only.It can be achieved as below. 在内核模块的退出函数中,请不要忘记将sys_call_table恢复为只读。它可以实现如下。

set_mem_ro((long unsigned int)sys_call_table,1);    

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM