news 2026/9/12 19:31:04

用eBPF和IMA构建Linux内核级执行阻断:从ring-0到LSM的轻量安全实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用eBPF和IMA构建Linux内核级执行阻断:从ring-0到LSM的轻量安全实践

如果你在 Linux 上做安全产品,最容易被问到的问题是:防护模块是不是必须写成内核驱动,进 ring-0?按照传统思路,答案接近“是”。因为你要拦截执行、检查文件、杀掉恶意进程,没有内核特权好像就做不了真正意义上的“杀毒”。但 ring-0 驱动这条路对普通开发者太不友好:驱动签名、内核版本兼容、loadable module 的限制、调试崩溃,每一项都能消耗你大量时间。

这篇文章给出的路线要轻得多:用 IMA 做文件完整性测量,用 eBPF LSM 在内核执行路径bprm_check_security上挂一个“黑名单检查”。最后做出来的东西,就是标题里说的 "crappy ring-0 toy antivirus"。

它当然不是一个能拿去打攻防演练的杀毒软件,甚至不能扫描病毒。它真正演示的是一套“静态完整性 + 动态执行阻断”的内核安全组装逻辑。读完你会理解三件事:ring-0 工具在系统调用路径上如何生效,IMA 和 LSM 各自解决什么问题,以及怎么把一个 eBPF LSM 程序编译、加载、测试到 Linux 内核里。如果你正在做 HIDS、零信任执行管控、软件供应链防篡改,这篇文章值得看完。

1. eBPF + LSM 做“杀毒”的价值在哪

传统杀毒软件在内核态的常规做法,是写一个驱动程序,hook 系统调用,或者绑定文件系统过滤回调。Windows 上有 minifilter,Linux 上有 fanotify、LSM、内核模块。问题是这些方案要么权限要求高,要么开发调试成本大,要么部署到不同发行版时会被内核模块校验挡住。

eBPF 提供的是另一种思路:你写一段受限的 C 代码,交给内核验证器检查,确认不会死循环、不会越界访问、不会非法修改内核内存后,再把它挂载到指定的 hook 点。用 eBPF 做安全检查,最大的优势不是性能,而是“失败不会拖垮内核”。这不代表没有风险,但相比直接加载模块,eBPF 的失控半径被缩小了很多。

那为什么还要用到 LSM?因为现代 Linux 已经内置了一套安全钩子框架。每次 execve、open、mmap 等关键操作发生时,都会走一遍 LSM 钩子链。过去你只能在这些位置选择 AppArmor、SELinux 这种现成策略,而现在 BPF LSM 允许你自己写一小段程序安插进去。IMA 同样属于这个安全链条的一员,不过它关心的不是“这个操作合不合法”,而是“这个文件是否被改动过、当前内容是什么”。

把三者放在一起,架构就很清晰:

  • IMA 负责“度量”:可执行文件被 execve 时,计算哈希并记录到内核运行日志;
  • eBPF LSM 负责“决策”:在 execve 系统调用到达真正执行点之前,按哈希或路径特征判断是否放行;
  • 用户态脚本负责“策略下发”:计算被拉黑文件的哈希,写入 eBPF map。

这个组合正好覆盖了“检测”和“阻断”两个环节。传统杀毒软件的特征码扫描,本质也是这个逻辑,只是它把特征库维护在厂商服务器上,而我们可以把它收敛到内核里的一个小 map 中。

2. 核心概念:ring-0、LSM、IMA、eBPF LSM

2.1 ring-0 与杀毒软件的“特权执念”

ring-0 是 CPU 的最高特权级别。在内核态,代码可以访问所有内存、I/O 设备,也可以控制系统调用行为。杀毒软件之所以想留在 ring-0,是因为恶意代码往往会藏到系统调用背后,用户态扫描看不到。但 ring-0 也是一把双刃剑:一个 bug 就能让整个系统宕机,驱动签名和版本兼容又提高了发布门槛。

eBPF 程序虽然运行在内核态,但它是由内核验证器约束过的“受限代码”。这不是严格意义上的 ring-0 驱动开发,却又真正跑在 ring-0 的执行路径上。这个特性让很多安全工具开始从“写内核模块”转向“写 eBPF 程序”。

