群晖被 C3RB3R 加密后怎么自救:免密钥恢复 Btrfs 旧数据块

先说结论

如果你的群晖 NAS 被 C3RB3R(文件后缀 .L0CK3D,留 read-me3.txt 勒索信)加密了,先别急着认栽、也别付赎金。只要你的共享是 Btrfs 文件系统、且还没做 defrag / balance / 强制修复这类破坏性操作,有一大部分文件是可以在没有密钥的情况下找回来的。原因很反直觉:C3RB3R 是「原地改写」文件,而 Btrfs 的写时复制(CoW)会把被改写的旧内容留在盘上,原文件的明文就藏在这些没被回收的旧数据块里。

开源社区已经把这个恢复流程做成了一套工具:crt106/c3rb3r-btrfs-recovery(MIT 协议,只读,只用于恢复你有权处置的数据)。本文把它的原理、路线选择、分步操作和真实案例数据讲清楚。如果你还没中招,请先读上一篇《群晖 NAS 被勒索:先升 DSM,再开不可变快照》——防住比事后救重要得多。

一、两条自救路线,先选对

能不能拿回文件,第一步不是找工具,而是判断加密进程还在不在。这两条路线收益差很多:

路线 A:加密进程还活着(收益最大)

如果事发不久、NAS 没重启,加密进程(例:/usr/bin/libgld -n 308,程序文件可能已从磁盘删除)很可能还在内存里跑。这时要做的第一件事是:

  1. 用 kill -STOP <pid> 把它冻结,不要 kill,更不要重启。
  2. 然后按另外两个仓库的方法,从进程内存里把会话主密钥取出来——这样能解开全部文件,收益远大于下面那条 Btrfs 路线:

路线 B:加密进程已退出(本仓库补的缺口)

如果 NAS 已经重启过,或者事发已经十几小时,内存里基本找不到密钥了(该项目作者做过一次穷尽式内存扫描,结果为零)。这时就走本仓库的 Btrfs 旧数据块恢复路线:不依赖密钥,只靠文件系统留下的旧明文。

本文重点讲路线 B。两条路线可以组合:先试拿密钥,拿不到再走 Btrfs。

二、原理:为什么没密钥也能找回明文

一句话:C3RB3R 只改写了文件的几段,而被改写的旧内容在 Btrfs 上整块留了下来。

具体怎么加密的:

  • 小文件:整体加密;
  • 1–5MB 的文件:只加密前 1MiB;
  • 大文件:加密「头、中、尾」三段,合计约 12%。

Btrfs 是写时复制文件系统。当文件被原地改写时,新内容会写到新的位置,而原来的数据块(extent)只要还被文件里「没被加密的部分」引用,就整块保留在盘上。这些旧 extent 像书挡一样夹在文件两端,作者把这种现象叫 「bookend extent」。

恢复工具做的事就是:按 Btrfs 的 extent 树,把每一个被改写的字节映射回它原来的旧 extent,直接从块设备只读读出来,拼回原始字节。更妙的是,在开启了「数据校验和」的共享上,还能用 Btrfs 自带的 CRC32C 校验和逐块证明恢复出来的内容确实是对的。

举个具体的例子更好理解:一个 100MB 的家庭视频,C3RB3R 只加密了头、中、尾三段约 12MB,剩下近 88MB 的明文原封不动躺在旧 extent 里;而一个 50KB 的文档因为小于 1MiB、被整体加密,盘上就找不到旧明文了。所以文件越大、越开启校验和、写入越早(早于最近一次快照),能捞回来的比例越高;反过来,海量小文件、数据库的处境最差。

一个容易误解的点:没勾「数据校验和」的 NoCOW 共享也不代表没救。只要数据写入时间早于该子卷的最后一次快照(哪怕快照后来删了),覆盖时仍然会走写时复制——所以快照前的老数据照样能捞。

三、这个工具是什么

