news 2026/8/30 16:56:46

eBPF+IMA LSM:构建一个内核态玩具杀毒软件原型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
eBPF+IMA LSM:构建一个内核态玩具杀毒软件原型

有个朋友在某次安全技术交流结束后跑来问我:你说我用 eBPF 写一个杀毒软件,是不是很酷?我第一反应是,eBPF 配合 IMA LSM 做内核态检测原型,确实可行,但你写出来的东西大概率活不过第一轮性能压测。他很快反问:那为什么现在这么多项目都在讨论 eBPF 做 AV、做 EDR?我意识到,很多人把“能拿到内核事件”和“能做成杀毒软件”画了等号。

我想聊的,就是怎么用 eBPF 配合 IMA LSM,把一个“crappy ring-0 toy antivirus”造出来——顺带说清楚它为什么只能是个 toy。

1. 很多人想用 eBPF 写杀毒,先要搞清楚它到底在杀什么

1.1 “ring-0 玩具杀软”这个说法,其实已经把重点说透了

标题里的 ring-0,在常见的安全语境里指的是 CPU 的最高特权级,也就是内核态。Windows 上很多杀软有内核驱动,Linux 上过去要做一个同样的事,通常也得写内核模块。内核模块出错就是宕机,开发调试门槛很高。eBPF 的出现把一个相对可控的“内核内执行环境”带给了普通开发者,于是很多项目开始尝试在 eBPF 里做安全检测。

但“ring-0”不等于“安全”。你可以把一个程序放进内核,不代表它能挡住所有恶意行为。“crappy”这个词才是项目的真实底色:它能跑,功能有限,边界粗糙,只适合当玩具或者教学原型。明白这一点,再去看 eBPF + IMA 的杀毒方案,才不会产生不切实际的期待。

这个玩具的核心价值,不是真的去对抗一轮 APT 攻击,而是让你理解:一次文件执行,在内核里经过哪些路径;一个安全检测系统,需要哪些模块才能成立。

1.2 IMA、eBPF、LSM 三个概念,放到一件小事里说清

假设用户执行了/tmp/demo.sh

  • IMA(Integrity Measurement Architecture)会先对这个文件做哈希度量,并把度量结果记录到内核的运行时度量列表中。它回答的是“这个文件的内容是什么”。
  • LSM(Linux Security Module)提供了一组内核安全钩子,例如 file_open、bprm_check。内核在执行脚本前会经过这些钩子,安全模块可以在钩子函数里决定是否允许继续。
  • eBPF 是一种内核内虚拟机,可以让开发者安全地挂载到 tracepoint、kprobe,以及 BPF LSM 提供的挂钩点,从而看到事件发生、修改返回值、记录上下文。

三个东西合在一起,就组成了一条完整的检测链路:eBPF 负责“看到文件被执行”,IMA 负责“拿到文件的完整性度量”,LSM 钩子负责“在被执行前插入我们的检查逻辑”。用户态程序再根据哈希和策略决定放行或告警。

1.3 为什么不是写一个用户态扫描器

传统做法是扫描磁盘文件,把文件和病毒库比对。这种模式的问题在于,你无法实时知道一个文件什么时候被创建、被修改、被执行。要实时,就得轮询文件系统,要么扫描全盘,要么使用 inotify/Fanotify。但这些都是用户态机制,和内核态之间始终隔着一层上下文切换,而且你拿不到执行链路中的精确上下文。

eBPF 的优势是实时、低开销、内核视角。一个文件被 execve 进来,eBPF 程序可以在事件发生的路径上立刻感知。IMA 又提供了哈希度量作为数据源,两者结合,就可以实现一个简单的“文件行为监控器”。

但它并不是真正的杀毒软件。真正的杀毒软件还需要病毒特征库、静态启发式分析、模拟执行、宏分析、固件检测、云端信誉系统等。eBPF + IMA 只能给你一个“检测管线”的骨架,离商业安全产品还差很多层。

2. 先搭一条最小可运行链路:文件被打开,哈希被校验,事件被记录

2.1 内核侧要准备什么

我建议先准备一个可随时重启的虚拟机,不要在主机的物理内核上做这种实验。IMA 策略一旦在内核中存在,部分版本里无法在运行时完全清空;eBPF 程序如果写得不合适,也会影响系统行为。

先确认内核配置是否支持要用的能力。常见检查方式:

grep -E 'CONFIG_(IMA|BPF|BPF_SYSCALL|BPF_LSM|SECURITYFS)' /boot/config-$(uname -r)

如果输出里CONFIG_BPF=yCONFIG_BPF_SYSCALL=y,说明 eBPF 基础能力没问题;CONFIG_IMA=y说明 IMA 功能被编译进内核;CONFIG_BPF_LSM=y说明 BPF LSM 支持可用;CONFIG_SECURITYFS=y说明需要挂载 securityfs 来管理 LSM 策略。