2.2 LSM:Linux 安全模块的标准接口

LSM 不是一个安全产品,而是一套内核安全钩子。它在关键操作发生时调用已经注册的安全回调,回调返回允许或拒绝。举例来说:

  • file_open:文件被打开时;
  • bprm_check_security:程序被 execve 准备执行时;
  • file_permission:文件被读写时。

SELinux、AppArmor 都是通过 LSM 框架生效的。从内核 5.8 开始,eBPF 自身也被注册为一个 LSM 模块,开发者可以在lsm/bprm_check_security这种 hook 位置挂载自己的 eBPF 程序。

2.3 IMA:完整性度量架构

IMA(Integrity Measurement Architecture)是一套文件完整性测量机制。它能在文件被打开、执行、mmap 时计算哈希,并把这些度量结果记录到内核的运行时度量列表中。IMA 支持三种策略模式:

模式作用是否阻断
measure记录文件哈希
appraise校验文件签名或哈希
audit记录安全审计日志

在本文演示中,我们主要用 measure 模式来验证“文件被篡改后哈希会变化”。实际生产系统可以配合 IMA 签名,在 appraise 模式下拒绝执行未签名文件。

2.4 eBPF LSM 的架构位置

BPF LSM 本质上是把 LSM 钩子开放给了 eBPF 程序。它的调用链路是:

execve() -> do_execve() -> bprm_check_security() // LSM hook -> IMA 的测量逻辑(如果策略包含 BPRM_CHECK) -> eBPF LSM 程序(决策是否放行) -> 放行 / 拒绝

这里必须区分清楚:IMA 和 eBPF LSM 处在同一条 LSM 链上,但职责不一样。IMA 做测量、记录、校验;eBPF LSM 做自定义的强制访问控制。我们让 IMA 负责“发现异常痕迹”,让 eBPF LSM 负责“执行黑名单阻断”,这正是标题所说“toy antivirus”的核心架构。

3. 整体架构设计

这个项目的目标很明确:构建一个能够在内核执行路径上,根据文件哈希或路径,拒绝特定可执行文件运行的最小防护系统。

模块划分如下:

  • 策略管理员(用户态):决定哪些文件需要被拉黑,计算文件路径的哈希,并通过 bpftool 写入 eBPF map。
  • IMA 测量层:对BPRM_CHECK事件记录文件哈希,作为审计和举证数据。
  • eBPF LSM 阻断层:每次bprm_check_security触发时,读取正在执行的程序路径,计算哈希,查询黑名单 map,如果命中则返回-EPERM
  • 内核安全执行链:把以上两步串联起来。

实际效果等价于:用户态无法绕过的“文件访问黑名单”。因为检查点位于内核 LSM 路径,即使攻击者拿到 shell,只要 execve 被拦,他也很难执行指定文件。当然,如果攻击者已经有 root 权限并能加载 eBPF 程序,他可以直接卸载或覆盖我们的 eBPF map,这是后话。toy 级设计无法对抗同权限级对手。

4. 环境准备与内核特性检查

4.1 推荐环境

强烈建议在虚拟机中做实验,不要在生产环境直接操作。原因有两个:实验过程中需要修改内核启动参数,一旦配置错误可能导致系统无法启动;IMA policy 写错也可能影响系统命令执行。

推荐环境:

  • Ubuntu 22.04 或更新的 Linux 发行版;
  • 内核版本 5.15 以上,BPF LSM 已经足够稳定;
  • 安装 clang、llvm、libbpf-dev、bpftool;
  • 内核开启 CONFIG_BPF_LSM、CONFIG_IMA 相关选项。

多数发行版默认内核已经带有相关配置,但需要启动参数显式启用 BPF LSM。

4.2 检查 LSM 启用状态

执行以下命令:

cat /sys/kernel/security/lsm

输出中如果包含bpf,说明 BPF LSM 已经启用。如果没有,你需要修改 GRUB 启动参数,追加:

lsm=lockdown,yama,integrity,apparmor,bpf

修改前先备份/etc/default/grub,然后运行:

