Skip to content

kallsyms_lookup_name is not exported anymore in kernels > 5.7 #3

@f0lg0

Description

@f0lg0

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);

#endif

And 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 :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions