fix replace code

This commit is contained in:
yunwei37
2023-05-31 01:03:16 +08:00
committed by 云微
parent fe3dfa9aca
commit 48fae08f08
11 changed files with 152 additions and 133 deletions

View File

@@ -1,6 +1,22 @@
# 后运行 eBPF 程序
# 在用户态应用退出后运行 eBPF 程序eBPF 程序的生命周期
通过使用 `--detach` 运行程序,用户空间加载器可以退出,而不会停止 eBPF 程序。
通过使用 detach 的方式运行 eBPF 程序,用户空间加载器可以退出,而不会停止 eBPF 程序。
## eBPF 程序的生命周期
首先,我们需要了解一些关键的概念,如 BPF 对象(包括程序,地图和调试信息),文件描述符 (FD)引用计数refcnt等。在 eBPF 系统中,用户空间通过文件描述符访问 BPF 对象而每个对象都有一个引用计数。当一个对象被创建时其引用计数初始为1。如果该对象不再被使用即没有其他程序或文件描述符引用它它的引用计数将降至0并在 RCU 宽限期后被内存清理。
接下来,我们需要了解 eBPF 程序的生命周期。首先,当你创建一个 BPF 程序,并将它连接到某个“钩子”(例如网络接口,系统调用等),它的引用计数会增加。然后,即使原始创建和加载该程序的用户空间进程退出,只要 BPF 程序的引用计数大于 0它就会保持活动状态。然而这个过程中有一个重要的点是不是所有的钩子都是相等的。有些钩子是全局的比如 XDP、tc's clsact 和 cgroup-based 钩子。这些全局钩子会一直保持 BPF 程序的活动状态,直到这些对象自身消失。而有些钩子是局部的,只在拥有它们的进程存活期间运行。
对于 BPF 对象程序或映射的生命周期管理另一个关键的操作是“分离”detach。这个操作会阻止已附加程序的任何未来执行。然后对于需要替换 BPF 程序的情况你可以使用替换replace操作。这是一个复杂的过程因为你需要确保在替换过程中不会丢失正在处理的事件而且新旧程序可能在不同的 CPU 上同时运行。
最后,除了通过文件描述符和引用计数来管理 BPF 对象的生命周期,还有一个叫做 BPFFS 的方法也就是“BPF 文件系统”。用户空间进程可以在 BPFFS 中“固定”pin一个 BPF 程序或映射,这将增加对象的引用计数,使得即使 BPF 程序未附加到任何地方或 BPF 映射未被任何程序使用,该 BPF 对象也将保持活动状态。
所以,当我们谈论在后台运行 eBPF 程序时,我们需要清楚这个过程的含义。在某些情况下,即使用户空间进程已经退出,我们可能还希望 BPF 程序保持运行。这就需要我们正确地管理 BPF 对象的生命周期
## 运行
这里还是采用了上一个的字符串替换的应用,来体现对应可能的安全风险。通过使用 `--detach` 运行程序,用户空间加载器可以退出,而不会停止 eBPF 程序。
编译:
@@ -38,4 +54,5 @@ sudo rm -r /sys/fs/bpf/textreplace
## 参考资料
- <https://github.com/pathtofile/bad-bpf>
- <https://github.com/pathtofile/bad-bpf>
- <https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html>

View File

@@ -201,8 +201,8 @@ int BPF_PROG(find_possible_addrs, struct pt_regs *regs, long ret)
long int read_size = buff_size;
bpf_printk("[TEXT_REPLACE] PID %d | read_size %lu | buff_addr 0x%lx\n", pid, read_size, buff_addr);
const unsigned int local_buff_size = 64;
const unsigned int loop_size = 64;
const unsigned int local_buff_size = 32;
const unsigned int loop_size = 32;
char local_buff[local_buff_size] = { 0x00 };
if (read_size > (local_buff_size+1)) {
@@ -295,6 +295,8 @@ int BPF_PROG(check_possible_addresses, struct pt_regs *regs, long ret)
break;
}
}
// for newer kernels, maybe use bpf_strncmp
// if (bpf_strncmp(pFind->text, TEXT_LEN_MAX, name) == 0) {
if (j >= name_len) {
// ***********
// We've found out text!