首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用Rust+eBPF监控文件读写防范勒索病毒

用Rust+eBPF监控文件读写防范勒索病毒

作者头像
不吃草的牛德
发布2026-07-13 20:18:25
发布2026-07-13 20:18:25
560
举报
文章被收录于专栏:RustRust

凌晨 3 点,勒索病毒来了

一个 Rust + eBPF 文件监控组件,如何在加密发生前 200ms 拦下它

凌晨 3:14,一台业务服务器的文件写入速率,从平时的 200 次/秒,瞬间飙到 8000 次/秒。 再往下看,目标文件名清一色在被改成 .locked.crypt。 勒索病毒。真来了。 但那天它没得逞——在第 131 个文件时,进程被强制杀掉了。


勒索病毒来临

做安全的同事都知道,勒索病毒永远在凌晨来。人少、反应慢、备份窗口还没跑完。

那天凌晨 3:14,监控大屏上一条曲线突然垂直起飞。某台核心业务服务器的文件写入速率,从平时的 200 次/秒,瞬间飙到 8000 次/秒。

再往下看,更吓人:这些写入操作,目标文件名清一色在被改成 .locked.crypt 这类后缀。

勒索病毒。没想到真的遇到了。

但这次,没让它得逞。在它加密到第 131 个文件时,进程被强制杀掉了。整个服务器,只丢了 131 个临时文件,全部可从备份恢复。

拦住它的,不是杀毒软件,不是 HIDS,是一段我们跑在内核里的 Rust 代码。


02 为什么传统方案拦不住

先说为什么绝大多数方案在勒索病毒面前形同虚设。

杀毒软件:滞后。 依赖特征库。新变种换个加密算法,特征库没更新,等于裸奔。等厂商出了新规则,你的文件已经没了。杀毒软件在更新,勒索病毒也在更改。道高一尺,魔高一丈

用户态文件监控(inotify / fanotify):可绕过。 这些机制在用户空间工作。恶意程序只要提权或走非常规路径,就能绕开。更别说 inotify 在高并发下会丢事件——你以为是安静的,其实是它没看到。

核心问题:监控点在用户空间,攻击也在用户空间。 你在同一个层面,对方想绕你就绕你。

要赢,得升维——进内核。


03 eBPF:把眼睛装进内核

eBPF 是 Linux 内核的一项技术,允许你在内核里安全地运行一段沙箱程序,挂载到各种"钩子"上:系统调用、内核函数、网络栈、甚至安全模块。

关键是:它看到的,是操作系统最底层的真实行为。 任何进程,无论怎么提权、怎么伪装,只要它读文件、写文件、删文件,就得走系统调用——而 eBPF 就挂在系统调用上。

绕不开。

最初我们为了过等保三,选择 ebfp 做了一个内部的服务端防篡改监控。其于这个实践,我们决定继续采用 ebpf。

于是我们挂了这几个钩子:

  • sys_enter_openat —— 任何打开/创建文件的操作
  • sys_enter_write —— 任何写操作(同时拿到写入字节数)
  • sys_enter_rename —— 任何重命名(勒索病毒最爱改后缀名)
  • sys_enter_unlink —— 任何删除(很多勒索会先删原文件再写加密版)

每个文件操作,无论来自哪个进程,都会触发我们的 eBPF 程序,把一条事件塞进内核缓冲区,传给用户空间的 Rust agent。


04 为什么用 Rust 写

这个组件有两部分:内核里的 eBPF 程序,和用户空间的监控 agent。两者我们都用 Rust 写。

内核部分用 Aya 框架。 Aya 是一个纯 Rust 的 eBPF 工具链,不需要依赖 libbpf 或 clang,直接把 Rust 编译成 eBPF 字节码。代码长这样(简化版):

代码语言:javascript
复制
use aya_ebpf::programs::TracePoint;
use aya_ebpf::macros::tracepoint;
use aya_ebpf::maps::PerfEventArray;
use aya_ebpf::cty::c_ulong;

#[map]
static mut EVENTS: PerfEventArray<FileEvent> =
    PerfEventArray::with_max_entries(1024, 0);

