/*
 * syscall.c
 *
 * gcc -D__KERNEL__ -DMODULE -c syscall.o syscall.c
 */

#include <linux/module.h>
#include <linux/kernel.h>     /* printk */
#include <sys/syscall.h>      /* SYS_... */
#include <linux/sched.h>      /* task_struct */
#include <asm/current.h>      /* current */
#include <asm/errno.h>
#include <asm/smplock.h>
#include <linux/slab.h>

int errno;

/*
 * SYS_exit      1
 * SYS_fork      2
 * SYS_vfork     190
 * SYS_execve    11
 */

extern void*   sys_call_table[];

asmlinkage int (*oldfork)(struct pt_regs regs);
asmlinkage int (*oldvfork)(struct pt_regs regs);
asmlinkage int (*oldexecve)(struct pt_regs regs);
asmlinkage void (*oldexit)(int rc);

asmlinkage int newfork(struct pt_regs regs)
{
   int childpid;

   printk(KERN_DEBUG "newfork: fork called by PID %ld (%s)\n", current->pid, current->comm);
   childpid = oldfork(regs);
   if (childpid != -1)
      printk(KERN_DEBUG "newfork: process PID %ld (%s) created child %ld\n", current->pid, current->comm, childpid);
   else
      printk(KERN_DEBUG "newfork: process PID %ld (%s) failed to create child\n", current->pid, current->comm);
   return childpid;
}

asmlinkage int newvfork(struct pt_regs regs)
{
   int childpid;

   printk(KERN_DEBUG "newvfork: vfork called by PID %ld (%s)\n", current->pid, current->comm);
   childpid = oldvfork(regs);
   if (childpid != -1)
      printk(KERN_DEBUG "newvfork: process PID %ld (%s) created child %ld\n", current->pid, current->comm, childpid);
   else
      printk(KERN_DEBUG "newvfork: process PID %ld (%s) failed to create child\n", current->pid, current->comm);
   return childpid;
}

asmlinkage int newexecve(struct pt_regs regs)
{
   int rc;
   /* From modexecvehash */
   char *filename;

   printk(KERN_DEBUG "newexecve: execve called by PID %ld (%s) - %lp\n", current->pid, current->comm, oldexecve);
#if 0
   lock_kernel();
   filename = getname((char*)regs.ebx);
   rc = PTR_ERR(filename);
   if (!IS_ERR(filename))
   {
      rc = do_execve(filename, (char **)regs.ecx, (char **)regs.edx, &regs);
      if (rc == 0)
      {
         current->ptrace &= ~PT_DTRACE;
         printk(KERN_DEBUG "newexecve: process PID %ld execve'd (%s)\n", current->pid, current->comm);
      }
      else
         printk(KERN_DEBUG "newexecve: process PID %ld failed to execve (%s) - rc = %d\n", current->pid, filename, rc);
      unlock_kernel();
      putname(filename);
   }
#endif
#if 1
   rc = oldexecve(regs);
   if (rc == 0)
      printk(KERN_DEBUG "newexecve: process PID %ld execve'd (%s)\n", current->pid, current->comm);
   else
      printk(KERN_DEBUG "newexecve: process PID %ld failed to execve - rc = %d\n", current->pid, rc);
#endif

   /* restore the original execve so that if ours breaks down
    * we can still create processes and not make the system unusable
    */
   sys_call_table[SYS_execve] = oldexecve;
   printk(KERN_DEBUG "cleanup_module: old execve syscall restored\n");
   return rc;
}

asmlinkage void newexit(int rc)
{
   printk(KERN_DEBUG "newexit: exit called by PID %ld (%s) with rc %d\n", current->pid, current->comm, rc);
   oldexit(rc);
}

int init_module()
{
   printk(KERN_DEBUG "init_module: syscall mod loaded\n");
   //sys_call_table_addr = (func_ptr**)sys_call_table;

   oldfork = (int(*)())sys_call_table[SYS_fork];
   sys_call_table[SYS_fork] = newfork;
   printk(KERN_DEBUG "init_module: fork syscall replaced\n");

   oldvfork = (int(*)())sys_call_table[SYS_vfork];
   sys_call_table[SYS_vfork] = newvfork;
   printk(KERN_DEBUG "init_module: vfork syscall replaced\n");

   oldexecve = (int(*)())sys_call_table[SYS_execve];
   sys_call_table[SYS_execve] = newexecve;
   printk(KERN_DEBUG "init_module: execve syscall replaced\n");

   oldexit = (void(*)())sys_call_table[SYS_exit];
   sys_call_table[SYS_exit] = newexit;
   printk(KERN_DEBUG "init_module: exit syscall replaced\n");

   return 0;
}

void cleanup_module()
{
   sys_call_table[SYS_fork] = oldfork;
   printk(KERN_DEBUG "cleanup_module: old fork syscall restored\n");

   sys_call_table[SYS_vfork] = oldvfork;
   printk(KERN_DEBUG "cleanup_module: old vfork syscall restored\n");

   sys_call_table[SYS_execve] = oldexecve;
   printk(KERN_DEBUG "cleanup_module: old execve syscall restored\n");

   sys_call_table[SYS_exit] = oldexit;
   printk(KERN_DEBUG "cleanup_module: old exit syscall restored\n");

   printk(KERN_DEBUG "cleanup_module: syscall mod removed\n");
}

