System call interception in linux-kernel module (k

2019-03-13 12:49发布

I need to replace a standard system call (e.g. SYS_mkdir) with my own implementation.

As I read in some sources, including this question on Stackoverflow, the sys_call_table is not exported symbol since kernel version 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?

5条回答
老娘就宠你
2楼-- · 2019-03-13 13:02

The problem is caused due to the fact that sys_call_table is read only. In order to avoid the error, before manipulating the sys_call_table, you have to make it writable as well. The kernel provides a function to achieve it. And that function is given as set_mem_rw().

Just add the below code snippet before manipulating the 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.

set_mem_ro((long unsigned int)sys_call_table,1);    
查看更多
霸刀☆藐视天下
3楼-- · 2019-03-13 13:03

this works for me.

See Linux Kernel: System call hooking example and 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();
}
查看更多
做个烂人
4楼-- · 2019-03-13 13:07

Yes there is a solution without patching/rebuilding the kernel. Use the Kprobes infrastructure (or 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! 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).

查看更多
Explosion°爆炸
5楼-- · 2019-03-13 13:12

First, you need to determine the location of 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.

查看更多
贪生不怕死
6楼-- · 2019-03-13 13:23

Use LSM infrustructure.

Look at LSM hooks path_mkdir or inode_mkdir for details. One question that needs to be solved is how to register your own LSM module while the system don't allow it explicitly. See the answer for details here:

How can I implement my own hook function with LSM?

查看更多
登录 后发表回答