-
Notifications
You must be signed in to change notification settings - Fork 136
kallsyms_lookup_name is not exported anymore in kernels > 5.7 #3
Description
Issue
In newer kernel versions (> 5.7.0) the function kallsyms_lookup_name, used in your ftrace_helper.h library, is not exported anymore by default. This means that compiling the code provided by you (also found here) on newer kernels will fail throwing: ERROR: modpost: "kallsyms_lookup_name" undefined!
More references:
https://lkml.org/lkml/2020/2/25/576
https://lwn.net/Articles/813350/
Solution
I've done some research online and found this workaround, which compiles and works on Manjaro with kernel 5.9.16; so I have decided to link this awesome solution to your library.
patch.c - here's the solution wrote by @zizzu0 and modified by me
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
// added by me
#include "inc/patch.h"
#define KPROBE_PRE_HANDLER(fname) static int __kprobes fname(struct kprobe *p, struct pt_regs *regs)
long unsigned int kln_addr = 0;
unsigned long (*kln_pointer)(const char* name) = NULL;
static struct kprobe kp0, kp1;
KPROBE_PRE_HANDLER(handler_pre0) {
kln_addr = (--regs->ip);
return 0;
}
KPROBE_PRE_HANDLER(handler_pre1) {
return 0;
}
static int do_register_kprobe(struct kprobe* kp, char* symbol_name, void* handler) {
int ret;
kp->symbol_name = symbol_name;
kp->pre_handler = handler;
ret = register_kprobe(kp);
if (ret < 0) {
pr_err("do_register_kprobe: failed to register for symbol %s, returning %d\n", symbol_name, ret);
return ret;
}
pr_info("Planted krpobe for symbol %s at %p\n", symbol_name, kp->addr);
return ret;
}
// this is the function that I have modified, as the name suggests it returns a pointer to the extracted kallsyms_lookup_name function
kln_p get_kln_p(void) {
int status;
status = do_register_kprobe(&kp0, "kallsyms_lookup_name", handler_pre0);
if (status < 0) return NULL;
status = do_register_kprobe(&kp1, "kallsyms_lookup_name", handler_pre1);
if (status < 0) {
// cleaning initial krpobe
unregister_kprobe(&kp0);
return NULL;
}
unregister_kprobe(&kp0);
unregister_kprobe(&kp1);
pr_info("kallsyms_lookup_name address = 0x%lx\n", kln_addr);
kln_pointer = (unsigned long (*)(const char* name)) kln_addr;
return kln_pointer;
}usable by including patch.h
#ifndef PATCH_H
#define PATCH_H
typedef unsigned long (*kln_p)(const char*);
kln_p get_kln_p(void);
#endifAnd finally, the patched function in the lib ftracer_hekper.h
#include "patch.h"
static int fh_resolve_hook_address(struct ftrace_hook* hook) {
// new method
kln_p kln = get_kln_p();
if (kln == NULL) return -1;
hook->address = kln(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;
}Conclusion
The described methods works also on older kernels, like in the 5.4.0-58 used in the Vagrant instance provided by your blog. It works by extracting the dynamic address from the kernel.
I haven't opened a pull request because I couldn't find the ftracer_helper lib here (I have only found it as a gist) and especially because I am a begginner in kernel land, my code could have been written and integrated better. It was a cool exercise for a beginner like me, hope you will find it useful!
Sorry for my English, I ain't a native speaker :)