-
Notifications
You must be signed in to change notification settings - Fork 0
/
find_kallsyms_lookup_name.c
91 lines (71 loc) · 2.46 KB
/
find_kallsyms_lookup_name.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#define pr_fmt(fmt) "ambix.find_kallsyms_lookup_name: " fmt
/*
* kallsyms_lookup_name undefined and finding not exported functions in the
* linux kernel
*
* zizzu 2020
*
* On kernels 5.7+ kallsyms_lookup_name is not exported anymore, so it is not
* usable in kernel modules. The address of this function is visible via
* /proc/kallsyms but since the address is randomized on reboot, hardcoding a
* value is not possible. A kprobe replaces the first instruction of a kernel
* function and saves cpu registers into a struct pt_regs *regs and then a
* handler function is executed with that struct as parameter. The saved value
* of the instruction pointer in regs->ip, is the address of probed function
* + 1. A kprobe on kallsyms_lookup_name can read the address in the handler
* function. Internally register_kprobe calls kallsyms_lookup_name, which is
* visible for this code, so, planting a second kprobe, allow us to get the
* address of kallsyms_lookup_name without waiting and then we can call this
* address via a function pointer, to use kallsyms_lookup_name in our module.
*
* example for _x86_64.
*/
#include <linux/kprobes.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 (*the_kallsyms_lookup_name)(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("register_probe() for symbol %s failed, returned %d\n",
symbol_name, ret);
return ret;
}
pr_debug("Planted kprobe for symbol %s at %p\n", symbol_name, kp->addr);
return ret;
}
int find_kallsyms_lookup_name(void)
{
int ret;
pr_debug("Looking up 'kallsyms_lookup_name'\n");
ret = do_register_kprobe(&kp0, "kallsyms_lookup_name", handler_pre0);
if (ret < 0)
return ret;
ret = do_register_kprobe(&kp1, "kallsyms_lookup_name", handler_pre1);
if (ret < 0) {
unregister_kprobe(&kp0);
return ret;
}
unregister_kprobe(&kp0);
unregister_kprobe(&kp1);
pr_debug("kallsyms_lookup_name address = 0x%lx\n", kln_addr);
the_kallsyms_lookup_name =
(unsigned long (*)(const char *name))kln_addr;
return 0;
}