aya1基础教程

开发环境

安装rust, 并使用nightly版本(nightly版本可选)

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly

在linux x86_64上安装bpf-linker

cargo install bpf-linker

如果是其他架构的linux或者macos则需要先安装LLVM15, 然后安装连接器

cargo install --no-default-features --features system-llvm bpf-linker

安装cargo-generate用于生成项目模板

cargo install cargo-generate

安装bpftool用于生成BTF
Ubuntu发行版(推荐Ubuntu 20.04 LTS (Focal))使用以下命令安装

sudo apt install linux-tools-$(uname -r)
export PATH=/usr/lib/linux-tools/$(uname -r):$PATH

创建项目

使用cargo-generate创建新项目

cargo generate https://github.com/aya-rs/aya-template

输入项目名: do-sys-open
选择eBPF程序类型: kprobe
输入挂载点: do_sys_open

框架分析

创建的新项目应该有以下结构

$ ls
Cargo.lock  Cargo.toml  do-sys-open  do-sys-open-common  do-sys-open-ebpf  README.md  xtask

do-sys-open: 与项目名同名的是用户态程序, 用于加载eBPF程序
do-sys-open-ebpf: 项目名+ebpf的是eBPF程序, 将被载入内核执行
do-sys-open-common: 项目名+common的是一个公共的模块

示例项目

我们刚刚创建了一个挂载到do_sys_open的kprobe类型的eBPF程序
我们将简单实现获取filename参数功能
得益于aya1注重开发体验, 生成的项目已经实现了用户态程序, 和一个包含简单输出的被挂载函数
我们只需添加获取参数的功能, 专注于功能开发

do-sys-open/do-sys-open-ebpf/src/main.rs

... omitted ...

use aya_bpf::helpers::bpf_probe_read_user_str_bytes;
use aya_bpf::bpf_printk;

... omitted ...

fn try_do_sys_open(ctx: ProbeContext) -> Result<u32, u32> {
    let mut buf = [0u8; 16];

    let filename = ctx.arg::<*const u8>(1).unwrap();
    let filename = unsafe {
        core::str::from_utf8_unchecked(bpf_probe_read_user_str_bytes(filename, &mut buf).unwrap())
    }
    .as_ptr();

    unsafe {
        bpf_printk!(b"filename: %s", filename);
    }

    info!(&ctx, "function do_sys_open called");
    Ok(0)
}

目前aya1的内核程序相关的文档不太完善, 但是也有一些: https://docs.aya-rs.dev/bpf/aya_bpf/index.html
其中ProbeContext结构封装了一下pt_regs结构体
我们可以方便的使用arg方法获取参数值(下标从0开始)
获取到的值只是个地址, 还需要使用bpf_probe_read_user_str_bytes函数将从用户态读取数据, 原因如下:

extern long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode);

其中filename参数有__user宏, 代表这个参数只是一个地址, 数据在用户态, 使用前需要从用户态读取

from_utf8_unchecked函数将bpf_probe_read_user_str_bytes读取到的数据从&[u8]转换为&str(bpf_printk宏不能输出&[u8]类型)

最后使用bpf_printk宏输出filename

编译程序

编译程序分为两步:

  1. 编译eBPF程序
  2. 编译用户态加载程序

编译eBPF程序:

cargo xtask build-ebpf --release

--release可选构建发布版

编译用户态程序:

cargo b -r

-r等于--release, 可选构建发布版
注意: 发布版用户态程序只会加载发布版eBPF程序

运行程序

生成的程序在项目根目录target/release下
运行需要root权限

sudo ./do-sys-open

bpf_printk宏不会输出到标准输出
使用如下命令查看输出:

echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on
sudo cat /sys/kernel/debug/tracing/trace_pipe

完整代码

https://github.com/b1nhack/do-sys-open


  1. Aya is an eBPF library for the Rust programming language, built with a focus on developer experience and operability. ↩︎ ↩︎ ↩︎