sudo update-grub sudo reboot

如果你使用的是云主机,尤其要注意:修改 GRUB 参数前确认有 VNC 或带外控制台,避免参数错误导致无法登录系统。

4.3 检查 IMA 是否可用

ls -l /sys/kernel/security/ima cat /sys/kernel/security/ima/ascii_runtime_measurements | tail -n 5

如果/sys/kernel/security/ima不存在,需要先挂载 securityfs:

sudo mkdir -p /sys/kernel/security sudo mount -t securityfs securityfs /sys/kernel/security

如果安全文件系统已经挂载,但 IMA 目录仍然不存在,说明当前内核没有启用 IMA,需要重新编译内核,或者换一个默认开启 IMA 的发行版内核。不建议为了一个 toy 项目重新编译内核,除非你本身就有内核开发环境。

4.4 检查 eBPF 所需权限

加载 eBPF LSM 程序需要 root 权限,或者当前用户具有CAP_BPFCAP_SYS_ADMIN能力。普通用户直接执行加载命令会失败:

Operation not permitted

认证资料显示,Linux 的新权限体系中 BPF 能力被拆分成了CAP_BPFCAP_PERFMON,但为了减少环境变量干扰,本文所有验证都在 root 用户下完成。

5. IMA 完整性度量配置与验证

5.1 写入 measure 策略

IMA 的运行时策略在 securityfs 的/sys/kernel/security/ima/policy文件中写入。我们只记录可执行文件的度量结果:

echo "measure func=BPRM_CHECK" | sudo tee /sys/kernel/security/ima/policy

除了BPRM_CHECK,IMA 还支持FILE_CHECK(文件打开)、MMAP_CHECK(内存映射执行)等事件类型。为了演示,只启用最小策略。

5.2 触发一次度量

执行一个普通命令,让 IMA 产生度量记录:

/usr/bin/md5sum /etc/hostname > /dev/null sudo tail -n 5 /sys/kernel/security/ima/ascii_runtime_measurements

输出中会出现类似下面的字段:

10 <template-hash> ima-ng sha256:<文件哈希> /usr/bin/md5sum

这里我使用了<template-hash><文件哈希>作为占位符,因为不同发行版、不同文件内容得到的哈希值完全不一样。你只需要确认:ascii_runtime_measurements末尾增加了新的记录,并且里面包含/usr/bin/md5sum路径。

5.3 验证文件修改会导致哈希变化

如果你尝试手动修改一个文件,并在修改前后分别执行,会看到 IMA 记录中的哈希值不同。这说明 IMA 已经完成了“静态完整性度量”的职责。

注意:当前 measure 模式只记录,不阻断。即使文件被篡改,系统也能执行它。真正阻断工作交给下一节的 eBPF LSM 程序。

6. eBPF LSM 执行阻断实现

6.1 程序逻辑设计

eBPF LSM 程序要做的事情比较朴素:

  1. bprm_check_security钩子处读取当前被执行的程序路径;
  2. 对路径计算一个简单的 djb2 哈希;
  3. 查询名为blocked_files的 eBPF map;
  4. 如果路径哈希存在于 map 中,返回-EPERM
  5. 否则放行。

这里用 djb2 哈希纯粹是为了演示,真实项目请改用更强健的哈希或直接使用路径长度固定前缀匹配。djb2 存在碰撞可能,而且这里计算的是路径哈希而不是文件内容哈希。这一点在最后的安全边界中会详细解释。

6.2 完整 eBPF C 程序

创建文件ima_guard.bpf.c

// 文件路径:ima_guard.bpf.c #include <linux/bpf.h> #include <linux/types.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> #define MAX_PATH 256 #define MAX_ENTRIES 1024 char LICENSE[] SEC("license") = "GPL"; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, MAX_ENTRIES); __type(key, __u32); __type(value, __u32); } blocked_files SEC(".maps"); static __always_inline __u32 djb2_path(const char *path) { __u32 hash = 5381; int i; for (i = 0; i < MAX_PATH; i++) { int c = path[i]; if (c == 0) { break; } hash = ((hash << 5) + hash) + c; } return hash; } SEC("lsm/bprm_check_security") int BPF_PROG(guard_bprm_check, struct linux_binprm *bprm) { char filename[MAX_PATH] = {}; __u32 key; long ret; ret = bpf_probe_read_kernel_str(filename, sizeof(filename), BPF_CORE_READ(bprm, filename)); if (ret <= 0) { return 0; } key = djb2_path(filename); if (bpf_map_lookup_elem(&blocked_files, &key)) { return -EPERM; } return 0; }

