找了一圈,无论是中文还是英文社区,竟然没有找到一篇讲如何恢复结构有损坏而无法打开(特别是无法通过另一个系统上的注册表编辑器加载)的 Windows 注册表 Hive 文件的教程,因此就来指点两句江山。

起因

太长不看,直接到步骤。

社团接了一单处理 Windows 重启后无法开机的维修委托。这台机器的症状表现为:开机提示“启动修复无法修复你的电脑”,并指出参考文件 C:\Windows\System32\Logfiles\Srt\SrtTrails.txt,如下图(没有找到中文的图)

图源 https://www.howto-connect.com/fix-srttrail-txt-windows-10-error-automatic-repair-loop/

通过 WinRE 查看这个文件,发现提示了 LCU 相关的错误。但当时维修的同学并不知道什么是 LCU(我也不知道,后来查出来是 Latest Cumulative Update)。因此他进行了如下操作:

  • 用 WePE 启动后尝试使用内置的一键引导修复(自动重建 ESP 分区,特别是 BCD)
  • 修复失败,发现 C 盘完全无剩余空间,因此清理了一些大文件
  • 清理后修复成功,但重启后问题依旧
  • 网上甚至有人建议执行 chkdsk疯了?),但他并未实际运行
  • 尝试通过 DISM++ 修复,但 DISM++ 未识别出来 C 盘中的 Windows 系统
  • 再使用 DISM++ 中的引导修复,成功后重启

重启后错误提示变成了这样(当时没拍照,网图没找到中文版,错误代码可能不同,但一定指出 SYSTEM 文件有问题):

图源 https://www.reddit.com/r/techsupport/comments/a9efru/windowssystem32configsystem_is_missing_or/

显然,图中明确指出系统的注册表坏了,且坏的是 C:\Windows\SYSTEM32\config\SYSTEM 这个文件。

一点基础知识:Windows 的注册表是多个文件组成的一种数据库,这每个文件被称为 Hive。程序所操作的,或在系统内的注册表编辑器中看到的注册表结构实际上是一种映射后的虚拟结构。例如特定于当前用户的设置都存放在 HKEY_CURRENT_USER 这一根键下,但实际的数据存放在 C:\Users\(你的用户名)\NTUSER.DAT 这个 Hive 中。甚至启动系统所用的 BCD 文件也是挂在注册表中的。而上面的 C:\Windows\SYSTEM32\config\SYSTEM 文件在注册表中则映射到了 HKEY_LOCAL_MACHINE\SYSTEM 位置。

再次启动进入 WePE,发现这个文件存在,那可能表明这个文件已经损坏。遗憾的是,他的电脑没有打开还原点,也没有备份注册表,而且 Windows 10 1803 以后并不会继续向 C:\Windows\SYSTEM32\config\RegBack 中备份跨用户的注册表 Hive 文件。这等于废掉了 Windows XP~7 时代的 “最后一次成功的配置” 启动选项。M$ 居然说这是为了减少 Windows 所占用的空间,但这所有的 Hive 加起来才几十 M,吃这点空间和整个系统重新安装所花费的成本而言可以说是不值一提。因此这一变化可以说是不可理喻。好在这一功能还可以通过注册表配置重新打开(讽刺的是,打开 RegBack 备份的选项就在这次坏掉的 HKLM\SYSTEM 项下)。

重启进入我的 Windows To Go 系统,尝试从其内部的注册表编辑器,通过“加载配置单元”的方法挂载损坏的 Hive 文件。然而,系统拒绝加载该文件:

由于没有备份文件,网上也没有找到其它的恢复办法,因此我们只能告知该同学,需要重新安装系统。因为当时比较晚了,因此就只进行了数据备份,准备第二天继续。但是回宿舍感觉不应当。就因为一个小小的注册表损坏重新安装系统,属实不值当。同时,他的电脑中有很多大型软件(Adobe 与 Autodesk 系列)和大量 C 盘中的配置(各种开发工具),如果重新安装系统,势必会带来大量时间成本。因此还是准备尝试抢救一下。

过程

因为前一天把 U 盘放在了工作室,只能第二天开工,把损坏的 SYSTEM 文件拷在我的电脑上,用 010 Editor 打开,大致浏览了一下。“感觉”内容都还在,并没有存在整个文件变成 00 或 FF 的现象,因此还是有拯救的可能的。

意,强烈建议你在操作之前,至少对整个 C 盘进行镜像级的全盘备份!可以通过 Ghost、DiskGenius 等工具完成。

