这里使用的是rust的aya框架!
最近出现了一个使用BPF实现的外挂样本!
准备环境
rustup install stable
rustup toolchain install nightly --component rust-src
rustup target add aarch64-unknown-linux-musl
安装 Rust 工具链后,还必须安装。链接器依赖于 LLVM,如果您在 Linux x86_64 系统上bpf-linker
运行,则可以针对 Rust 工具链附带的版本进行构建(默认且推荐,直接使用rust自带的LLVM,而不是使用外部的):
cargo install bpf-linker
在基于 Debian 的发行版上,需要安装
llvm-19-dev
、libclang-19-dev
和libpolly-19-dev
包(如果使用 LLVM 19)。
如果在任何其他架构上运行macos 或 linux,则需要先安装最新稳定版本的 LLVM(例如,使用 brew install llvm
),然后使用以下命令安装链接器:
LLVM_SYS_180_PREFIX=$(brew --prefix llvm) cargo install \
--no-default-features bpf-linker
手动写aya的项目太麻烦了,需要 cargo-generate
去生成一个模板,安装命令:
cargo install cargo-generate
最后,为了生成内核数据结构的绑定,您必须安装 ,可以从您的发行版安装,也可以从源代码bpftool
构建 。
https://aya-rs.dev/book/start/development/#prerequisites
输入以下命令创建一个示例的项目:
cargo generate https://github.com/aya-rs/aya-template
关于项目类型,如果你是一点基础都没有的,你可能需要看一下:https://aya-rs.dev/book/programs/
eBPF 程序约束
eBPF 程序将运行在 eBPF 虚拟机中,这是一个非常小,非常不实用的运行时环境:
堆栈只有 512 字节
无法访问堆空间,而必须将数据写入映射
在 Rust 中限制如下:
几乎无法标准库。可以使用
core
其他库(例如字符串的contains
用不了)core::fmt
用不了,并且依赖它的特征也不能被使用,例如Display
,Debug
由于没有堆,不能使用
alloc
或collections
不能这样
panic
,因为 eBPF VM 不支持堆栈展开和abort
指令没有
main
功能
除此之外,我们编写的很多代码都是unsafe
,需要使用helper
包下的函数去进行一些额外操作。
安卓平台特化
创建完模板项目,会有一个README.md,这里会说怎么在本地机器上面运行,但是本文目的是运行到安卓arm64平台,所以说大概看看就好了,没什么用(((
首先在生成出来的项目根目录创建一个.cargo
文件夹,里面放一个config.toml
,内容如下
[target.aarch64-unknown-linux-musl]
linker = "ld.lld"
既然用了lld
,所以说要安装一下,这里展示的是fedora平台的,如果是apt,把dnf换成apt就好了:
sudo dnf install lld
这里需要注意的是aya非常的阴间,他设定平台用的是环境变量,而不是指定feature,需要设置环境变量:CARGO_CFG_BPF_TARGET_ARCH=aarch64
如果你是RustRover,会更加麻烦,需要去到设置设置环境变量,非常的不优雅!!!
构建命令是,构建成功后会生成一个可执行文件,直接运行就好了
cargo build --target aarch64-unknown-linux-musl --release
后记
let program: &mut KProbe = ebpf.program_mut("insmod_tracer_arm64_sys_finit_module").unwrap().try_into()?;
program.attach("__arm64_sys_finit_module", 0)?;
最开始我比较好奇program_mut("insmod_tracer_arm64_sys_finit_module")
里面这坨字符串是什么,但是文档没有,AI也不会(GPT-5),后面我改改,又看了他的宏实现才发现,这个字符串,其实是方法名称
#[kprobe(function = "__arm64_sys_finit_module")]
pub fn insmod_tracer_arm64_sys_finit_module(ctx: ProbeContext) -> u32 {
info!(&ctx, "kprobe called __arm64_sys_finit_module: pid = {}", ctx.pid());
0
}
然后attach
里面是你要附加的方法,我这里例子里面是__arm64_sys_finit_module
!
读取用户空间字符串
let mut buf = [0u8; 256];
let my_str = unsafe {
core::str::from_utf8_unchecked(bpf_probe_read_user_str_bytes(arg1 as *const u8, &mut buf).map_err(|e| 1u32)?)
};