在实际发行版里,这几个配置不一定全部开启。特别是CONFIG_BPF_LSM,不少发行版默认没有启用。如果没启用,BPF LSM 路径就无法工作,但 tracepoint 路径仍然可以用。

2.2 用 IMA 测量模式先拿到基线

IMA 有多种模式,常见的有 measure、appraise、audit。measure 模式会计算并记录文件哈希,但不阻止执行;appraise 模式会在哈希校验不通过时拒绝访问。玩具方案里,我建议先用 measure,先看数据,不要一上来就强制拦截。

需要挂载 securityfs:

mount -t securityfs securityfs /sys/kernel/security

写入一条测量策略,让内核在每次执行二进制或脚本时计算哈希:

echo "measure func=BPRM_CHECK" > /sys/kernel/security/ima/policy

注意:这条命令只是一种常见写法,具体策略语法取决于内核版本和发行版配置。如果写入失败,第一步先看dmesg,第二步确认当前用户是否有权限,第三步确认是否已有策略规则存在。在某些内核配置下,一旦写入过策略,后续清理需要重启虚拟机。

写入成功后,可以查看 IMA 的运行时度量记录:

tail /sys/kernel/security/ima/ascii_runtime_measurements

如果能看到类似boot_aggregate或执行文件的哈希记录,说明 IMA 测量链路已经走通。这一步的价值是拿到“文件哈希”的权威来源,后续和 eBPF 采集的事件做关联。

2.3 再用 eBPF 监听执行与打开事件

IMA 负责度量,eBPF 负责事件。最快速的验证方式是先用 bpftrace 看能不能捕获 execve 系统调用:

bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%d %s %s\n", pid, comm, str(args->filename)); }'

这个命令会在每次执行新进程时打印 PID、进程名和被执行的路径。如果这个能跑通,说明 eBPF 事件通道没问题。

如果要更贴近“杀毒”场景,可以挂到 LSM 钩子上,比如security_file_open。这样文件被打开时就能感知。但 LSM hook 的可用性依赖CONFIG_BPF_LSM,并且需要把bpf注册到 LSM 列表中,否则程序无法 attach。很多发行版里,CONFIG_BPF_LSM没有开启,你会遇到类似unknown funcinvalid argument的错误。

从工程经验看,如果只是想先跑通一个玩具原型,不需要执着于 LSM 钩子。tracepoint 已经能覆盖“看到文件被执行”这个需求。LSM 的价值在于“执行前拦截”,这个后面再补。

2.4 用户态决策:哈希、白名单和告警

内核侧的 eBPF 程序把事件抛给用户态,用户态进程负责决策。为什么不能全部放在 eBPF 里做?因为 eBPF 程序有复杂度限制,不适合在 BPF 指令流里维护一个大哈希表、加载特征库、做正则匹配。更合理的分工是:

  • eBPF 只负责采集事件,把 PID、路径、调用链等基本信息发出去。
  • IMA 负责提供文件哈希。
  • 用户态程序维护白名单或规则集,计算哈希后与白名单比对。
  • 如果匹配失败,用户态程序可以记录告警,也可以调用接口终止进程。

这个链路里,真正的“决策”在用户态。虽然牺牲了一点实时性,但对玩具原型来说完全够用,而且更容易调试和更新规则。

3. 把“拿到事件”变成“内核态检测”,中间有多道坎

3.1 决策要不要放在内核态

很多人会把“放在内核态”当成优势本身。实际上,决策在用户态还是内核态,要看你做什么判断。

如果只是对少量文件做哈希比对,用户态完全能胜任,而且写起来简单得多。用户态可以做 SQLite 查询、加载 YARA 规则、访问外部 API,这些都是 eBPF 里很难实现的。

但如果你要追求极低的延迟,或者要在进程 exec 之前就完成拒绝,那就必须依赖内核态动作。IMA 的 appraise 模式可以在内核态直接拒绝哈希不匹配的文件,但它依赖签名和密钥管理体系,这比“用 bpftrace 看一眼事件”复杂得多。

更务实的做法是:先让 eBPF 事件通知用户态,由用户态调用kill或通过系统调用干预。这个过程会有几百微秒到几毫秒的延迟,对玩具场景足够了。真正进入生产级 EDR 时,再考虑把关键决策下放到内核,使用 LSM 钩子返回错误码来阻断。

3.2 LSM 钩子、eBPF 和 IMA 策略可能会打架

Linux 的 LSM 框架里可以同时启用多个安全模块,模块之间存在固定顺序。BPF LSM 在这个顺序里的位置,直接影响你的 eBPF 程序能否看到其他 LSM 已经处理过的结果。IMA 本身也实现了一些安全钩子,如果你的策略和 BPF LSM 策略同时存在,会让问题变得难排查。