如果做纯手工修复,可能这时候需要对照 Windows 注册表 Hive 文件格式一点一点检查。然而,该格式并不公开,只有一些大家猜测出来的文档,同时 Hive 文件结构相当复杂,手动修并不现实。因此我有如下思路:

  • 首先,我们需要找一个能读取 Windows Hive 文件格式的库或工具,同时该工具必须不利用 Win32 API 来操作 Hive 文件(因为上面通过 WinToGo 打开 SYSTEM 文件的失败尝试表明 Win32 API 已经无法接受这个文件),这筛选掉了 Registry Finder、WinReg等。
  • 其次,如果该库或工具也可以写入 Hive 文件,那可以尝试先打开,再写入一个新的文件,看看这个写入的文件是否可以启动电脑。我将这种过程称为”洗一遍“。
  • 再次,如果没有找到可以写入 Hive 文件的,就找一个无法打开损坏的 Hive 文件的工具,那么读进来的时候会出错,通过错误信息甚至挂调试器等手段找到出错的位置,再对照网上能找到的 Hive 文件格式,手动恢复

幸运的是,上面的第三点并没有派上用场,因为我找到的工具——hivex 已经通过写入新的注册表文件修复了该问题。

Hivexlibguestfs 项目下的一组操作 Windows Hive 文件的 C 语言库。更好的是,hivex 提供了几个 CLI 工具:hivexml、hivexsh 与 hivexregedit,让我可以直接调用这些工具来处理注册表,不需要写代码。

上述工具中对我们有用的是 hivexsh,也就是”Hivex Shell“,相当于一个命令行版的 regedit。

这些工具本来设计来是在 Linux 等环境操作各种系统的磁盘镜像的,Hivex 仅是其一个附加功能。因此这个项目也很有 UNIX 味——比如用比我还老几倍的 autoconf + make 构建工具链。里面也没有只比我大 2 岁的 CMake 和比我小的 Meson 这些现代化构建工具的配置。因此在 Windows 上构建这套工具就变得痛苦万分。在和 MSYS2 搏斗半个小时后,我果断放弃,在 Ubuntu 虚拟机上 apt install 下来了 hivex 的全套工具(允悲)。在 Linux 上修复 Windows 系统文件错误,想想都讽刺。

$ sudo apt get libhivex-bin libhivex-bin-dbgsym libhivex-dev

接下来,将损坏的 SYSTEM 文件上传到 Ubuntu 中,执行下列命令:

$ hivexsh -uw SYSTEM           # SYSTEM 是要修复的文件名,如果你的不同,就需要相应更改,u 表示允许一定程序上的结构错误(最好在不带 -u 时失败时再加上),w 表示允许写入(必须)

(此处进入了 hivexsh 的 Shell)
SYSTEM\> ls                    # 查看 Hive 内的内容,可以与自己电脑上的对比
SYSTEM\> commit SYSTEM_NEW     # 将整个 Hive 文件写入到 SYSTEM_NEW 文件
SYSTEM\> exit

然后将生成的 SYSTEM_NEW 文件传到 Windows 系统中。在我的电脑的注册表编辑器中加载该文件成功,表明至少一定程序上其结构被修复。

然而,这只涉及到了一个 SYSTEM 文件,我们注意到还有 SYSTEM.LOG1 等日志文件,因此我们需要生成这些文件。我的方法相当简单:直接在我的电脑上加载生成的文件,再卸载,就生成了这些文件。

将这些文件覆盖故障电脑上的对应文件(注意清理掉 config 文件夹下所有 SYSTEM.xxx 文件),重启正常进入系统,恢复成功。

但如果你照着这样做,并没有成功,那可能说明你遇到的文件损坏要严重一些,可以参考下一节我提供的更多思路。

写在后面

我把 hivex 生成的文件和原来的损坏文件用 010 Editor 进行了对比,结果发现只有两个位置不同:

上图为修复后,下图为修复前

查询网上推断出的 Hive 文件结构,第一处对应 Primary Sequence Number,其应当与 Secondary Sequence Number 相同,以表示文件完全同步。但图中显示的两个数值(0002ADAC0002ADAB)不同(因此这样修复后不知道系统会不会有暗病)。第二处是文件头的 Checksum。可能就是这两个“小”问题导致了系统不认文件。

实际上,在 Windows XP 与 Vista 时代后,Windows 的注册表格式已经相对健壮,通过日志文件等机制,注册表结构损坏已经相对少见。本次这样简单的一个小问题居然没有被 Windows 自动修复,而是直接躺下拒绝启动,还是让我比较意外的。

同时如果上述方法没有解决你的问题,我提供这些额外思路:

  • 先用十六进制编辑器打开文件,看看有没有变成全 0 或全 1,如果整个文件都已经乱掉了,也不用挣扎了。
  • 如果 hivex 即使加上 -u 也打不开损坏的文件,尝试加上 -duw 打出大量日志,分析 hivex 日志看看哪里出了问题,再对照推测出的格式文档,手动修复。
  • 如果还不行,考虑用其它 Hive 文件处理库而不是 hivex,如 regipypython-registryRegistry C# 库 等等,其中 regipy 似乎更新得比较勤。建议打开这些工具的异常或日志功能,捕获所有解析过程中的错误,甚至挂上调试器,以便定位问题点,再去手动恢复。


不想被自己的惰性打败。