这段代码的关键点有三个。

第一,BPF_CORE_READ(bprm, filename)struct linux_binprm中读取filename指针,然后通过bpf_probe_read_kernel_str安全地把内核态字符串拷贝到栈上。栈上的filename属于 eBPF 程序自身栈空间,verifier 可以确认访问边界。

第二,djb2_path遍历栈上字符串,并且循环次数被限制在MAX_PATH以内。这样 verifier 可以验证循环不会死循环,程序能够通过加载检查。

第三,SEC("lsm/bprm_check_security")表明这段程序要挂载到bprm_check_security这个 LSM 钩子。返回0表示允许,返回负错误码表示拒绝,这里是-EPERM,所以用户侧看到的是 “Permission denied”。

6.3 编译命令

使用 clang 编译 eBPF 目标文件:

clang -O2 -g -Wall -target bpf -D__TARGET_ARCH_x86 -c ima_guard.bpf.c -o ima_guard.bpf.o

如果你的 CPU 是 ARM64,把__TARGET_ARCH_x86换成__TARGET_ARCH_arm64,并且注意交叉编译环境。编译成功后会生成ima_guard.bpf.o

6.4 加载并挂载 eBPF 程序

使用 bpftool 加载:

sudo bpftool prog load ima_guard.bpf.o /sys/fs/bpf/ima_guard autoattach

如果你的 bpftool 版本较旧,不支持autoattach或 LSM attach,也可以尝试手动挂载:

sudo bpftool prog load ima_guard.bpf.o /sys/fs/bpf/ima_guard sudo bpftool prog attach name guard_bprm_check type lsm bprm_check_security

加载成功后,可以查看程序信息:

sudo bpftool prog show name guard_bprm_check

如果这里报错,常见原因是内核配置或启动参数没有启用 BPF LSM。检查CONFIG_BPF_LSM=y,并且/sys/kernel/security/lsm中包含bpf

6.5 查看 eBPF map ID

map 被程序引用,但加载后你需要知道它的 ID,或者直接用名字操作:

sudo bpftool map show name blocked_files

输出中会显示 map 的 id、类型、key 大小、value 大小和当前元素数量。接下来向 map 写入黑名单。

7. 联动测试:让一个文件“被杀”

7.1 先创建一个测试脚本

mkdir -p /tmp/av_demo cat > /tmp/av_demo/hello.sh <<'EOF' #!/bin/bash echo "I am running" EOF chmod +x /tmp/av_demo/hello.sh

先直接执行它,确认能够正常运行:

/tmp/av_demo/hello.sh

预期输出:

I am running

7.2 计算路径哈希

这里必须注意:eBPF 程序读取的是传入 execve 的路径。为了让流程稳定,请使用绝对路径执行,并且计算绝对路径的哈希。

python3 - <<'EOF' import struct def djb2(s): h = 5381 for c in s.encode(): h = ((h << 5) + h + c) & 0xFFFFFFFF return h path = "/tmp/av_demo/hello.sh" key = djb2(path) print("key bytes:", " ".join(f"{b:02x}" for b in struct.pack("<I", key))) EOF

脚本会输出类似:

key bytes: 27 3a 9c a2

这个字节序列是小端序。bpftool 写入 map 时,必须使用相同字节序,否则 eBPF 程序计算出的 key 与 map 中保存的 key 不匹配,查找不到。

7.3 写入 eBPF map

假设上面输出的是27 3a 9c a2,执行:

sudo bpftool map update name blocked_files key hex 27 3a 9c a2 value hex 00 00 00 01

value 任意写一个非零值即可,因为 eBPF 程序只检查这个 key 是否存在。写入后查看:

sudo bpftool map dump name blocked_files

