如何用eBPF(BCC)获取系统调用表地址?Kprobe及符号表疑问
如何用eBPF的Kprobe获取系统调用地址及查找内核符号表
一、查找内核符号表中系统调用表的方法
内核符号表包含内核导出的所有函数、变量地址,系统调用表的符号名随架构不同有所差异:
- x86_64 架构:符号名为
sys_call_table - ARM64 架构:通常为
sys_call_table或el0_sys_call_table - 其他架构可通过内核文档或源码确认
1. 用户空间直接查看
可以通过 /proc/kallsyms 文件直接查询:
# 查看系统调用表地址 cat /proc/kallsyms | grep sys_call_table
如果输出为空,说明内核限制了符号访问,临时解除限制:
sysctl -w kernel.kptr_restrict=0
注意:该设置会在重启后失效,生产环境需谨慎操作。
2. eBPF程序内动态查找
在eBPF程序中无法直接读取 /proc 文件,需使用内核提供的 bpf_kallsyms_lookup_name 函数查找符号地址。但该函数属于GPL-only接口,因此你的eBPF程序必须声明为GPL兼容:
char __license[] SEC("license") = "GPL";
二、eBPF程序实现获取系统调用地址
以下是示例程序,通过Kprobe触发时查找系统调用表,并获取指定系统调用(如sys_open)的地址:
#include <vmlinux.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> // 声明GPL许可证,才能使用bpf_kallsyms_lookup_name char __license[] SEC("license") = "GPL"; // 通过Kprobe hook任意内核函数(这里以sys_open为例)触发逻辑 SEC("kprobe/sys_open") int BPF_KPROBE(sys_open) { void *sys_call_table; void *sys_open_addr; // 查找系统调用表地址 sys_call_table = (void *)bpf_kallsyms_lookup_name("sys_call_table"); if (!sys_call_table) { bpf_printk("Failed to find sys_call_table"); return 0; } // 根据系统调用号获取对应地址,__NR_open为x86_64下open的系统调用号 sys_open_addr = ((void **)sys_call_table)[__NR_open]; bpf_printk("sys_open address: %p", sys_open_addr); return 0; }
三、关键注意事项
- 系统调用号兼容性:不同内核版本、架构的系统调用号可能不同,建议在用户空间通过
sys/syscall.h中的__NR_*宏获取,或动态传递给eBPF程序。 - 内核符号隐藏:部分发行版内核可能未导出
sys_call_table,此时可尝试通过hook内核中调用系统调用的入口函数(如do_syscall_64)间接获取系统调用表地址。 - Kprobe限制:部分内核函数被加入Kprobe黑名单,无法直接hook,需提前确认目标函数是否允许Kprobe附着。
- 权限问题:加载eBPF程序需要CAP_BPF、CAP_PERFMON等权限,运行时需以root用户执行。
内容的提问来源于stack exchange,提问作者bbpfuser
相关产品推荐
相关产品推荐

