Linux-kernel

Any donation is very welcome
Fork me on GitHub

I. Linux kernel articles

4. Overwrite syscall

Sounds terrifying, right ? Actually, I found it very cool so that's why I wanted to share it ! Let's begin. Create all the folders and stuff and put in the file:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>

unsigned long *syscall_table = NULL;
// corresponds to the syscall that will be replaced
asmlinkage long (*original)(unsigned int size, unsigned long *ptr);
unsigned long original_cr0;

#define SYSCALL_TO_REPLACE _NR_epoll_ctl_old

asmlinkage int new_syscall(unsigned int size, unsigned long *ptr)
{
  printk("I'm the syscall which overwrittes the previous !\n");
  return 0;
}

static unsigned long **find_syscall_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 *);
  }

  return NULL;
}

static int _init overwrite_init(void)
{
  syscall_table = (void **) find_syscall_table();
  if (syscall_table == NULL) {
    printk(KERN_ERR"net_malloc: Syscall table is not found\n");
    return -1;
  }
  // wrapper for asm part
  original_cr0 = read_cr0();
  write_cr0(original_cr0 & ~0x00010000);
  original = (void *)syscall_table[SYSCALL_TO_REPLACE];

  // we now overwrite the syscall
  syscall_table[SYSCALL_TO_REPLACE] = (unsigned long *)new_syscall;
  write_cr0(original_cr0);
  printk("net_malloc: Patched! syscall number : %d\n", SYSCALL_TO_REPLACE);
  return 0;
}

static void _exit overwrite_exit(void)
{
  // reset overwritten syscall
  if (syscall_table != NULL) {
    original_cr0 = read_cr0();
    write_cr0(original_cr0 & ~0x00010000);

    syscall_table[SYSCALL_TO_REPLACE] = (unsigned long *)original;

    write_cr0(original_cr0);
  }
}

module_init(overwrite_init);
module_exit(oveerwrite_exit);

If you want to test it on a very used syscall, try to replace read or write and replace them with this:

asmlinkage int new_syscall(unsigned int fd, const char _user *buf, size_t count)
{
    printk("who is calling the big bad write ?\n"); 
    return (*original)(fd, buf, count);
}

You'll see, the result will be quite fun (or not, it's just a point of view...).