第一种
第一种是借助于kprobe机制,通过kprobe机制中会调用kallsyms_lookup_name函数并设置到kprobe结构体中返回的原理找到我们需要的函数地址
内核中调用逻辑简化代码如下:
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 | int register_kprobe(struct kprobe *p){ int ret; struct kprobe *old_p; struct module *probed_mod; kprobe_opcode_t *addr; /* Adjust probe address from symbol */ addr = kprobe_addr(p); if (IS_ERR(addr)) return PTR_ERR(addr); p->addr = addr; 。。。。。。}static kprobe_opcode_t *kprobe_addr(struct kprobe *p){ return _kprobe_addr(p->addr, p->symbol_name, p->offset);}static kprobe_opcode_t *_kprobe_addr(kprobe_opcode_t *addr, const char *symbol_name, unsigned int offset){ if ((symbol_name && addr) || (!symbol_name && !addr)) goto invalid; if (symbol_name) { addr = kprobe_lookup_name(symbol_name, offset); if (!addr) return ERR_PTR(-ENOENT); } 。。。。。}kprobe_opcode_t * __weak kprobe_lookup_name(const char *name, unsigned int __unused){ return ((kprobe_opcode_t *)(kallsyms_lookup_name(name)));} |
如上逻辑可以看到其实就是调用的kallsyms_lookup_name函数来获取地址存到我们的addr中
第二种
第二种方式是借助于内核调试器提供的能力间接调用kallsyms_lookup_name函数获取地址
在内核中有这样一个函数并且这个函数是导出的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | extern int kdbgetsymval(const char *, kdb_symtab_t *);int kdbgetsymval(const char *symname, kdb_symtab_t *symtab){ if (KDB_DEBUG(AR)) kdb_printf("kdbgetsymval: symname=%s, symtab=%px\n", symname, symtab); memset(symtab, 0, sizeof(*symtab)); symtab->sym_start = kallsyms_lookup_name(symname); if (symtab->sym_start) { if (KDB_DEBUG(AR)) kdb_printf("kdbgetsymval: returns 1, " "symtab->sym_start=0x%lx\n", symtab->sym_start); return 1; } if (KDB_DEBUG(AR)) kdb_printf("kdbgetsymval: returns 0\n"); return 0;}EXPORT_SYMBOL(kdbgetsymval); |
代码示例
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 | /************************************************************************* > File Name: test.c > Author: > Mail: > Created Time: 2024年06月09日 星期日 11时34分43秒 > 两种获取内核未导出函数地址方法 ************************************************************************/#include <linux/init.h>#include <linux/module.h>#include <linux/kallsyms.h>#include <linux/kprobes.h>#include <linux/cdev.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("ch");typedef struct __ksymtab { unsigned long value; /* Address of symbol */ const char *mod_name; /* Module containing symbol or * "kernel" */ unsigned long mod_start; unsigned long mod_end; const char *sec_name; /* Section containing symbol */ unsigned long sec_start; unsigned long sec_end; const char *sym_name; /* Full symbol name, including * any version */ unsigned long sym_start; unsigned long sym_end; } kdb_symtab_t;extern int kdbgetsymval(const char *, kdb_symtab_t *);struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; char name[64]; struct cdev *cdev; /* will die */};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_func)(const char* name) = NULL;static int __init m_init(void) { kp.pre_handler = noop_pre; int ret = register_kprobe(&kp); if (ret != 0) { printk("find failed\n"); return ret; } kallsyms_lookup_name_func = (void*)kp.addr; printk("lookup func addr is %p\n", kp.addr); unregister_kprobe(&kp); unsigned long chrdevs_addr = kallsyms_lookup_name_func("chrdevs"); printk("chardevs addr is 0x%p\n", chrdevs_addr); kdb_symtab_t val; kdbgetsymval("chrdevs", &val); printk("second method get chrdevs addr is 0x%p\n", val.sym_start); return 0;}static void __exit m_exit(void) {}module_init(m_init);module_exit(m_exit); |