起因
最近泓清和我聊了下 Android ebpf 的事情,最初是维术大佬开直播讲这个,无奈晦涩难懂,于是自己网上找了点资料查查,还算有点收获。本文以监控 syscalls_enter 为例.
环境
1. 支持 eBPF 的手机 (小米 12), 并且有 Magisk.
2.AOSP 环境 (其他方案可以参考维术等等).
eBPF 内核钩子代码实现
不多说,直接上代码, external/bpf_sys.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <linux/bpf.h> #include <stdbool.h> #include <stdint.h> #include <bpf_helpers.h>
DEFINE_BPF_MAP(sys_enter_map, HASH, int, uint32_t, 1024);
struct syscalls_enter_args { unsigned short common_type; unsigned char common_flags; unsigned char common_preempt_count; int common_pid;
long id; unsigned long args[6]; };
struct task_struct { int pid; int tgid; char comm[16]; struct task_struct *group_leader; };
DEFINE_BPF_PROG("tracepoint/raw_syscalls/sys_enter", AID_ROOT, AID_NET_ADMIN, sys_enter) (struct syscalls_enter_args *args) {
int key = bpf_get_current_pid_tgid(); uint32_t syscall_id=args->id;
bpf_sys_enter_map_update_elem(&key, &syscall_id, BPF_ANY); return 0; }
LICENSE("Apache 2.0");
|
这个 syscalls_enter_args 的结构体可以从 /sys/kernel/tracing/events/raw_syscalls/sys_enter/format
获取,从这里可以获取 sys_enter 的所有参数信息.
用户空间程序实现
external/bpf_cli.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <android-base/macros.h> #include <stdlib.h> #include <unistd.h> #include <iostream> #include <bpf/BpfMap.h> #include <bpf/BpfUtils.h> #include <libbpf_android.h>
int main() { constexpr const char tp_prog_path[] = "/sys/fs/bpf/prog_bpf_sys_tracepoint_raw_syscalls_sys_enter"; constexpr const char tp_map_path[] = "/sys/fs/bpf/map_bpf_sys_sys_enter_map"; int mProgFd = bpf_obj_get(tp_prog_path); bpf_attach_tracepoint(mProgFd, "raw_syscalls", "sys_enter"); sleep(1); android::bpf::BpfMap<int, int> myMap(tp_map_path);
const auto iterFunc = [&](const uint32_t &key, const uint32_t &val, android::bpf::BpfMap<int, int> &) { printf("pid is:%d,syscall_id:%d\n", key, val); return android::base::Result<void>(); };
while (1) { usleep(40000); myMap.iterateWithValue(iterFunc); }
exit(0); }
|
这里的代码可以参考官方代码,结合着一起理解
使用 eBPF 扩展内核
编译并加载代码
external/Android.bp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| bpf { name: "bpf_sys.o", srcs: ["bpf_sys.c"], cflags: [ "-Wall", "-Werror", ], }
cc_binary { name: "bpf_cli",
cflags: [ "-Wall", "-Werror", "-Wthread-safety", ], clang: true, shared_libs: [ "libcutils", "libbpf_android", "libbase", "liblog", "libnetdutils", "libbpf", ], srcs: [ "bpf_cli.cpp", ], }
|
在 AOSP 环境下
1 2 3 4
| source build/envsetup.sh lunch armv8-eng cd external/bpf_sys/ mm
|
将编译后的 bpf_sys.o (/out/target/product/armv8/system/etc/bpf)
放入 Magisk 模块里,其路径为 system/etc/bpf
把 bpf_cli (/out/target/product/armv8/system/bin)
放到 /data/local/tmp
里.
1 2 3 4
| su cd /data/local/tmp chmod 777 bpf_cli ./bpf_cli
|
效果如图:
github 地址:
https://github.com/PShocker/Android_bpf_sys
参考:
使用 eBPF 扩展内核
Android S 下的 eBPF
android 平台 eBPF 初探