crt106/c3rb3r-btrfs-recovery 本质上是一个 AI Agent 技能(SKILL.md),给 Claude Code、Codex、Cursor 这类支持技能机制的 agent 用。它把一次真实恢复的完整流程、只读工具和验证方法整理成通用指南。核心设计原则有三个:

  • 只读:NAS 端只做 ioctl 查询、读文件、读块设备,结果写到 NAS 以外的盘,绝不在数据卷上落文件、绝不改写任何东西;
  • 可验证:恢复出来的每一块都能和 Btrfs 校验和对照,区分「精确恢复」和「推测解码」;
  • 可回退:本地换候选字节时生成 *.c3rbak 备份,字节级回滚。

四、详细步骤(Btrfs 恢复路线)

第 0 步:先守住红线(这步比恢复本身重要)

在动任何东西之前,这几件事绝对不要做:

  • 不要删除、覆盖任何 .L0CK3D 文件,也不要清空回收站。旧数据块正是靠这些密文文件「拽着」才没被文件系统回收——你一删,旧明文可能立刻被覆盖,就真没了;
  • 不要做 defrag、balance、btrfs check --repair,这些会重写数据,直接毁掉旧块;
  • 不要付赎金,也不要运行来源不明的「解密器」(很多是二次诈骗);
  • 尽快把 NAS 断电或断网保现场,减少写入。

第 1 步:安装

用 skills CLI:

# 安装到当前项目
npx skills add crt106/c3rb3r-btrfs-recovery

# 全局安装,或指定 agent
npx skills add crt106/c3rb3r-btrfs-recovery -g
npx skills add crt106/c3rb3r-btrfs-recovery -a claude-code

手动安装:把 skills/c3rb3r-btrfs-recovery/ 整个目录复制到 agent 的技能目录(如 Claude Code 的 ~/.claude/skills/)。

第 2 步:环境自检 preflight

在任何设备上先跑这一步,它会逐项给出 OK / WARN / FAIL 和处理建议:

cd skills/c3rb3r-btrfs-recovery/scripts
python mkjob.py -- preflight /volume1/某个文件.jpg.L0CK3D > job.sh
{ printf '%s\n' "$SUDO_PW"; cat job.sh; } | ssh admin@NAS "sudo -S -p '' sh -s"

这里 mkjob.py 负责把工具和路径列表打包,经 SSH 标准输入交给 NAS 执行,不在数据卷上落文件。

第 3~6 步:确定代数 → 单文件验证 → 全量普查 → 导出验证

这一步建议直接把情况交给 agent,它会按 SKILL.md 自动执行;也可以手动跑脚本。流程是:

  1. 确定攻击代数 G_ATTACK:不同代际的加密格式略有差异,先对齐;
  2. 用已知原件做单文件验证:拿一个你本地还留着原版的同名文件去对,确认恢复逻辑正确、再批量;
  3. 全量普查 survey:扫描整卷,标出每个文件哪些字节被改写、能不能从旧 extent 找回;
  4. 流式导出 + 多重验证:把找回的字节拼回,本地多进程解码验证、对照已知原件、挑选最佳候选,最后出报告。

实操时几个要盯紧的点:preflight 任何一项出现 FAIL(比如发现是 ext4 卷、或 NAS 上找不到 Python 3),就先别继续,按它给的建议处理;G_ATTACK 代数是后面所有匹配的基准,判错会导致整批对不上;单文件验证一定要用你确认本地原版无误的文件去对,验证通过再放手批量,别拿疑似损坏的副本去校验。

脚本分工一览

脚本 运行位置 作用
c3r_btrfs.py NAS(root,Python 3.6+ 标准库,只读) preflight、env、gens、survey、extract、patch、csum、plan
mkjob.py 本地 把工具和路径列表打包,经 SSH 标准输入交给 NAS 执行,不在数据卷上落文件
c3r_local.py 本地 解包 tar 流、多进程解码验证、对照已知原件、挑选最佳候选、打补丁换候选并可回退、统计

