2023-04-13 17:18:12 +08:00

132 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第十章 系统集成开发eBPF
安卓系统的安全攻防技术日新月异现如今进入了一个全新的高度。随着eBPF技术的崛起国内外安全人员人业也在积极发掘eBPF技术在安全领域的应用场景。
本章将为安卓系统开发与定制人员提供一种代码修改与eBPF可观测性相结合的系统定制细路旨在为安全行业的发展发挥一点抛砖引玉的作用。
## 10.1 eBPF概述
eBPF(extended Berkeley Packet Filter) 是一种现代化的Linux内核技术它允许开发人员对网络数据包进行更细粒度的过滤和修改。与传统的Berkeley Packet Filter(BPF)相比eBPF具有更灵活、可扩展性和安全性的优势因此得到了广泛的应用和认可。在实际应用中eBPF可以实现网络流量监控、日志记录、流量优化和安全审计等功能因此具有广泛的应用前景。
### 10.1.1 eBPF发展背景
在2008年Linux内核开发者提出了BPF(Berkeley Packet Filter)的概念它是一种用于过滤和修改网络数据包的内核模块。BPF是一种非常强大的工具它允许开发人员对网络数据包进行细粒度的过滤和修改从而实现网络流量监控、日志记录、性能优化等功能。
然而BPF也有一些限制。首先BPF模块需要由内核开发人员手动编写和编译因此对于非专业开发人员来说编写BPF模块是一项具有挑战性的任务。其次BPF模块的编写需要一定的技术知识和经验否则可能会导致内核崩溃或其他问题。最后BPF模块的访问权限非常高意味着它们可以访问系统的所有内存和网络资源因此需要严格的安全控制。
为了解决这些问题Linux内核开发者在2014年引入了eBPF(extended Berkeley Packet Filter) 技术。eBPF是一种扩展的BPF它提供了更多的功能和权限控制同时降低了内核开发人员的编写难度和风险。与传统的BPF相比eBPF具有更灵活、可扩展性和安全性的优势因此得到了广泛的应用和认可。
eBPF发展之初是为了用于高效的网络数据包的过滤。在发展过程中除了对传统的数据包过滤字节码格式进行扩展外还支持更多类型的eBPF程序它们可以在整个操作系统的不同模块中运行。最初提倡的可观测性领域也扩展为支持数据的观测与修改包括用户态数据与内核函数返回值。这样的发展路径让eBPF技术看起来更像是一个现代化的Hook技术框架。这也是安全从业人员其对爱不释手的原因。
相信随着内核版本的更新其内置的eBPF功能支持也会越来越丰富。而作为eBPF的能力核心-eBPF内核方法接口也会越来越多基于这些接口实现的安全功能势必会影响到整个行业的发展。
### 10.1.2 eBPF的工作原理
eBPF的工作原理可以概括为三个步骤解析、执行和卸载。
1. 解析
在系统启动时内核会将eBPF符号表(eBPF symbol table) 加载到内存中。eBPF符号表是一个二进制文件它包含了eBPF模块的所有符号和参数。内核还会将eBPF模块的二进制代码转换为机器码并将其加载到内存中。
2. 执行
当网络数据包到达时内核会首先检查数据包是否被匹配到eBPF模块。如果数据包被匹配到内核会执行eBPF模块中的代码对网络数据包进行过滤或修改。在执行期间内核会使用eBPF的运行时数据结构体(runtime data structure)来存储和传递参数和上下文信息。
3. 卸载
当eBPF模块执行完毕后内核会将其卸载并从内存中清除。卸载时内核会将所有符号和参数还原成二进制码并将其从内存中清除。
### 10.1.3 eBPF的应用场景
eBPF是一种非常强大的技术它可以实现许多网络流量监控、日志记录、性能优化等功能。下面列举了一些常见的eBPF应用场景:
1. 日志记录
eBPF可以用于记录网络流量、系统调用、错误事件等信息从而实现全面的系统监控和日志记录。目前这方面技术应用于云原生安全较多。如`sysdig``falco`这类安全监控工具新版本就使用了eBPF来实现系统调用的监控。
2. 流量控制
eBPF 可以用于实现网络流量控制,例如限制同一主机的网络流量、限制同一端口的网络流量等。这个应用最多的就是防火墙,比如大名鼎鼎的`iptables`就有了基于eBPF的扩展版本。
3. 流量优化
eBPF可以用于优化网络流量例如过滤重复数据包、压缩数据包、优化TCP/IP协议栈等。在网络应用上典型的是可以使用eBPF开发透明代理工具、网络数据镜像转发工具、流量优化工具等。
4. 安全审计
eBPF可以用于实现安全审计功能例如记录系统用户的操作、检查系统资源使用情况等。在这个应用领域如主机安全类防护产品`HIDS`就有了发展的空间。安全工具`Tracee`就是属于这类应用。
## 10.2 eBPF相关的开发工具
eBPF是一种现代化的Linux内核技术它允许开发者在内核中安全地运行外部程序用于处理网络数据包、系统调用等场景。eBPF相比传统的内核模块有更高的安全性和可移植性因此得到了越来越广泛的应用。eBPF虽然运行在内核但是控制它的程序却是运行在用户态下面将介绍一下它的开发方法。
在开发eBPF相关工具时常用的有bcc、bpftrace和libbpf。下面将对这三个工具/库进行介绍。
### 10.2.1 bcc
`bcc`是一款开源的eBPF快速开发工具。最初使用python作为eBPF程序的开发语言随着社区的发展该工具支持了C语言开发eBPF程序。该项目是一个开源工具它的仓库地址是https://github.com/iovisor/bcc。该仓库提供了一组python语言编写的eBPF工具集位于tools目录下涉及的功能包含了文件、进程、网络、延时、性能观测等多个应用场景的工具;同时也提供了一组C语言编写的eBPF工具集位于libbpf-tools工具下这下面的工具很多是tools的C语言实现版本是非常好的eBPF入门学习资料。
该工具的项目README中有列出eBPF可以运行的不同系统位置的分布图。也提供了tools目录下工具用途的介绍。比如监控文件的打开操作可以执行如下命令
```
$ sudo python3 tools/opensnoop
```
### 10.2.2 bpftrace
`bpftrace`的主要用途是用于记录和追踪系统方法调用。eBPF程序可以用于处理网络数据包、系统调用、文件访问等场景。使用`bpftrace`开发者可以可以快速验证要观测的函数是否支持eBPF来实现。
`bpftrace`是开源的工具它的仓库地址是https://github.com/iovisor/bpftrace。按照官方的说明安装好该工具后会提供一个`bpftrace`工具。这个主程序接受单选的命令与一个bt格式的脚本程序作为输入。脚本中可以设置观测程序的入口和出口、参数传递等信息非常方便。需要注意的是目前`bpftrace`只提供了观测功能,没有提供数据的修改功能。这点上不如`bcc``libbpf`
执行下面的命令,可以观测所有的文件打开操作:
```
$ sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
```
### 10.2.3 libbpf
libbpf是一个用于编写和运行eBPF程序的开源库。它的仓库地址是https://github.com/libbpf/libbpf。 它提供了一组C接口的函数允许开发者使用C/Rust语言编写和运行eBPF程序。libbpf官方还单独提供了一些使用libbpf开发eBPF程序的样例。仓库地址是https://github.com/libbpf/libbpf-bootstrap。该仓库下的examples/c目录下的演示代码整体的风格与bcc的libbpf-tools目录下类似前者代码简洁后者功能更丰富。
总的来说,`bcc``bpftrace``libbpf`都是用于开发eBPF相关工具的重要工具它们提供了丰富的功能和工具方便开发者进行eBPF程序的开发、调试和追踪。
## 10.3 安卓系统集成eBPF功能
eBPF的功能实现与完善是优先对x86_64架构提供支持。arm64与其它的系统架构则会在后面补充跟上。对于大部分的安卓手机设备来说系统主流采用的是arm64架构的处理器因此它的各方面功能支持会延后支持其支持的程度与arm64版本的Linux其他发行版本对齐比如arm64架构同版本内核的Ubuntu系统与安卓的eBPF功能支持基本是一致的。
### 10.3.1 不同版本内核对eBPF的影响
安卓系统的版本更新通常也伴随着系统内核版本的升级。目前最新的安卓14采用6.1版本的内核。它的默认的内核配置支持与在同版本内核的arm64 Ubuntu系统eBPF功能一致。支持常用的`kprobes``uprobes``tracepoint``raw_tracepoint`。但一些arm64到高版本内核仍然不支持的特性比如`fentry``fmod_ret``kfuncs``LSM``SYSCALL``tp_btf`还需要等待主线内核提供更新支持。列出的不支持的部分从内核如下地址https://github.com/torvalds/linux/commit/efc9909fdce00a827a37609628223cd45bf95d0b可以看到已经有了更新的支持但Ubuntu arm64架构的6.1内核上仍然测试失败相信不久这些功能都可以在arm64上运行良好。
安卓12内核采用5.10安卓13采用5.10与5.15。这两个版本的Linux内核支持上面说的`uprobes``tracepoint``raw_tracepoint`。但它们对`kprobes`的支持有一些欠缺只能说部分支持。造成这个的原因是安卓GKI2.0的一些变化,让`CONFIG_DYNAMIC_FTRACE`这样的选项无法成功开启,具体会在下面一些需要注意的内核配置的小节进行说明。
### 10.3.2 一些需要注意的内核配置
与安卓eBPF相关的内核配置有如下
1. `CONFIG_DYNAMIC_FTRACE`。如果内核配置了`CONFIG_DYNAMIC_FTRACE`, Ftrace框架内部的`mcount`会被实现成一个空函数(只有一条`ret`指令)。在系统启动时,`mcount`会被替换成`nop`指令。打开tracer后所有函数的对应位置会被动态替换成跳转到`ftrace_caller()`的指令。这个选项是`fentry`的内核配置`CONFIG_FPROBE`的依赖会导致fentry无法生效。
2. `CONFIG_FUNCTION_TRACER`。内核中打开`CONFIG_FUNCTION_TRACER`后,会增加`pg`编译选项,这样在每个函数入口处都会插入`bl mcount`跳转指令,函数运行时会进入`mcount`函数。`mcount`会判断函数指针`ftrace_trace_function`是否被注册,默认注册的是空函数`ftrace_stub`这是ftrace静态方法跟踪的内核配置选项。这个选项也是`fentry`的内核配置`CONFIG_FPROBE`的依赖,会导致`fentry`无法生效。这个选项也会有一个`available_filter_functions`文件供用户配置Ftrace如果没有开启会因为缺少了它`bpftrace`在kprobes功能函数列表时就会失败。
3. `CONFIG_FTRACE_SYSCALLS`。这是一个在几乎所有Ubuntu发行版本中都开启的内核配置但是在安卓中却中默认关闭的。并且安卓官方的Pixel6以上设备开启后配置Kprobe相关的选项开启会让设备并得很卡。这个内核配置会在tracefs的events目录下加入一个syscalls目录支持对所有的系统调用进行单独的跟踪观测是一个很有用的内核配置。
关于其它内核配置对eBPF的影响可以查看bcc提供的一个内核配置说明文档。地址是https://github.com/iovisor/bcc/blob/master/docs/kernel_config.md。
### 10.3.3 为低版本系统打上eBPF补丁
## 10.4 测试eBPF功能
### 10.4.1 运行bcc工具
### 10.4.2 运行bpftrace工具
### 10.4.3 如何编译基于libbpf的eBPF程序
## 10.5 eBPF实现安卓系统进程跟踪
在这里不得不提一个两个功能强大的方法:`bpf_probe_read_user``bpf_probe_write_user`这两个接口允许eBPF读取与写入内存地址指定的数据它们拥有内核一样的能力却有着比内核高得多的稳定性功能不可谓不强大。
## 10.6 小结