'Proper way of getting address of sys_call_table in recent kernels that do not export kallsyms_lookup_name
I'm currently working on a LKM to intercept some syscalls for printing statistics about them system-wide.
I've come across different ways of getting the sys_call_table address, but have yet to find a way that works on newer kernels (5.11). Before wouldn't we have used kallsyms_lookup_name? But it looks like that symbol is no longer exported.
I could just look at /proc/kallsyms but this seems like a bad idea and not generalizable.
Thank you for any guidance or suggested alternatives!
Solution 1:[1]
Before wouldn't we have used
kallsyms_lookup_name? But it looks like that symbol is no longer exported.
Yes, that's what you would have used before 5.7.0, when the symbol stopped being exported because nobody was using it outside of core kernel code, and it was just there to be abused by modules to find and use other non-exported symbols.
You don't have many options (these are just "hacks"):
- If you are compiling the kernel already, just re-add the export directives after the functions in
kernel/kallsyms.c. - If you are just playing around for educational purposes, you can use an
unsigned longmodule parameter or simply hardcode the symbol address in your module before compiling (taking it from/proc/kallsyms) and then cast it to the appropriate type. - You could also re-implement the functionality by yourself in your module looking at
kernel/kallsyms.cto see how it works. - You technically could also open and read
/proc/kallsymsfrom kernel space usingfilp_open(), though that'd be kind of insane to be honest. - If you are writing an actual serious kernel module, avoid using the function (or re-implementing it). You shouldn't use it anyway.
Solution 2:[2]
We could also find the address of the kallsyms_lookup_name function using kprobes.
Quotes taken from here (kprobes)
Kprobes enables you to dynamically break into any kernel routine and collect debugging and performance information non-disruptively. You can trap at almost any kernel code address
To register a kprobe, first a kprobe struct needs to be initialized with the name of the symbol that needs to be trapped. We can do that by setting the symbol_name in the kprobe struct.
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name"
};
The kprobe struct has the following elements within it (shortened for brevity):
struct kprobe {
...
/* location of the probe point */
kprobe_opcode_t *addr;
/* Allow user to indicate symbol name of the probe point */
const char *symbol_name;
...
}
With the introduction of the “symbol_name” field to struct kprobe, the probepoint address resolution will now be taken care of by the kernel.
Once the symbol_name is set, the address of the probe point is determined by the kernel.
So, now all that's left to do is to register the probe, extract the probepoint address and then unregister it:
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);
We now have the kallsyms_lookup_name address. Using that we can find the sys_call_table address the old-fashioned way:
kallsyms_lookup_name("sys_call_table");
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | InertFluid |
