├── .gitignore ├── kallsyms.h ├── ksyms.h ├── kallsyms.c ├── kallsyms_kp.c ├── Makefile ├── kallsyms_lp.c ├── main.c └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.out 3 | *.swp 4 | -------------------------------------------------------------------------------- /kallsyms.h: -------------------------------------------------------------------------------- 1 | 2 | unsigned long kallsyms_lookup_name(const char *name); 3 | int kallsyms_lookup_fault(const char *name); 4 | int init_kallsyms(void); 5 | -------------------------------------------------------------------------------- /ksyms.h: -------------------------------------------------------------------------------- 1 | #ifndef KSYMS_H 2 | #define KSYMS_H 3 | 4 | #define KSYMDEC(x) extern typeof(&x) _##x; 5 | #define KSYMDEF(x) void *_##x = NULL; 6 | #define KSYM(x) ((typeof(&x))_##x) 7 | #define KSYMINIT(x) (_##x = (void *)kallsyms_lookup_name(#x)) 8 | #define KSYMINIT_FAULT(x) if (!KSYMINIT(x)) return kallsyms_lookup_fault(#x) 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /kallsyms.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if IS_ENABLED(CONFIG_KPROBES) 8 | #include "kallsyms_kp.c" 9 | #elif IS_ENABLED(CONFIG_LIVEPATCH) 10 | #include "kallsyms_lp.c" 11 | #elif LINUX_VERSION_CODE < KERNEL_VERSION(5,7,0) 12 | int init_kallsyms(void) { 13 | return 0; 14 | } 15 | #else 16 | #error "No suitable kallsyms acquisition method!" 17 | #endif 18 | 19 | int kallsyms_lookup_fault(const char *sym) { 20 | printk("kallsyms_lookup_name failed for: %s\n", sym); 21 | return -EBADF; 22 | } 23 | -------------------------------------------------------------------------------- /kallsyms_kp.c: -------------------------------------------------------------------------------- 1 | #include "kallsyms.h" 2 | #include 3 | 4 | typedef unsigned long(*kallsymsFn)(const char *); 5 | 6 | static kallsymsFn kallsyms = NULL; 7 | 8 | unsigned long kallsyms_lookup_name(const char *name) 9 | { 10 | return kallsyms(name); 11 | } 12 | 13 | int init_kallsyms(void) 14 | { 15 | struct kprobe kp = {0}; 16 | int ret = 0; 17 | kp.symbol_name = "kallsyms_lookup_name"; 18 | 19 | ret = register_kprobe(&kp); 20 | 21 | if (ret < 0) 22 | return ret; 23 | 24 | kallsyms = (kallsymsFn)kp.addr; 25 | 26 | unregister_kprobe(&kp); 27 | 28 | return ret; 29 | } 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += kallsyms_example.o 2 | kallsyms_example-objs := main.o kallsyms.o 3 | MCFLAGS += -g 4 | ccflags-y += ${MCFLAGS} 5 | CC += ${MCFLAGS} 6 | KDIR := /lib/modules/$(shell uname -r)/build 7 | ifndef OUT_DIR 8 | KOUTPUT := $(PWD)/build 9 | else 10 | KOUTPUT := $(OUT_DIR) 11 | endif 12 | 13 | KOUTPUT_MAKEFILE := $(KOUTPUT)/Makefile 14 | 15 | all: $(KOUTPUT_MAKEFILE) 16 | @echo "$(KOUTPUT)" 17 | 18 | make -C $(KDIR) M=$(KOUTPUT) src=$(PWD) modules 19 | 20 | $(KOUTPUT): 21 | mkdir -p "$@" 22 | 23 | $(KOUTPUT_MAKEFILE): $(KOUTPUT) 24 | touch "$@" 25 | 26 | clean: 27 | make -C $(KDIR) M=$(KOUTPUT) src=$(PWD) clean 28 | $(shell rm $(KOUTPUT_MAKEFILE)) 29 | rmdir $(KOUTPUT) 30 | -------------------------------------------------------------------------------- /kallsyms_lp.c: -------------------------------------------------------------------------------- 1 | #include "kallsyms.h" 2 | #include 3 | #include 4 | 5 | MODULE_INFO(livepatch, "Y"); 6 | 7 | static struct klp_func funcs[] = { 8 | { 9 | .old_name = "kallsyms_lookup_name", 10 | .new_func = kallsyms_lookup_name, 11 | }, {} 12 | }; 13 | 14 | static struct klp_func failfuncs[] = { 15 | { 16 | .old_name = "___________________", 17 | }, {} 18 | }; 19 | 20 | static struct klp_object objs[] = { 21 | { 22 | .funcs = funcs, 23 | }, 24 | { 25 | .name = "kallsyms_failing_name", 26 | .funcs = failfuncs, 27 | }, { } 28 | }; 29 | 30 | static struct klp_patch patch = { 31 | .mod = THIS_MODULE, 32 | .objs = objs, 33 | }; 34 | 35 | unsigned long kallsyms_lookup_name(const char *name) 36 | { 37 | return ((unsigned long(*)(const char *))funcs->old_func)(name); 38 | } 39 | 40 | int init_kallsyms(void) 41 | { 42 | int r = klp_enable_patch(&patch); 43 | 44 | if (!r) 45 | return -1; 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "kallsyms.h" 5 | #include "ksyms.h" 6 | #include 7 | 8 | MODULE_DESCRIPTION("Access to kallsyms_lookup_symbol through kernel livepatch interface"); 9 | MODULE_AUTHOR("Heep"); 10 | MODULE_LICENSE("GPL"); 11 | 12 | static int example_init(void); 13 | static void example_exit(void); 14 | 15 | module_init(example_init); 16 | module_exit(example_exit); 17 | 18 | #define mprintk(format, ...) printk(KBUILD_MODNAME": "format, ##__VA_ARGS__) 19 | 20 | KSYMDEF(kvm_lock); 21 | KSYMDEF(vm_list); 22 | 23 | static int example_init(void) 24 | { 25 | int r; 26 | 27 | if ((r = init_kallsyms())) 28 | return r; 29 | 30 | KSYMINIT_FAULT(kvm_lock); 31 | KSYMINIT_FAULT(vm_list); 32 | 33 | if (r) 34 | return r; 35 | 36 | mprintk("initialized\n"); 37 | 38 | mprintk("kvm_lock: %p\n", _kvm_lock); 39 | mprintk("vm_list: %p\n", _vm_list); 40 | 41 | return 0; 42 | } 43 | 44 | static void example_exit(void) 45 | { 46 | mprintk("uninitialized\n"); 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Access to kallsyms on Linux 5.7+ 2 | 3 | `kallsyms_lookup_name` is a wonderful interface that is useful for quickly hacking kernel code without recompiling most of default kernels. Unfortunately, it is also very easy to abuse it, and deincentivizes hardware manufacturers from open-sourcing their drivers and adding them to mainline kernel tree. This is one of the reasons, why the function was unexported in Linux release 5.7. 4 | 5 | This library is meant specifically for hacking the kernel code away, for not too serious projects - those that almost certainly wouldn't be included in the kernel. It uses variety of methods to retrieve `kallsyms_lookup_name` address. One of them is the kernel livepatching interface which has been available at least in 5.0+. We trick it into giving us the `kallsyms_lookup_name` address without actually patching the function. As long as there isn't an active patch on the function, this method should work just fine. The other one is using kprobes, which takes precedence. Based on kernel config, the right implementation is chosen. And on kernel versions <5.7, a null implementation is used. 6 | --------------------------------------------------------------------------------