最常见的现象是:你写了一个 BPF LSM 程序想拦截某个文件执行,但同一个 hook 上还有 AppArmor 或 SELinux 的策略,二者返回值和处理逻辑叠加,最后文件还是被执行了,或者反而被拒绝了。你在 eBPF 里看到的输入是原始参数,但在它之前可能已经有模块修改了路径、改写了挂载命名空间,或者对文件做了 overlay 映射。

所以原型环境里,建议先关闭不必要的 LSM 模块,或者在启动参数里明确指定 LSM 顺序。这不是为了“绕过”安全机制,而是为了让实验变量足够干净,方便你确认到底是 BPF 程序没触发,还是 LSM 顺序干扰了结果。

3.3 一个比较稳妥的原型判断流程

把整个体系看作一条流水线:

  1. 事件发生:execveopen触发。
  2. eBPF 程序在事件路径上采集路径、PID、进程名、返回结果等上下文。
  3. eBPF 把事件写入 ring buffer 或 perf buffer。
  4. 用户态程序接收事件,拿到真实路径,计算文件哈希。
  5. 用户态程序查询白名单/黑名单。
  6. 如果文件不在白名单中,记录告警,并决定是否终止进程。

每一步都不复杂,但串联起来就是一个可用的安全检测原型。这个流程最容易被忽略的是“真实路径”这一层。用户看到的/tmp/demo.sh可能是一个符号链接,真实文件在/usr/local/packages/demo.sh;也可能是一个 overlayfs 路径,在宿主机和容器里看到的路径不一样。如果只拿用户态路径去做白名单,哈希永远匹配不上。

4. 参数、边界和排错:这个玩具离生产到底差多远

4.1 内核配置、启动参数和挂载点,先花十分钟确认

很多问题不是写错代码,而是环境没准备好。我列了一个检查维度,可以直接当作上手清单:

检查项判断标准常见问题
内核版本至少 5.7 以上,BPF LSM 才可用内核太老,部分能力缺失
CONFIG_BPF / CONFIG_BPF_SYSCALL需要 yeBPF 程序无法加载
CONFIG_BPF_LSM需要 yBPF LSM attach 失败
CONFIG_IMA需要 yIMA 目录不存在
SECURITYFS 挂载/sys/kernel/security有内容策略无法写入
IMA policy 状态能读到规则,或能写入规则策略为只读
当前用户权限需要 root 或 CAP_SYS_ADMIN写入与加载被拒绝

先花十分钟跑一遍,能避免绝大多数“为什么没反应”的疑问。

4.2 最常见的问题是“事件没出来”和“策略没生效”

我先说一个通用排查链路:现象 → 输入 → 环境 → 参数 → 日志。不要一上来就怀疑代码写得不对。

  • 现象:bpftrace 执行后没有任何输出。
  • 输入:确认是不是真的执行了一个二进制文件,还是执行了一个内建 shell 命令。forcdecho这些命令不一定触发 execve。
  • 环境:确认内核配置,确认是否有 root 权限,确认是否在容器里缺少 securityfs 可见性。
  • 参数:确认 attach 点名称是否正确,比如 tracepoint 路径里有没有打错字,LSM hook 是否被内核支持。
  • 日志:运行dmesg -w观察内核日志,很多时候 eBPF verifier 会明确告诉你失败原因。

如果 IMA 策略没有生效,可以这样排查:

  1. 查看/sys/kernel/security/ima/ascii_runtime_measurements是否有内容。
  2. 如果没有,检查 policy 写入是否成功,以及是否选择了measure而不是其他模式。
  3. 再确认是不是所有文件都被过滤了,比如 IMA 策略里可能写了uid=0fsname=ext4等条件。

4.3 五个边界:为什么它只能叫 toy antivirus

第一,特征能力缺失。它没有恶意特征库,不能识别未知恶意软件。白名单模式只能发现“不在列表里的文件”,无法回答“这个文件为什么是恶意的”。

第二,检测面有限。用 execve 和 open 事件做检测,只能看到“文件被打开/执行”这个维度。一个恶意脚本可以完全不做文件落地,直接内存执行,或者借用合法工具进程做间接操作。toy 方案看不到这些。

第三,性能问题。如果对每个执行文件都做完整哈希,在大规模环境中会带来明显延迟。尤其是在打包脚本、编译任务、批量工具调用场景中,哈希计算会成为瓶颈。

第四,策略维护成本高。白名单需要持续更新。开发环境里每天都会生成新二进制,临时脚本、构建产物,都会触发误报。玩具系统可以人工加白名单,生产系统这就变成了一个需要流程化管理的策略平台。

第五,自身防护不足。eBPF 程序可以被预期约束限制,但并不能完全阻止有足够权限的人卸载你的程序。真正的安全产品还需要自我完整性校验、防篡改机制、内核侧审计日志,这些已经远远超出“toy”的范畴。

