搭建开发环境¶
为了简单起见,我这里使用 vagrant 搭建虚拟机开发环境:
- 安装 vagrant-env 插件:
$ vagrant plugin install vagrant-env
- clone 示例代码仓库:
$ mkdir -p $GOPATH/src/github.com/mozillazg && \
cd $GOPATH/src/github.com/mozillazg && \
git clone https://github.com/mozillazg/hello-libbpfgo.git && \
cd hello-libbpfgo && \
git submodule update --init --recursive
- 配置 .env 文件(用于将本机的 GOPATH 挂载到虚拟机中):
# 修改 .env 文件,将 GOPATH 的值修改为本机对应的路径
$ cp .env.example .env
- 启动虚拟机:
$ vagrant up
- 等虚拟机就位后,进入虚拟机:
$ vagrant ssh
编写示例程序¶
这里使用 hello-libbpfgo 中 01-hello-world 目录下的示例程序为例简单介绍一下示例程序。
main.ebpf.c:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
char file_name[256];
bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));
char fmt[] = "open file %s\n";
bpf_trace_printk(fmt, sizeof(fmt), &file_name);
return 0;
}
main.ebpf.c 这个程序比较简单,就是hook do_sys_openat2 这个内核函数的调用, 将函数调用参数中的 filename 信息给记录下来。
主要看一下,怎么在 go 程序中使用 libbpfgo 调用这个 ebpf 程序:
main.go:
package main
import (
"fmt"
"time"
bpf "github.com/aquasecurity/libbpfgo"
)
func main() {
bpfModule, err := bpf.NewModuleFromFile("main.bpf.o")
if err != nil {
panic(err)
}
defer bpfModule.Close()
if err := bpfModule.BPFLoadObject(); err != nil {
panic(err)
}
prog, err := bpfModule.GetProgram("kprobe__do_sys_openat2")
if err != nil {
panic(err)
}
if _, err := prog.AttachKprobe("do_sys_openat2"); err != nil {
panic(err)
}
for {
fmt.Println("Waiting...")
time.Sleep(10 * time.Second)
}
}
从上面的 go 程序中可以看到,使用 libbpfgo 调用 ebpf 程序主要有四个步骤:
- 通过 bpf.NewModuleFromFile 方法读取编译好的 .o 文件
- 使用 bpfModule.BPFLoadObject() 加载读取的 .o 文件中对象信息
- 使用 bpfModule.GetProgram 获取 ebpf 程序中对应的 hook 函数
- 如果这个函数是个 kprobe hook 函数,那么就调用 prog.AttachKprobe 把它 attach 到对应的内核函数hook中。
下面编译程序然后看一下对应的效果:
$ vagrant ssh
$ cd $GOPATH/src/github.com/mozillazg/hello-libbpfgo/01-hello-world
$ make
$ sudo ./main
另开一个终端,查看 ebpf 中打印的信息:
$ vagrant ssh
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
runc:[2:INIT]-100616 [000] d... 5527.233315: bpf_trace_printk: open file /proc/self/fd
runc:[2:INIT]-100616 [000] d... 5527.233641: bpf_trace_printk: open file /proc/self/status
runc:[2:INIT]-100616 [000] d... 5527.233802: bpf_trace_printk: open file /etc/passwd
runc:[2:INIT]-100616 [000] d... 5527.233829: bpf_trace_printk: open file /etc/group
更多信息,详见 https://github.com/mozillazg/hello-libbpfgo/tree/master/01-hello-world
参考资料¶
- How to Build eBPF Programs with libbpfgo
- aquasecurity/btfhub: BTFHub, together with BTFHub Archive repository, provides BTF files for existing published kernels that don't support embedded BTF.
- aquasecurity/libbpfgo: eBPF library for Go, wrapping libbpf
- aquasecurity/tracee: Linux Runtime Security and Forensics using eBPF
Comments