#[tracepoint(name = "sys_enter_write")]
pub fn sys_enter_write(ctx: TracePointContext) -> u32 {
    let pid = unsafe { bpf_get_current_pid_tgid() >> 32 };
    let comm = unsafe { bpf_get_current_comm(&mut [0u8; 16]) };
    // 取出本次写入的字节数、文件名,组装成事件
    let event = FileEvent { pid, op: OP_WRITE, bytes, comm };
    unsafe { EVENTS.output(&ctx, &event, 0); }
    0
}

用户空间部分也用 Rust。 原因有三点:

  1. 1. 这个 agent 要常驻、要稳,不能因为内存泄漏或段错误崩——Rust 的所有权在编译期就消除了这类问题
  2. 2. 事件处理是高吞吐的——每秒可能上万条,Rust 的零成本抽象能榨干性能
  3. 3. 安全软件自身不能成为攻击面。Rust 没有内存安全漏洞,意味着恶意程序很难通过 agent 本身反攻

05 检测逻辑:它怎么知道这是勒索

eBPF 负责"看见",真正的判断在用户空间的 Rust agent 里。

核心思路是行为异常检测,不是特征匹配。我们不看"这是不是已知病毒",而是看"这个进程的行为正不正常"。

对每个进程,我们维护一组滑动窗口计数:

代码语言:javascript
复制
进程 P 在最近 5 秒内:
- 文件写入次数:8732 次   ← 正常进程通常不超过几百
- 重命名次数:8011 次     ← 且目标后缀是 .locked/.crypt
- 删除原文件次数:6500 次 ← 加密前先删源文件是典型特征

触发规则(举例):

  • • 5 秒内写入 > 500 个独立文件 → 黄色预警
  • • 同时出现批量 rename 改后缀 → 橙色预警
  • • rename 目标含已知勒索特征后缀 → 红色,立即处置

那天的病毒,在第 3 秒就触发了红色规则:写入 8732 次 + 改名 8011 次 + 后缀 .locked


06 处置:不只是报警

发现不等于拦住。真正的价值在处置。

我们的 agent 触发红色规则后做了三件事,按优先级:

1. 杀进程。 立刻 kill -9 那个 PID。内核里的 eBPF 同时下发一条 LSM 策略,直接拒绝该进程后续的 open/write 系统调用——双保险。

2. 快照隔离。 把受影响目录标记为只读,防止二次写入。

3. 告警 + 取证。 把进程链(parent PID、命令行、加载的库)打包发到安全平台,供事后溯源。

从 eBPF 捕获第一条异常,到进程被杀,整个过程 < 200 毫秒。病毒只来得及动 131 个文件。


07 为什么比传统 HIDS 快这么多

传统 HIDS(主机入侵检测)大多也是用户态轮询 + 规则引擎,但有两个硬伤:

一是采样。 为了性能,它不可能监控每一个 syscall,只能抽样。勒索病毒最开始那几百次写入,很可能被抽样漏掉。

二是路径。 它在 VFS 层之上,恶意程序用 unlink + 直接写 raw device、或者走内存映射 mmap 写文件,它能看见的就不全。

eBPF 在内核 syscall 入口,全量、不可绕过。我们这套组件在压测下,每秒处理 12 万次文件事件,CPU 占用 < 3%。近乎零感。


08 写在最后

那 131 个文件,第二天全部从备份恢复,业务零中断。

事后复盘,攻击链上其实有很多点可以拦——弱密码、过期账号、没打的补丁。但最后真正挡住它的,是内核里那段 Rust 代码。

安全这件事,没有银弹。但有一件事是确定的:监控点必须比攻击点更底层。 攻击者只要还在用系统调用,你就得把眼睛装进内核里。

目前我们利用 Rust + eBPF,把这件事做得既快又稳的最佳组合。相比 HIDS,我们没有成熟的商业化产品,但这个小股入帮我解决了信息安全的一个大问题。


文中架构基于真实 eBPF 技术方案设计;Aya 为实际可用的纯 Rust eBPF 框架。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust火箭工坊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 凌晨 3 点,勒索病毒来了
    • 勒索病毒来临
    • 02 为什么传统方案拦不住
    • 03 eBPF:把眼睛装进内核
    • 04 为什么用 Rust 写
    • 05 检测逻辑:它怎么知道这是勒索
    • 06 处置:不只是报警
    • 07 为什么比传统 HIDS 快这么多
    • 08 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档