第 7 步:看懂报告

恢复报告会把结果分三类,别混为一谈:

  • 精确恢复并经校验和证实:最可靠,Btrfs CRC32C 逐块对得上;
  • 推测后解码通过:逻辑上拼回成功,但缺校验和佐证,建议保留待核;
  • 失败:这部分就是真没了。

五、兼容性:你的环境能不能用

项目已在 群晖 DSM 7.2.1(SHR、cachedev、Python 3.8)上真机验证。其他环境请先跑 preflight,它会逐项给建议。已知适配/待验证的情况:

  • DSM 6、OMV、Unraid、没有 Python 3 的设备、把盘拆下来接到 Linux 电脑离线处理——文档里有各自的定制引导和报错处理;
  • 不适用于 ext4(多数 QNAP 卷、群晖老卷),ZFS 需要另一套工具链。

六、真实案例:到底救回了什么

作者公开的第一个案例,恢复结果如下:

范围 结果
homes、photo 等 CoW 共享 35,445 个文件 / 229.9GB,经 SHA 核对和完整解码验证
精确恢复部分的校验和抽检 约 20 万个 4K 块,与 Btrfs CRC32C 全部一致
NoCOW 共享中、快照前写入的数据 又找回约 6,100 个文件 / 约 346GB(另有约 3,800 个文件本地已有原件)
救不回的部分 1MiB 以下被整文件加密的小文件、数据库文件、NoCOW 共享中快照后写入的文件

一句话总结:大文件、开启校验和的共享、快照前的老数据,恢复率高;整文件加密的小文件、数据库、快照后才写入的内容,基本没戏。

七、救不回什么,以及几点提醒

  • 1MiB 以下的小文件被整体加密,盘上没有旧明文可捞;
  • 数据库文件(如 SQLite/PostgreSQL 这类频繁整文件改写的)往往整块重写,旧块早被覆盖;
  • NoCOW 共享里、最后一次快照之后才写入的数据,覆盖时没走 CoW,旧块不存在;
  • 工具本身只读,但任何恢复操作都有风险:请先保全证据、先用已知原件做小样本验证,再批量操作;
  • 这不是「一键还原全部文件」的方案,它是把文件系统里侥幸留下的旧明文尽量捞回来,能捞多少取决于写入量和时间——卷越满、事发越久,被覆盖的旧块越多。

三个仓库可以组合使用:soimy/c3rb3r-rescue 和 Benjamin-zlt/c3rb3r-baremetal-rescue 负责「进程还在时从内存拿密钥」,本仓库负责「密钥没了再靠 Btrfs 旧数据块」。

八、几个常见误区

  • 「删掉勒索信、改个名就能用」:文件内容已被加密改写,改名不改变数据;删掉 .L0CK3D 反而会让旧数据块失去引用、加速被文件系统回收,可能让原本能捞的也捞不回。
  • 「我开了快照,肯定能还原」:快照救的是快照时点之后的新增和改动,而这次是加密进程直接改写「当前版本」的文件,快照覆盖不到已被改写的当前文件;本方案恰恰是利用「快照前的旧块」,没快照时旧块也可能因 CoW 留存,别把快照当成唯一指望。
  • 「付费找人解密更省事」:来源不明的「解密服务」很多是二次诈骗,既拿不回数据又泄露隐私;本文方案只读、免费、可自检,优先走它。
  • 「ext4 的群晖老卷、QNAP 也能用」:不适用。ext4 没有写时复制,旧块不会被保留,需要完全不同的思路。

免责声明

本项目按「现状」提供,只用于恢复你自己有权处置的数据。工具本身只读,但任何恢复操作都有风险:请先保全证据,先用已知原件做小样本验证,再批量操作。作者不对数据损失承担责任。本文只是对该开源方案的整理与说明,不构成任何专业技术服务承诺;真要动手前,请务必先读完仓库里的 SKILL.md 与 references 目录。

THE END