在Android中开发eBPF程序学习总结(二)
在上一章的基础上深入研究
在上一篇文章中,我自己改了一版BPF程序的代码bpftest.c
,代码也在上一篇文章中放出来了,但是一个完整的BPF程序,还需要一个用户态的loader,也就是需要有一个读取BPF程序给我们数据的程序。
之前也说了,可以使用MAP来进行数据交互,在bpftest.c
代码中 bpf_execve_map_update_elem(&key, &event, BPF_ANY);
,把event
结构体更新到key=1的map中,也就是说,把每个进行syscall
调用的程序的pid,gid,还有程序名,更新到MAP中。所以我们需要一个loader,来读取MAP,从而得到这些信息。
最开始,loader我使用的是android demo代码中的那个,但是在使用中发现,没办法读取结构体的值,也搜不到相关文章,能搜到示例代码的value类型都是整型,并且我对android开发也不是很熟悉,所以考虑用C自己写一个。
通过strace抓取之前这个loader的系统调用:
1 | bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/prog_bpftest_tracepoint_raw_syscalls_sys_enter", bpf_fd=0, file_flags=0}, 120) = 3 |
通过上面的系统调用,我们就能理清楚,loader程序到底做了哪些工作。
接着我找到了Linux内核中的一个bpf_load.c,参考了一下在普通的Linux系统中,loader是怎么处理的,所以我对该程序进行了修改,增加了以下代码:
1 | struct androidBPF { |
接着,我就能使用C代码来写loader了:
1 |
|
在本地的arm64机器上就能编译了:
1 | $ ls -alF |
上面的loader只简单实现了一下读取map的操作,进阶的玩法还可以更新map的数据,比如我只想监控curl
程序,那么可以把111=>curl
写入map当中,然后在BPF程序中,从map[111]
获取value,只有当comm == map[111]
的情况下,才把信息写入map当中。
我们重新再来理解一下loader的操作:
- BPF_OBJ_GET prog_bpftest_tracepoint_raw_syscalls_sys_enter,获取prog对象
- 读取SEC定义的section的id,从
/sys/kernel/tracing/events/raw_syscalls/sys_enter/id
获取 - perf_event_open打开相应时间,因为是tracepoint,所以type要设置为PERF_TYPE_TRACEPOINT,config等于上面获取id
- 打开事件后,获取了一个文件描述符,对该文件描述符进行ioctl操作,操作的命令有两个,
PERF_EVENT_IOC_SET_BPF
和PERF_EVENT_IOC_ENABLE
,PERF_EVENT_IOC_SET_BPF设置为prog对象的文件描述符
到这里为止,表示激活了你想调用的BPF程序了,要不然默认情况下BPF都处于未激活状态。
接下来就是对map的操作:
- BPF_OBJ_GET /sys/fs/bpf/map_bpftest_execve_map,获取map对象。
- BPF_MAP_LOOKUP_ELEM {map_fd=5, key=0x7ff104b5f4, value=0x7ff104b5e8},从map_fd中搜索key对应的value,储存在value的指针中返回。
目前这块的资料太少了,只能通过一些demo和源码来进行研究,下一篇将会研究uprobe的用法。
参考
在Android中开发eBPF程序学习总结(二)