5. 从玩具到可用原型,我建议你按这个框架拆

5.1 五层拆开:事件采集、数据源、决策引擎、动作、日志

我把这类项目拆成五层,这样既能避免一开始就陷入某段代码,也方便后面替换模块。

分层职责玩具实现生产级方向
事件采集感知内核事件tracepoint / BPF LSM多 hook 组合、事件去重、上下文关联
数据源提供判断依据IMA 哈希、文件路径特征库、信誉查询、威胁情报
决策引擎判断是否放行本地白名单动态策略、ML/规则引擎、多源评分
动作执行处置记日志/杀进程LSM 拦截、隔离、云侧联动
日志保留审计记录输出到文件审计平台、Kafka、SIEM 集成

这个框架的好处是每一层都可以单独迭代。事件采集不完善时,可以用日志层补信息;决策引擎不成熟时,可以先只告警不阻断。每一层之间用明确的接口连接,后面换掉任何一层都不会影响其他部分。

5.2 先跑通、再批量、最后自动化

我的建议是分三个阶段推进。

第一阶段,先跑通最小链路。找几个测试文件,手动执行,确认 eBPF 事件能出来,IMA 哈希能取到,用户态白名单能正确判断。

第二阶段,扩大样本集。放几十个系统命令进去,观察误报率和性能开销。重点看两个东西:一是文件哈希计算的耗时,二是批量执行时事件是否丢。eBPF ring buffer 如果满了,事件会被丢,这在日志里可能表现为“偶尔缺记录”。

第三阶段,再考虑自动化。把白名单放进配置文件或数据库,把告警接到已经存在的监控体系里,比如文件日志、消息队列、工单系统。这时候你已经不是在写一个玩具,而是把玩具身上练出来的经验,落成一个轻量级检测原型的骨架。

5.3 这类项目真正适合谁,不适合谁

它适合三类人:

  • 学 eBPF 和内核安全事件机制,需要一个有画面感的练手项目的人。
  • 安全平台开发工程师,想快速验证“实时检测文件执行”这类需求的可行性。
  • 反病毒或者 EDR 产品的新人,想理解内核态检测中事件、策略、哈希、阻断之间的关系。

它不适合:

  • 想用它保护真实生产环境的人。
  • 想直接替换商业杀软或 EDR 产品的人。
  • 没有时间维护白名单和策略,指望一套规则解决所有问题的人。

把预期放低,反而能做出一个不错的学习项目。如果你一开始就期待它成为一个“全网最强”的杀毒软件,那大概率会在性能、误报、日志风暴里很快放弃。

回到朋友那个问题。用 eBPF 写一个玩具杀毒,值不值得?我觉得很值得。它最大的收获不是得到一套能用的杀毒软件,而是让你把“用户态扫描”这件事放到内核视角重新看一遍。真正可复用的,是你对事件、哈希、策略、拒绝、日志这条链路的理解。

等你要做真正的安全产品,或者想评估 eBPF 是否能承担某类安全检测时,这个玩具会是一个非常清晰的参考系。先跑通一条事件链,再谈对抗强度,这比一开始就追求“什么都检测”要靠谱得多。

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

百度2016研发工程师笔试题解析:C/C++基础与算法考点全拆解

1. 从一道老题看大厂笔试的底层逻辑 每年金三银四、金九银十的求职季,总能看到大量“百度2016研发工程师笔试题”的帖子被翻出来。有人觉得2016年的题太老,没什么参考价值;也有人刷完一遍直呼经典,说很多题目放到现在依然是面试官…

作者头像 李华
网站建设 2026/8/30 16:52:50

8款口碑AI论文平台横向实测,本硕博避坑选型手册

前言:AI 写论文乱象频发,实测 8 款工具理清适配边界 每到毕业季,本科生、硕博生都会集中寻找 AI 论文辅助工具,市面各类写作软件层出不穷。但普遍存在几类硬伤:虚假参考文献、无法匹配本校格式、不支持公式代码生成、A…

作者头像 李华
网站建设 2026/8/30 16:51:14

资深后端开发面经分享

介绍一下我的基本情况: 2020年双非软工毕业,6年的后端开发经验,工作地点深圳,年包40左右,目前在一个跨境电商的营销组,没进过一线的互联网大厂。陆陆续续因为想换工作准备了接近一年的时间(其实…

作者头像 李华
网站建设 2026/8/30 16:46:28

面试官问:多个 AI Agent 之间如何进行通信与协作?

只回答「调接口、传 JSON」,大概率下一句就接不住了。 比如报告 Agent 把取数任务交给数据 Agent。十分钟后连接断了,报告 Agent 该重试,还是继续等?数据 Agent 是没收到、正在处理、等人补充时间范围,还是已经生成了…

作者头像 李华