mirror of
https://github.com/AuxXxilium/redpill-lkm5.git
synced 2024-11-23 23:11:02 +07:00
feat: port intercept execve (#1)
Signed-off-by: Jim Ma <majinjing3@gmail.com> Signed-off-by: Jim Ma <majinjing3@gmail.com>
This commit is contained in:
parent
d97931a735
commit
41fc4fd882
@ -1,8 +1 @@
|
|||||||
bromolow 3.10.108
|
epyc7002 5.10.55
|
||||||
apollolake 4.4.180
|
|
||||||
broadwell 4.4.180
|
|
||||||
broadwellnk 4.4.180
|
|
||||||
denverton 4.4.180
|
|
||||||
geminilake 4.4.180
|
|
||||||
v1000 4.4.180
|
|
||||||
r1000 4.4.180
|
|
||||||
|
Binary file not shown.
@ -443,7 +443,7 @@ const struct hw_config supported_platforms[] = {
|
|||||||
},
|
},
|
||||||
.emulate_rtc = false,
|
.emulate_rtc = false,
|
||||||
.swap_serial = false,
|
.swap_serial = false,
|
||||||
.reinit_ttyS0 = true,
|
.reinit_ttyS0 = false,
|
||||||
.fix_disk_led_ctrl = true,
|
.fix_disk_led_ctrl = true,
|
||||||
.has_cpu_temp = true,
|
.has_cpu_temp = true,
|
||||||
.is_dt = true,
|
.is_dt = true,
|
||||||
|
200
internal/helper/ftrace_helper.h
Normal file
200
internal/helper/ftrace_helper.h
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Helper library for ftrace hooking kernel functions
|
||||||
|
* Author: Harvey Phillips (xcellerator@gmx.com)
|
||||||
|
* License: GPL
|
||||||
|
* Link: https://github.com/xcellerator/linux_kernel_hacking/blob/master/3_RootkitTechniques/3.9_hiding_logged_in_users/ftrace_helper.h
|
||||||
|
* */
|
||||||
|
|
||||||
|
#include <linux/ftrace.h>
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#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 <linux/kprobes.h>
|
||||||
|
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
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
#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 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]);
|
||||||
|
}
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/fs.h> //struct filename
|
#include <linux/fs.h> //struct filename
|
||||||
#include "override/override_syscall.h" //SYSCALL_SHIM_DEFINE3, override_symbol
|
#include "override/override_syscall.h" //SYSCALL_SHIM_DEFINE3, override_symbol
|
||||||
#include "call_protected.h" //do_execve(), getname(), putname()
|
#include "call_protected.h" //do_execve(), getname(), putname()
|
||||||
|
#include "helper/ftrace_helper.h"
|
||||||
|
|
||||||
#ifdef RPDBG_EXECVE
|
#ifdef RPDBG_EXECVE
|
||||||
#include "../debug/debug_execve.h"
|
#include "../debug/debug_execve.h"
|
||||||
@ -58,61 +59,68 @@ int add_blocked_execve_filename(const char *filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SYSCALL_SHIM_DEFINE3(execve,
|
static asmlinkage long (*orig_execve)(const struct pt_regs *);
|
||||||
const char __user *, filename,
|
|
||||||
const char __user *const __user *, argv,
|
/*
|
||||||
const char __user *const __user *, envp)
|
* The hook for sys_execve()
|
||||||
|
*/
|
||||||
|
asmlinkage int hook_execve(const struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct filename *path = _getname(filename);
|
char *filename = (char *)regs->di;
|
||||||
|
|
||||||
//this is essentially what do_execve() (or SYSCALL_DEFINE3 on older kernels) will do if the getname ptr is invalid
|
char *kbuf;
|
||||||
if (IS_ERR(path))
|
long error;
|
||||||
return PTR_ERR(path);
|
int i;
|
||||||
|
|
||||||
const char *pathname = path->name;
|
/*
|
||||||
#ifdef RPDBG_EXECVE
|
* We need a buffer to copy filename into
|
||||||
RPDBG_print_execve_call(pathname, argv);
|
*/
|
||||||
#endif
|
kbuf = kzalloc(NAME_MAX, GFP_KERNEL);
|
||||||
|
if(kbuf == NULL)
|
||||||
|
return orig_execve(regs);
|
||||||
|
|
||||||
for (int i = 0; i < MAX_INTERCEPTED_FILES; i++) {
|
/*
|
||||||
|
* Copy filename from userspace into our kernel buffer
|
||||||
|
*/
|
||||||
|
error = copy_from_user(kbuf, filename, NAME_MAX);
|
||||||
|
if(error){
|
||||||
|
kfree(kbuf);
|
||||||
|
return orig_execve(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_INTERCEPTED_FILES; i++) {
|
||||||
if (!intercepted_filenames[i])
|
if (!intercepted_filenames[i])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (unlikely(strcmp(pathname, intercepted_filenames[i]) == 0)) {
|
if (unlikely(strcmp(kbuf, intercepted_filenames[i]) == 0)) {
|
||||||
pr_loc_inf("Blocked %s from running", pathname);
|
pr_loc_inf("Blocked %s from running", kbuf);
|
||||||
|
kfree(kbuf);
|
||||||
//We cannot just return 0 here - execve() *does NOT* return on success, but replaces the current process ctx
|
//We cannot just return 0 here - execve() *does NOT* return on success, but replaces the current process ctx
|
||||||
do_exit(0);
|
do_exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Depending on the version of the kernel do_execve() accepts bare filename (old) or the full struct filename (newer)
|
/*
|
||||||
//Additionally in older kernels we need to take care of the path lifetime and put it back (it's automatic in newer)
|
* Clean up and return
|
||||||
//See: https://github.com/torvalds/linux/commit/c4ad8f98bef77c7356aa6a9ad9188a6acc6b849d
|
*/
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
|
kfree(kbuf);
|
||||||
int out = _do_execve(pathname, argv, envp);
|
return orig_execve(regs);
|
||||||
_putname(path);
|
|
||||||
return out;
|
|
||||||
#else
|
|
||||||
return _do_execve(path, argv, envp);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Declare the struct that ftrace needs to hook the syscall */
|
||||||
|
static struct ftrace_hook hooks[] = {
|
||||||
|
HOOK("__x64_sys_execve", hook_execve, &orig_execve),
|
||||||
|
};
|
||||||
|
|
||||||
static override_symbol_inst *sys_execve_ovs = NULL;
|
static override_symbol_inst *sys_execve_ovs = NULL;
|
||||||
int register_execve_interceptor()
|
int register_execve_interceptor()
|
||||||
{
|
{
|
||||||
pr_loc_dbg("Registering execve() interceptor");
|
pr_loc_dbg("Registering execve() interceptor");
|
||||||
|
|
||||||
if (sys_execve_ovs) {
|
int err;
|
||||||
pr_loc_bug("Called %s() while execve() interceptor is already registered", __FUNCTION__);
|
err = fh_install_hooks(hooks, ARRAY_SIZE(hooks));
|
||||||
return -EEXIST;
|
if(err)
|
||||||
}
|
return err;
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
|
|
||||||
override_symbol_or_exit_int(sys_execve_ovs, "SyS_execve", SyS_execve_shim);
|
|
||||||
#else
|
|
||||||
// TODO there is another __ia32_sys_execve, maybe need to override.
|
|
||||||
override_symbol_or_exit_int(sys_execve_ovs, "__x64_sys_execve", SyS_execve_shim);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pr_loc_inf("execve() interceptor registered");
|
pr_loc_inf("execve() interceptor registered");
|
||||||
return 0;
|
return 0;
|
||||||
@ -121,25 +129,9 @@ int register_execve_interceptor()
|
|||||||
int unregister_execve_interceptor()
|
int unregister_execve_interceptor()
|
||||||
{
|
{
|
||||||
pr_loc_dbg("Unregistering execve() interceptor");
|
pr_loc_dbg("Unregistering execve() interceptor");
|
||||||
|
/* Unhook and restore the syscall and print to the kernel buffer */
|
||||||
if (!sys_execve_ovs) {
|
fh_remove_hooks(hooks, ARRAY_SIZE(hooks));
|
||||||
pr_loc_bug("Called %s() while execve() interceptor is not registered (yet?)", __FUNCTION__);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
int out = restore_symbol(sys_execve_ovs);
|
|
||||||
if (out != 0)
|
|
||||||
return out;
|
|
||||||
sys_execve_ovs = NULL;
|
|
||||||
|
|
||||||
//Free all strings duplicated in add_blocked_execve_filename()
|
|
||||||
unsigned int idx = 0;
|
|
||||||
while (idx < MAX_INTERCEPTED_FILES-1 && intercepted_filenames[idx]) {
|
|
||||||
kfree(intercepted_filenames[idx]);
|
|
||||||
intercepted_filenames[idx] = NULL;
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_loc_inf("execve() interceptor unregistered");
|
pr_loc_inf("execve() interceptor unregistered");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
output/rp-epyc7002-5.10.55-dev.ko.gz
Normal file
BIN
output/rp-epyc7002-5.10.55-dev.ko.gz
Normal file
Binary file not shown.
BIN
output/rp-epyc7002-5.10.55-prod.ko.gz
Normal file
BIN
output/rp-epyc7002-5.10.55-prod.ko.gz
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user