0%

Android-eBPF监控所有系统调用

起因

最近泓清和我聊了下 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);

//syscalls_enter的参数,下面有说明
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;
};


// SEC("raw_syscalls/sys_enter")
DEFINE_BPF_PROG("tracepoint/raw_syscalls/sys_enter", AID_ROOT, AID_NET_ADMIN, sys_enter)
(struct syscalls_enter_args *args)
{
//获取进程信息
// struct task_struct *task = (void *)bpf_get_current_task();

// int key = bpf_get_smp_processor_id();
int key = bpf_get_current_pid_tgid();//这里是强制取低32位,也就是pid
uint32_t syscall_id=args->id;//拿到调用的syscall_id

bpf_sys_enter_map_update_elem(&key, &syscall_id, BPF_ANY);
return 0;
}

// char _license[] SEC("license") = "GPL";
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";
// Attach tracepoint and wait for 4 seconds
int mProgFd = bpf_obj_get(tp_prog_path);
// int mMapFd = bpf_obj_get(tp_map_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);//遍历map
}

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 初探