预期能看到一个 key-value 条目。

7.4 再次执行目标文件

/tmp/av_demo/hello.sh

预期输出:

bash: /tmp/av_demo/hello.sh: Permission denied

为什么不是 “No such file” 或 “Syntax error”?因为错误发生在内核 execve 路径上,shell 还没能把脚本内容读进内存,进程就被拒绝了。这正是 LSM hook 生效的典型表现。

7.5 验证 IMA 记录

执行被拒绝后,再查看 IMA 度量记录:

sudo tail -n 5 /sys/kernel/security/ima/ascii_runtime_measurements

这里可能有两种情况。如果 IMA 策略在 eBPF LSM 之前执行,日志里会多一条/tmp/av_demo/hello.sh的度量记录;如果 eBPF LSM 先返回拒绝,IMA 日志可能没有该文件记录。不同内核版本的 LSM 钩子执行顺序不完全一致,这并不影响方案有效性,因为我们的阻断决策由 eBPF LSM 负责。

7.6 删除黑名单,恢复执行

为了确认是 eBPF map 导致的拒绝,而不是文件权限问题,删除 map 元素后再次执行:

sudo bpftool map delete name blocked_files key hex 27 3a 9c a2 /tmp/av_demo/hello.sh

此时应该恢复输出:

I am running

到这里,一个完整的“静态哈希拉黑 + 内核执行阻断”链路已经跑通。

8. 常见问题与排查思路

问题现象可能原因排查方式解决方案
bpftool 报 “Operation not permitted”当前用户缺少 CAP_BPF 或 CAP_SYS_ADMINsudo执行;检查内核能力和用户组使用 root 用户;在测试环境可临时赋予相应 capability
bpftool prog load 报 “unknown attach type”内核版本过低或 bpftool 版本过旧查看uname -rbpftool version升级内核和 bpftool,至少保证内核 5.8+,推荐 5.15+
eBPF 程序加载后,执行任何程序都被拒绝LSM hook 挂载异常,或 verifier 误判;更常见的是黑名单 map 中写入了系统程序路径的哈希先用bpftool map dump name blocked_files查看 map 内容;再检查程序逻辑删除错误 map 项;测试时先只拉黑一个明确的测试文件
/sys/kernel/security/lsm中没有bpf内核启动参数未启用 BPF LSMcat /proc/cmdline查看当前启动参数追加lsm=lockdown,yama,integrity,apparmor,bpf后重启
IMA 策略写不进/sys/kernel/security/ima/policy内核没有 CONFIG_IMA_WRITE_POLICY,或已存在默认不可写策略查看内核 config 和安全 fs 挂载情况使用支持动态写策略的内核;或在编译内核时开启 CONFIG_IMA_WRITE_POLICY
路径哈希不匹配,map 明明有值但拦截不生效字节序或路径不一致,比如相对路径和绝对路径都传入过 execvebpftool map dump查看实际 key;用 Python 分别计算相对/绝对路径的哈希统一使用绝对路径执行;写入 map 时按照小端字节序填入
修改 GRUB 参数后系统启动失败LSM 列表写错或顺序不对通过带外控制台查看启动日志恢复 GRUB 备份参数;在虚拟机中先验证配置

所有这些排查动作,都应该在可控的测试环境完成。涉及内核启动参数和 IMA 策略时,记住一个原则:先备份,再修改。

9. 生产级方案演进与安全边界

9.1 为什么这里只是“toy”

本文实现的方案有几个明显短板:

第一,它拦截的是“路径哈希”,不是“文件内容哈希”。攻击者只要把黑名单文件复制成另一个路径,拦截就会失效。在真实安全产品中,你需要用 IMA 计算出的文件摘要作为 key,或者直接把哈希结果与签名校验绑定。

第二,它拦不住“无文件执行”。如果恶意代码直接把 shellcode 注入到已存在的合法进程中,不走 execve 路径,这个 eBPF 程序根本看不到。

第三,它没有做持久化。一旦重启,eBPF 程序和 map 内容都会丢失。

第四,它依赖 eBPF map 的完整性。如果攻击者已经拿到 root 权限并能加载 eBPF 程序,他可以清空 map 或卸载我们的程序。对抗同权限级对手,需要更深层的信任链保障。

