结合前面一篇针对BPF的学习,本篇文章重点介绍编写一个对内核系统调用exec的例子。本测试例子基本上包含了全部的,syscall类别系统调用的BPF的框架。
-
#!/usr/bin/python
-
from __future__ import print_function
-
from bcc import BPF
-
from collections import defaultdict
-
-
bpf_text = """
-
#include
-
#include
-
#include
-
-
#define ARGSIZE 256
-
struct data_t {
-
u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
-
char comm[TASK_COMM_LEN];
-
char argv[ARGSIZE];
-
};
-
-
BPF_PERF_OUTPUT(events);
-
-
int syscall__execve(struct pt_regs *ctx,
-
const char __user *filename,
-
const char __user *const __user *__argv,
-
const char __user *const __user *__envp)
-
{
-
struct data_t data = {};
-
-
-
bpf_trace_printk("Hello, World!222%s\\n",filename);
-
data.pid = bpf_get_current_pid_tgid();
-
bpf_get_current_comm(&data.comm,sizeof(data.comm));
-
-
bpf_probe_read_user(data.argv, sizeof(data.argv), filename);
-
-
events.perf_submit(ctx, &data, sizeof(struct data_t));
-
-
return 0;
-
}
-
-
"""
-
b = BPF(text=bpf_text)
-
execve_fnname = b.get_syscall_fnname("execve")
-
b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")
-
print("%-18s %-16s %-14s" % ("COMM", "PID","ARGS"))
-
-
-
argv = defaultdict(list)
-
-
def print_event(cpu, data, size):
-
event = b["events"].event(data)
-
argv[event.pid].append(event.argv)
-
-
argv_text = b' '.join(argv[event.pid]).replace(b'\n', b'\\n')
-
-
print("%-18s %-16d %-14s" % (event.comm, event.pid,argv_text))
-
-
-
b["events"].open_perf_buffer(print_event)
-
while 1:
-
try:
-
b.perf_buffer_poll()
-
except KeyboardInterrupt:
-
exit()
主要注意点:
1. BPF_PERF_OUTPUT
创建BPF的table,通过Perf 的环形缓存区,把用户定义的event事件的数据推送到用户空间,这是把事件数据推送到用户空间的首选的方式。
也就是如果把从内核中获取到的数据,push到用户空间进行处理和展示,在代码中添加该宏。
2. b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")
这里的fn_name="syscal__execve"函数名,保持格式的绝对一致,也就是syscall__ 后面两个下划线,execve名字和内核中syscall保持一致。
3. 在内核中插桩syscall__execve 函数时,这里的第一个参数struct pt_regs *ctx 保持固定,其余的参数是syscall类函数保持一致的,下面是调用execve的参数。
int
execve(const char *pathname, char *const argv[],
char *const envp[]);
4. 其实整个程序包含了两个部分,一个是python的客户端,一个是以字符串的形式bpf_text出现的插桩函数,使用C语言编写,整个测试程序包含了,用户空间和内核空间的代码。
5 bpf_trace_printk函数,类似于C语言中printf,编写bpf程序时,可以用来调试打印相关信息。注意该打印函数会将信息输出到下面的文件中:/sys/kernel/debug/tracing/trace_pipe,通过跟踪该文件进行查看。
6. print_even 函数时对内核中获取的数据和信息的统一展示。
阅读(1566) | 评论(0) | 转发(0) |