测试环境:openEuler 22.03
架构:x86_64
Linux内核原本提供了kallsyms_lookup_name用于获取未exported函数的指针位置,但是在Linux Kernel 5.7.0版本之后,kallsyms_lookup_name函数也没有export了,下面模块代码中提供了一种通过kprobe获得kallsyms_lookup_name函数起始地址的方式。
代码主要流程:
(1)调用register_kprobe获得kallsyms_lookup_name函数的地址指针kallsyms_lookup_name_func
(2)调用kallsyms_lookup_name_func获取其它未export的函数的地址指针(print_modules)
// allsyms_lookup_name_test
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
int noop_pre(struct kprobe *p, struct pt_regs *regs) { return 0; }
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name",
};
unsigned long (*kallsyms_lookup_name_fun)(const char *name) = NULL;
// 调用kprobe找到kallsyms_lookup_name的地址位置
int find_kallsyms_lookup_name(void)
{
int ret = -1;
kp.pre_handler = noop_pre;
ret = register_kprobe(&kp);
if (ret < 0) {
printk(KERN_INFO "register_kprobe failed, error:%d\n", ret);
return ret;
}
printk(KERN_INFO "kallsyms_lookup_name addr: %p\n", kp.addr);
kallsyms_lookup_name_fun = (void*)kp.addr;
unregister_kprobe(&kp);
return ret;
}
static int __init kallsyms_lookup_name_test_init(void)
{
int ret;
ret = find_kallsyms_lookup_name();
if (ret < 0) {
printk(KERN_INFO "find kallsyms_lookup_name failed\n");
return ret;
}
static typeof(&print_modules) print_modules_p;
//调用kallsyms_lookup_name_fun指针找到print_modules函数起始地址位置
print_modules_p = (typeof(&print_modules))kallsyms_lookup_name_fun("print_modules");
//打印内核加载的模块
print_modules_p();
printk(KERN_INFO "print_modules_p: 0x%p", print_modules_p);
return 0;
}
static void __exit kallsyms_lookup_name_test_exit(void) { return; }
module_init(kallsyms_lookup_name_test_init);
module_exit(kallsyms_lookup_name_test_exit);
MODULE_LICENSE("GPL");
Makefile如下:
obj-m+=kallsyms_lookup_name_test.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=${PWD} modules
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=${PWD} clean
测试结果:
参考资料: