From 9c447df0f7d0daa4b8ce0d81c38b917239b629ff Mon Sep 17 00:00:00 2001 From: yunwei37 <1067852565@qq.com> Date: Tue, 30 May 2023 18:40:01 +0800 Subject: [PATCH] Add advance topics --- README.md | 37 +++++++--- src/22-android/README.md | 152 +++++++++++++++++++++++++++++++++++++++ src/23-http/README.md | 3 + src/24-hide/README.md | 3 + src/25-signal/README.md | 2 + src/26-sudo/README.md | 3 + src/27-replace/README.md | 3 + 7 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 src/22-android/README.md create mode 100644 src/23-http/README.md create mode 100644 src/24-hide/README.md create mode 100644 src/25-signal/README.md create mode 100644 src/26-sudo/README.md create mode 100644 src/27-replace/README.md diff --git a/README.md b/README.md index b2f7045..7dbdfdf 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ -# eBPF 开发者教程:通过小工具一步步学习 eBPF +# eBPF 开发者教程与知识库:通过小工具一步步学习 eBPF [![CI](https://github.com/eunomia-bpf/bpf-developer-tutorial/actions/workflows/main.yml/badge.svg)](https://github.com/eunomia-bpf/bpf-developer-tutorial/actions/workflows/main.yml) [GitHub](https://github.com/eunomia-bpf/bpf-developer-tutorial) [Gitee 镜像](https://gitee.com/yunwei37/bpf-developer-tutorial) -这是一个基于 `CO-RE`(一次编译,到处运行)的 eBPF 的开发教程,提供了从入门到进阶的 eBPF 开发实践,包括基本概念、代码实例、实际应用等内容。 +这是一个基于 `CO-RE`(一次编译,到处运行)的 eBPF 的开发教程,提供了从入门到进阶的 eBPF 开发实践,包括基本概念、代码实例、实际应用等内容。和 BCC 不同的是,我们使用 libbpf、Cilium、libbpf-rs、eunomia-bpf 等框架进行开发,包含 C、Go、Rust 等语言的示例。 本教程不会进行复杂的概念讲解和场景介绍,主要希望提供一些 eBPF 小工具的案例(**非常短小,从二十行代码开始入门!**),来帮助 eBPF 应用的开发者快速上手 eBPF 的开发方法和技巧。教程内容可以在目录中找到,每个目录都是一个独立的 eBPF 工具案例。 -教程关注于可观测性、网络、安全等等方面的 eBPF 应用。 +教程关注于可观测性、网络、安全等等方面的 eBPF 示例。 ## 目录 +### 入门文档 + +包含简单的 eBPF 程序样例与介绍。 + - [lesson 0-introduce](src/0-introduce/README.md) 介绍 eBPF 的基本概念和常见的开发工具 - [lesson 1-helloworld](src/1-helloworld/README.md) 使用 eBPF 开发最简单的「Hello World」程序,介绍 eBPF 的基本框架和开发流程 - [lesson 2-kprobe-unlink](src/2-kprobe-unlink/README.md) 在 eBPF 中使用 kprobe 捕获 unlink 系统调用 @@ -26,9 +30,9 @@ - [lesson 10-hardirqs](src/10-hardirqs/README.md) 使用 hardirqs 或 softirqs 捕获中断事件 - [lesson 11-bootstrap](src/11-bootstrap/README.md) 使用 libbpf-boostrap 为 eBPF 编写原生的 libbpf 用户态代码,并建立完整的 libbpf 工程。 - [lesson 12-profile](src/12-profile/README.md) 使用 eBPF 进行性能分析 -- [lesson 13-tcpconnlat](src/13-tcpconnlat/README.md) 记录 TCP 连接延迟,并使用 libbpf-boostrap 或 Webassembly (WASM)在用户态处理数据 +- [lesson 13-tcpconnlat](src/13-tcpconnlat/README.md) 记录 TCP 连接延迟,并使用 libbpf 在用户态处理数据 - [lesson 14-tcpstates](src/14-tcpstates/README.md) 记录 TCP 连接状态 -- [lesson 15-tcprtt](src/15-tcprtt/README.md) 以直方图方式记录 TCP RTT,并使用 libbpf-boostrap 或 WASM 在用户态采样 map 信息 +- [lesson 15-tcprtt](src/15-tcprtt/README.md) 以直方图方式记录 TCP RTT,并使用 libbpf 在用户态采样 map 信息 - [lesson 16-memleak](src/16-memleak/README.md) 检测内存泄漏 - [lesson 17-biopattern](src/17-biopattern/README.md) 捕获磁盘 IO 模式 - [lesson 18-further-reading](src/18-further-reading/README.md) 更进一步的相关资料? @@ -36,7 +40,20 @@ - [lesson 20-tc](src/20-tc/README.md) 使用 eBPF 进行 tc 流量控制 - [lesson 21-xdp](src/21-xdp/README.md) 使用 eBPF 进行 XDP 报文处理 -在学习 eBPF 的过程中,我们受到了 [bcc python developer tutorial](src/bcc-documents/tutorial_bcc_python_developer.md) 的许多启发和帮助,但从 2022 年的角度出发,使用 libbpf 开发 eBPF 的应用是目前相对更好的选择。但目前似乎很少有基于 libbpf 和 BPF CO-RE 出发的、通过案例和工具介绍 eBPF 开发的教程,因此我们发起了这个项目,采用类似 bcc python developer tutorial 的组织方式,但使用 CO-RE 的 libbpf 进行开发。 +### 进阶文档与示例程序 + +这里涵盖了一系列和 eBPF 相关的高级内容,包含在 Android 上使用 eBPF 程序、使用 eBPF 程序进行可能的攻击与防御、复杂的追踪等等。将 eBPF 用户态与内核态的部分结合起来,可能能带来巨大的威力(同时也是安全隐患)。 + +- [在 Android 上使用 eBPF 程序](src/22-android/README.md) +- [使用 eBPF 追踪 HTTP 请求或其他用户态协议](src/23-http/README.md) +- [使用 eBPF 隐藏进程或文件信息](src/24-hide/README.md) +- [使用 bpf_send_signal 发送信号终止进程](src/25-signal/README.md) +- [使用 eBPF 添加 sudo 用户](src/26-sudo/README.md) +- [使用 eBPF 替换任意程序读取或写入的文本](src/27-replace/README.md) + +## 为什么要写这个教程? + +在学习 eBPF 的过程中,我们受到了 [bcc python developer tutorial](src/bcc-documents/tutorial_bcc_python_developer.md) 的许多启发和帮助,但从当下的角度出发,使用 libbpf 开发 eBPF 的应用是目前相对更好的选择。但目前似乎很少有基于 libbpf 和 BPF CO-RE 出发的、通过案例和工具介绍 eBPF 开发的教程,因此我们发起了这个项目,采用类似 bcc python developer tutorial 的组织方式,但使用 CO-RE 的 libbpf 进行开发。 本项目主要基于 [libbpf-boostrap](https://github.com/libbpf/libbpf-bootstrap) 和 [eunomia-bpf](https://github.com/eunomia-bpf/eunomia-bpf) 两个框架完成,并使用 eunomia-bpf 帮助简化一部分 libbpf eBPF 用户态代码的编写,让开发者专注于内核态的 eBPF 代码的开发。 @@ -47,10 +64,10 @@ 面对创建一个 eBPF 项目,您是否对如何开始搭建环境以及选择编程语言感到困惑?别担心,我们为您准备了一系列 GitHub 模板,以便您快速启动一个全新的eBPF项目。只需在GitHub上点击 `Use this template` 按钮,即可开始使用。 -- :基于C语言和 libbpf 框架的eBPF项目模板 -- :基于C语言和cilium/ebpf框架的eBPF项目模板 -- :基于Rust语言和libbpf-rs框架的eBPF项目模板 -- :基于C语言和eunomia-bpf框架的eBPF项目模板 +- :基于 C 语言和 libbpf 框架的eBPF 项目模板 +- :基于 Go 语言和cilium/框架的的 eBPF 项目模板 +- :基于 Rust 语言和libbpf-rs 框架的 eBPF 项目模板 +- :基于 C 语言和 eunomia-bpf 框架的eBPF 项目模板 这些启动模板包含以下功能: diff --git a/src/22-android/README.md b/src/22-android/README.md new file mode 100644 index 0000000..18220e6 --- /dev/null +++ b/src/22-android/README.md @@ -0,0 +1,152 @@ +# 在 Andorid 上使用 eBPF 程序 + +> 摘要:本文主要记录了笔者在 Android Studio Emulator 中测试高版本 Android Kernel 对基于 libbpf 的 CO-RE 技术支持程度的探索过程、结果和遇到的问题。 +> 测试采用的方式是在 Android Shell 环境下构建 Debian 环境,并基于此尝试构建 eunomia-bpf 工具链、运行其测试用例。 + +## 背景 + +截至目前(2023-04),Android 还未对 eBPF 程序的动态加载做出较好的支持,无论是以 bcc 为代表的带编译器分发方案,还是基于 btf 和 libbpf 的 CO-RE 方案,都在较大程度上离不开 Linux 环境的支持,无法在 Android 系统上很好地运行[^WeiShu]。 + +虽然如此,在 Android 平台上尝试 eBPF 也已经有了一些成功案例,除谷歌官方提供的修改 `Android.bp` 以将 eBPF 程序随整个系统一同构建并挂载的方案[^Google],也有人提出基于 Android 内核构建 Linux 环境进而运行 eBPF 工具链的思路,并开发了相关工具。 + +目前已有的资料,大多基于 adeb/eadb 在 Android 内核基础上构建 Linux 沙箱,并对 bcc 和 bpftrace 相关工具链进行测试,而对 CO-RE 方案的测试工作较少。在 Android 上使用 bcc 工具目前有较多参考资料,如: + ++ SeeFlowerX: ++ evilpan: + +其主要思路是利用 chroot 在 Android 内核上运行一个 Debian 镜像,并在其中构建整个 bcc 工具链,从而使用 eBPF 工具。如果想要使用 bpftrace,原理也是类似的。 + +事实上,高版本的 Android 内核已支持 btf 选项,这意味着 eBPF 领域中新兴的 CO-RE 技术也应当能够运用到基于 Android 内核的 Linux 系统中。本文将基于此对 eunomia-bpf 在模拟器环境下进行测试运行。 + +> [eunomia-bpf](https://github.com/eunomia-bpf/eunomia-bpf) 是一个结合了 libbpf 和 WebAssembly 技术的开源项目,旨在简化 eBPF 程序的编写、编译和部署。该项目可被视作 CO-RE 的一种实践方式,其核心依赖是 libbpf,相信对 eunomia-bpf 的测试工作能够为其他 CO-RE 方案提供参考。 + +## 测试环境 + ++ Android Emulator(Android Studio Flamingo | 2022.2.1) ++ AVD: Pixel 6 ++ Android Image: Tiramisu Android 13.0 x86_64(5.15.41-android13-8-00055-g4f5025129fe8-ab8949913) + +## 环境搭建[^SeeFlowerX] + +1. 从 [eadb 仓库](https://github.com/tiann/eadb) 的 releases 页面获取 `debianfs-amd64-full.tar.gz` 作为 Linux 环境的 rootfs,同时还需要获取该项目的 `assets` 目录来构建环境; +2. 从 Android Studio 的 Device Manager 配置并启动 Android Virtual Device; +3. 通过 Android Studio SDK 的 adb 工具将 `debianfs-amd64-full.tar.gz` 和 `assets` 目录推送到 AVD 中: + + `./adb push debianfs-amd64-full.tar.gz /data/local/tmp/deb.tar.gz` + + `./adb push assets /data/local/tmp/assets` +4. 通过 adb 进入 Android shell 环境并获取 root 权限: + + `./adb shell` + + `su` +5. 在 Android shell 中构建并进入 debian 环境: + + `mkdir -p /data/eadb` + + `mv /data/local/tmp/assets/* /data/eadb` + + `mv /data/local/tmp/deb.tar.gz /data/eadb/deb.tar.gz` + + `rm -r /data/local/tmp/assets` + + `chmod +x /data/eadb/device-*` + + `/data/eadb/device-unpack` + + `/data/eadb/run /data/eadb/debian` + +至此,测试 eBPF 所需的 Linux 环境已经构建完毕。此外,在 Android shell 中(未进入 debian 时)可以通过 `zcat /proc/config.gz` 并配合 `grep` 查看内核编译选项。 + +>目前,eadb 打包的 debian 环境存在 libc 版本低,缺少的工具依赖较多等情况;并且由于内核编译选项不同,一些 eBPF 功能可能也无法使用。 + +## 工具构建 + +在 debian 环境中将 eunomia-bpf 仓库 clone 到本地,具体的构建过程,可以参考仓库的 [build.md](https://github.com/eunomia-bpf/eunomia-bpf/blob/master/documents/build.md)。在本次测试中,笔者选用了 `ecc` 编译生成 `package.json` 的方式,该工具的构建和使用方式请参考[仓库页面](https://github.com/eunomia-bpf/eunomia-bpf/tree/master/compiler)。 + +>在构建过程中,可能需要自行安装包括但不限于 `curl`,`pkg-config`,`libssl-dev` 等工具。 + +## 结果 + +有部分 eBPF 程序可以成功在 Android 上运行,但也会有部分应用因为种种原因无法成功被执行。 + +### 成功案例 + +#### [bootstrap](https://github.com/eunomia-bpf/eunomia-bpf/tree/master/examples/bpftools/bootstrap) + +运行输出如下: + +```console +TIME PID PPID EXIT_CODE DURATION_NS COMM FILENAME EXIT_EVENT +09:09:19 10217 479 0 0 sh /system/bin/sh 0 +09:09:19 10217 479 0 0 ps /system/bin/ps 0 +09:09:19 10217 479 0 54352100 ps 1 +09:09:21 10219 479 0 0 sh /system/bin/sh 0 +09:09:21 10219 479 0 0 ps /system/bin/ps 0 +09:09:21 10219 479 0 44260900 ps 1 +``` + +#### [tcpstates](https://github.com/eunomia-bpf/eunomia-bpf/tree/master/examples/bpftools/tcpstates) + +开始监测后在 Linux 环境中通过 `wget` 下载 Web 页面: + +```console +TIME SADDR DADDR SKADDR TS_US DELTA_US PID OLDSTATE NEWSTATE FAMILY SPORT DPORT TASK +09:07:46 0x4007000200005000000000000f02000a 0x5000000000000f02000a8bc53f77 18446635827774444352 3315344998 0 10115 7 2 2 0 80 wget +09:07:46 0x40020002d98e50003d99f8090f02000a 0xd98e50003d99f8090f02000a8bc53f77 18446635827774444352 3315465870 120872 0 2 1 2 55694 80 swapper/0 +09:07:46 0x40010002d98e50003d99f8090f02000a 0xd98e50003d99f8090f02000a8bc53f77 18446635827774444352 3315668799 202929 10115 1 4 2 55694 80 wget +09:07:46 0x40040002d98e50003d99f8090f02000a 0xd98e50003d99f8090f02000a8bc53f77 18446635827774444352 3315670037 1237 0 4 5 2 55694 80 swapper/0 +09:07:46 0x40050002000050003d99f8090f02000a 0x50003d99f8090f02000a8bc53f77 18446635827774444352 3315670225 188 0 5 7 2 55694 80 swapper/0 +09:07:47 0x400200020000bb01565811650f02000a 0xbb01565811650f02000a6aa0d9ac 18446635828348806592 3316433261 0 2546 2 7 2 49970 443 ChromiumNet +09:07:47 0x400200020000bb01db794a690f02000a 0xbb01db794a690f02000aea2afb8e 18446635827774427776 3316535591 0 1469 2 7 2 37386 443 ChromiumNet +``` + +开始检测后在 Android Studio 模拟界面打开 Chrome 浏览器并访问百度页面: + +```console +TIME SADDR DADDR SKADDR TS_US DELTA_US PID OLDSTATE NEWSTATE FAMILY SPORT DPORT TASK +07:46:58 0x400700020000bb01000000000f02000a 0xbb01000000000f02000aeb6f2270 18446631020066638144 192874641 0 3305 7 2 2 0 443 NetworkService +07:46:58 0x40020002d28abb01494b6ebe0f02000a 0xd28abb01494b6ebe0f02000aeb6f2270 18446631020066638144 192921938 47297 3305 2 1 2 53898 443 NetworkService +07:46:58 0x400700020000bb01000000000f02000a 0xbb01000000000f02000ae7e7e8b7 18446631020132433920 193111426 0 3305 7 2 2 0 443 NetworkService +07:46:58 0x40020002b4a0bb0179ff85e80f02000a 0xb4a0bb0179ff85e80f02000ae7e7e8b7 18446631020132433920 193124670 13244 3305 2 1 2 46240 443 NetworkService +07:46:58 0x40010002b4a0bb0179ff85e80f02000a 0xb4a0bb0179ff85e80f02000ae7e7e8b7 18446631020132433920 193185397 60727 3305 1 4 2 46240 443 NetworkService +07:46:58 0x40040002b4a0bb0179ff85e80f02000a 0xb4a0bb0179ff85e80f02000ae7e7e8b7 18446631020132433920 193186122 724 3305 4 5 2 46240 443 NetworkService +07:46:58 0x400500020000bb0179ff85e80f02000a 0xbb0179ff85e80f02000ae7e7e8b7 18446631020132433920 193186244 122 3305 5 7 2 46240 443 NetworkService +07:46:59 0x40010002d01ebb01d0c52f5c0f02000a 0xd01ebb01d0c52f5c0f02000a51449c27 18446631020103553856 194110884 0 5130 1 8 2 53278 443 ThreadPoolForeg +07:46:59 0x400800020000bb01d0c52f5c0f02000a 0xbb01d0c52f5c0f02000a51449c27 18446631020103553856 194121000 10116 3305 8 7 2 53278 443 NetworkService +07:46:59 0x400700020000bb01000000000f02000a 0xbb01000000000f02000aeb6f2270 18446631020099513920 194603677 0 3305 7 2 2 0 443 NetworkService +07:46:59 0x40020002d28ebb0182dd92990f02000a 0xd28ebb0182dd92990f02000aeb6f2270 18446631020099513920 194649313 45635 12 2 1 2 53902 443 ksoftirqd/0 +07:47:00 0x400700020000bb01000000000f02000a 0xbb01000000000f02000a26f6e878 18446631020132433920 195193350 0 3305 7 2 2 0 443 NetworkService +07:47:00 0x40020002ba32bb01e0e09e3a0f02000a 0xba32bb01e0e09e3a0f02000a26f6e878 18446631020132433920 195206992 13642 0 2 1 2 47666 443 swapper/0 +07:47:00 0x400700020000bb01000000000f02000a 0xbb01000000000f02000ae7e7e8b7 18446631020132448128 195233125 0 3305 7 2 2 0 443 NetworkService +07:47:00 0x40020002b4a8bb0136cac8dd0f02000a 0xb4a8bb0136cac8dd0f02000ae7e7e8b7 18446631020132448128 195246569 13444 3305 2 1 2 46248 443 NetworkService +07:47:00 0xf02000affff00000000000000000000 0x1aca06cffff00000000000000000000 18446631019225912320 195383897 0 947 7 2 10 0 80 Thread-11 +07:47:00 0x40010002b4a8bb0136cac8dd0f02000a 0xb4a8bb0136cac8dd0f02000ae7e7e8b7 18446631020132448128 195421584 175014 3305 1 4 2 46248 443 NetworkService +07:47:00 0x40040002b4a8bb0136cac8dd0f02000a 0xb4a8bb0136cac8dd0f02000ae7e7e8b7 18446631020132448128 195422361 777 3305 4 5 2 46248 443 NetworkService +07:47:00 0x400500020000bb0136cac8dd0f02000a 0xbb0136cac8dd0f02000ae7e7e8b7 18446631020132448128 195422450 88 3305 5 7 2 46248 443 NetworkService +07:47:01 0x400700020000bb01000000000f02000a 0xbb01000000000f02000aea2afb8e 18446631020099528128 196321556 0 1315 7 2 2 0 443 ChromiumNet +``` + +### 一些可能的报错原因 + +#### [opensnoop](https://github.com/eunomia-bpf/eunomia-bpf/tree/master/examples/bpftools/opensnoop) + +例如 opensnoop 工具,可以在 Android 上成功构建,但运行报错: + +```console +libbpf: failed to determine tracepoint 'syscalls/sys_enter_open' perf event ID: No such file or directory +libbpf: prog 'tracepoint__syscalls__sys_enter_open': failed to create tracepoint 'syscalls/sys_enter_open' perf event: No such file or directory +libbpf: prog 'tracepoint__syscalls__sys_enter_open': failed to auto-attach: -2 +failed to attach skeleton +Error: BpfError("load and attach ebpf program failed") +``` + +后经查看发现内核未开启 `CONFIG_FTRACE_SYSCALLS` 选项,导致无法使用 syscalls 的 tracepoint。 + +## 总结 + +在 Android shell 中查看内核编译选项可以发现 `CONFIG_DEBUG_INFO_BTF` 默认是打开的,在此基础上 eunomia-bpf 项目提供的 example 已有一些能够成功运行的案例,例如可以监测 `exec` 族函数的执行和 tcp 连接的状态。 + +对于无法运行的一些,原因主要是以下两个方面: + +1. 内核编译选项未支持相关 eBPF 功能; +2. eadb 打包的 Linux 环境较弱,缺乏必须依赖; + +目前在 Android 系统中使用 eBPF 工具基本上仍然需要构建完整的 Linux 运行环境,但 Android 内核本身对 eBPF 的支持已较为全面,本次测试证明较高版本的 Android 内核支持 BTF 调试信息和依赖 CO-RE 的 eBPF 程序的运行。 + +Android 系统 eBPF 工具的发展需要官方新特性的加入,目前看来通过 Android APP 直接使用 eBPF 工具需要的工作量较大,同时由于 eBPF 工具需要 root 权限,普通 Android 用户的使用会面临较多困难。 + +## 参考 + +[^Google]: +[^WeiShu]: +[^SeeFlowerX]: diff --git a/src/23-http/README.md b/src/23-http/README.md new file mode 100644 index 0000000..3f37876 --- /dev/null +++ b/src/23-http/README.md @@ -0,0 +1,3 @@ +# http + +TODO diff --git a/src/24-hide/README.md b/src/24-hide/README.md new file mode 100644 index 0000000..1ad1aef --- /dev/null +++ b/src/24-hide/README.md @@ -0,0 +1,3 @@ +# 使用 eBPF 隐藏进程或文件信息 + +TODO diff --git a/src/25-signal/README.md b/src/25-signal/README.md new file mode 100644 index 0000000..b099bcb --- /dev/null +++ b/src/25-signal/README.md @@ -0,0 +1,2 @@ +# signal + diff --git a/src/26-sudo/README.md b/src/26-sudo/README.md new file mode 100644 index 0000000..bf41965 --- /dev/null +++ b/src/26-sudo/README.md @@ -0,0 +1,3 @@ +# sudo + +TODO diff --git a/src/27-replace/README.md b/src/27-replace/README.md new file mode 100644 index 0000000..43424e9 --- /dev/null +++ b/src/27-replace/README.md @@ -0,0 +1,3 @@ +# replace + +TODO