本文作者:uiop@360SRC
看到越来越多使用eBPF的文章,奈何手边没有高版本的手机,只能使用开发版进行体验,踩了许多坑之后记录一下简单使用过程。
一、ebpf简介
eBPF 是一项革命性的技术,起源于 Linux 内核,可以在操作系统的内核中运行沙盒程序。它被用来安全和有效地扩展内核的功能,而不需要改变内核的源代码或加载内核模块。
eBPF 通过允许在操作系统内运行沙盒程序,应用程序开发人员可以在运行时,可编程地向操作系统动态添加额外的功能。然后,操作系统保证安全和执行效率,就像在即时编译(JIT)编译器和验证引擎的帮助下进行本地编译一样。eBPF 程序在内核版本之间是可移植的,并且可以自动更新,从而避免了工作负载中断和节点重启。
ebpf工作原理
eBPF 程序在事件触发时由内核运行,所以可以被看作是一种函数挂钩或事件驱动的编程形式。从用户空间运行按需 eBPF 程序的价值较小,因为所有的按需用户调用已经通过正常的非 VM 内核 API 调用(“syscalls”)来处理,这里 VM 字节码带来的价值很小。事件可由 kprobes/uprobes、tracepoints、dtrace probes、socket 等产生。这允许在内核和用户进程的指令中钩住(hook)和检查任何函数的内存、拦截文件操作、检查特定的网络数据包等等。
运行 eBPF 程序的步骤
用户空间将字节码和程序类型一起发送到内核,程序类型决定了可以访问的内核区域(主要是 BPF 辅助函数的各种子集)。
内核在字节码上运行验证器,以确保程序可以安全运行(kernel/bpf/verifier.c)。
内核将字节码编译为本地代码,并将其插入(或附加到)指定的代码位置。(如果启用了 JIT 功能,字节码编译为本地代码)。
插入的代码将数据写入环形缓冲区或通用键值 map。
用户空间从共享 map 或环形缓冲区中读取结果值。
eBPF 程序的高层次组件
后端:这是在内核中加载和运行的 eBPF 字节码。它将数据写入内核 map 和环形缓冲区的数据结构中。
加载器:它将字节码后端加载到内核中。通常情况下,当加载器进程终止时,字节码会被内核自动卸载。
前端:从数据结构中读取数据(由后端写入)并将其显示给用户。
数据结构:这些是后端和前端之间的通信手段。它们是由内核管理的 map 和环形缓冲区,可以通过文件描述符访问,并需要在后端被加载之前创建。它们会持续存在,直到没有更多的后端或前端进行读写操作。
二、为什么选择ebpf
Linux内核的主要目的是抽象硬件或虚拟硬件并提供一致的API(系统调用),允许应用程序运行和共享资源。为了实现这一目标,需要维护大量的子系统和层来分配这些职责。每个子系统通常允许某种级别的配置来满足用户的不同需求。如果无法配置所需的行为,则需要更改内核,从历史上看,留下两个选项:
原生支持
更改内核源代码并说服 Linux 内核社区需要进行更改。
等待数年新内核版本才能成为商品。
内核模块
编写内核模块
定期修复它,因为每个内核版本都可能会破坏它
由于缺乏安全边界而存在损坏 Linux 内核的风险
借助 eBPF,可以使用一个新选项,允许对 Linux 内核的行为进行重新编程,而无需更改内核源代码或加载内核模块。在很多方面,这与 JavaScript 和其他脚本语言如何解锁系统的演变非常相似,而系统的改变变得困难或昂贵。
三、开发版上环境搭建
环境搭建
使用rk3588开发版armbian系统+redroid作为开发环境
按照官方wiki刷好最新的armbian系统
https://wiki.radxa.com/Rock5/5b/getting_started
在armbian上使用redroid(新版armbian内核直接可以使用redroid了,也可以直接使用cuttlefish)
安装docker
$ sudo apt-get update
$ sudo apt-get upgrade
$ curl -fsSL test.docker.com -o get-docker.sh && sh get-docker.sh
$ sudo usermod -aG docker $USER
$ docker run -itd --rm --privileged \
--pull always \
-v ~/data:/data \
-p 5555:5555 \
redroid/redroid:12.0.0-arm64
编译支持ebpf功能的内核(如果是ubuntu可以按照官方教程编译)
下载依赖包
$ sudo apt-get update
$ sudo apt-get install -y git device-tree-compiler libncurses5 libncurses5-dev build-essential libssl-dev mtools bc python3 dosfstools bison flex rsync u-boot-tools make
安装python2
$ cd /usr/local/lib/
$ sudo wget https://www.python.org/ftp/python/2.7/Python-2.7.tgz
$ sudo tar -zxvf Python-2.7.tgz
$ sudo ln -snf /usr/local/lib/Python-2.7/python /usr/bin/python2
下载内核并修改支持ebpf配置
$ sudo apt install linux-source-5.10.110-legacy-rockchip-rk3588
$ mkdir kernel
$ cd kernel/
$ tar -xf /usr/src/linux-source-5.10.110-rockchip-rk3588.tar.xz
$ sudo xz -d /usr/src/linux-rockchip-rk3588-legacy_5.10.110_23.02.2_config.xz
$ cp /usr/src/linux-rockchip-rk3588-legacy_5.10.110_23.02.2_config .config
$ make mrproper
$ nano .config
修改以下配置
CONFIG_IKHEADERS=y
CONFIG_ASHMEM=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_KPROBES=y
CONFIG_KRETPROBES=y
开始编译
$ make -j8
$ sudo make modules_install
$ sudo make install
$ cd /boot/
$ sudo mv Image Image.old
$ sudo ln -s vmlinuz-5.10.110 Image
最后重启即可
报错解决
如果出现warning错误而导致的error, forbidden warning
kernel/scripts/gcc-wrapper.py
四、ebpf开发环境搭建
1. 切换docker的bridge网络模式为macvlan
创建macvlan注意修改网段及网卡名为自己的
$ docker network create -d macvlan --subnet=192.168.0.0/24 --gateway=192.168.0.1 -o parent=enP4p65s0 -o macvlan_mode=bridge mymacvlan
重新分配ip并启动redroid
$ docker run --net=mymacvlan --ip=192.168.0.100 -itd --rm --privileged \
-v ~/data:/data \
redroid/redroid:12.0.0-latest \
androidboot.redroid_width=1080 \
androidboot.redroid_height=1920 \
androidboot.redroid_dpi=480
2. 根据以下两篇文章完成bcc开发环境搭建
https://blog.seeflower.dev/archives/138/
https://blog.seeflower.dev/archives/111/
修改配置不同处
3. 最后可以获取这样的效果
五、简单体验bcc
1. 执行execsnoop来查看rootbeer的检测点
2. 编写ebpf程序,并绕过root检测
简单看来直接在exec基础上修改系统调用的参数即可绕过检测
参考:
https://blog.seeflower.dev/category/eBPF/
https://ebpf.io/zh-cn/what-is-ebpf/