9.2 真实项目应该怎么演进

如果要在生产环境做类似的防篡改执行管控,更推荐的组合是:

  1. IMA 开启 appraise 模式,配合 IMA 签名机制,只允许执行被信任签名的文件;
  2. 将 IMA 度量基准哈希存储在 TPM PCR 中,实现远程证明;
  3. eBPF LSM 程序不是维护静态黑名单,而是查询一个集中的策略下发结果,配合用户态 agent 做实时更新;
  4. 对白名单之外的可执行文件,先记录审计日志,灰度观察一段时间后再切换为阻断模式;
  5. 对关键文件系统目录启用 dm-verity 或 overlayfs 只读保护,从文件系统层降低被篡改的风险。

eBPF LSM 最适合的场景,是那些“你希望按自定义规则快速拦截特定执行事件”的专项需求。它做不到万能的杀毒,但可以让你的防护策略成为内核安全检查链路中的一环。对于刚接触 eBPF 的开发者,用这个例子理解 LSM hook 的挂

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 11:59:09

欢聚时代PHP校招笔试全解析:基础内功与实战避坑

这套“欢聚时代2017校招笔试题目&#xff08;PHP工程师类&#xff09;A卷”&#xff0c;我前前后后看了不下五遍。当时是帮一个学弟做考前突击&#xff0c;他把卷子拍照发我&#xff0c;我一边看一边感慨&#xff0c;这份卷子的出题人绝对是懂PHP的&#xff0c;没有一味追求偏题…

作者头像 李华
网站建设 2026/9/6 17:33:53

第295篇 PointNet系列——3D点云处理的深度学习之路

6D位姿估计聊完了&#xff0c;这篇讲3D点云处理。机器人用激光雷达和深度相机获取的3D数据就是点云——一堆三维坐标点。怎么让神经网络理解这些无序的、不规则的点集&#xff1f;PointNet给出了答案。 PointNet是2017年Charles Qi等人提出的&#xff0c;第一次用深度学习直接…

作者头像 李华
网站建设 2026/9/6 14:08:34

TVA-World架构:具身智能全栈算法研究新突破(1)

前沿技术探索&#xff1a;TVA智能体&#xff08;简称TVA&#xff09;TVA智能体&#xff08;亦称“AI智能体视觉”或“TVA视觉智能体”&#xff09;是依托Transformer架构与“因式智能体”理论构建的通用视觉技术体系。它有机融合深度强化学习&#xff08;DRL&#xff09;、卷积…

作者头像 李华
网站建设 2026/9/7 0:06:29

Nova-Quantum:裸机LLM内核无操作系统启动与推理解析

Nova-Quantum 是一个很有意思的项目&#xff1a;一个约 41 MB 的 ISO 镜像&#xff0c;启动后不进入 Linux、不加载 Windows&#xff0c;而是直接进入一个自定义内核&#xff0c;由这个内核自己完成大语言模型的加载和推理。项目副标题里的 "Bootet Ohne OS" 是德语&…

作者头像 李华
网站建设 2026/9/7 0:06:25

网络化监控DVR从入门到实战:远程访问、端口映射与存储配置全攻略

简介&#xff1a;这是一套专为IE浏览器设计的网络视频监控系统DVR插件资源包&#xff0c;面向安防集成工程师、嵌入式Web前端开发者及视频监控平台二次开发人员&#xff0c;解决IE环境下通过网页远程访问海康等主流DVR设备的兼容性与功能调用问题。压缩包共61个文件&#xff0c…

作者头像 李华
网站建设 2026/9/6 19:56:49

阿里巴巴开源Java八股文终极版:系统梳理面试知识地图

凌晨两点多&#xff0c;技术群里忽然有人甩了一条链接&#xff0c;标题写着“阿里巴巴官方上线&#xff01;号称国内Java八股文天花板&#xff08;终极版&#xff09;首次开源”。原本安静得只能听到风扇转的群&#xff0c;瞬间热闹了起来。有人第一反应是标题党&#xff1a;“…

作者头像 李华