本文首发于 BetaWorld 博客,原题为《如何解除 Flightsigned Build 的时间炸弹》。BetaWorld 是一个专业的中文信息网站。转载须注明出处。
「一名喜欢beta的外国菜鸟,抱歉中文不好」
名词定义:Flightsigning
微软一般会使用如下四种证书为 Windows 系统文件签名:
- PreProd – PCA 2010
- Prod – PCA 2011
- Flight – Development PCA 2014
- Test – 杂项。
- 其中,最常见的是 MSIT Test CodeSign CA
动名词「Flightsigning」指的就是使用 Flight 证书进行签名的过程。
「Flight」在这里指的是微软使用的一个特定证书,或一个证书系列。当使用 Flight 证书对一个 Windows 版本签名时,该版本便被称为「Flightsigned 版本」。
为什么「Flightsigned 版本」无法在当前日期启动?
早期 Flightsigned 版本的 Flight 证书具有明确的过期时间,并设计为在系统时间超过过期时间后立即失效。这意味着一旦这些证书过期,Windows 将把这些证书数字签名的系统文件视为无效文件。
这有点像你不能用打补丁的系统文件正常启动 Windows。但有一点是不同的,如果签名证书因文件的修改而无效,则可以在 BCD 文件中增加 NOINTEGRITYCHECKS 选项强制引导程序继续执行,但如果是证书过期,引导程序就根本不听使唤了。
这意味着系统时间不得超过这些 flight 证书的到期时间。因此,不能在当前日期启动 flightsigned 版本。
我为什么要写这篇文章?
过去 15 年里,我一直在基于 DOS 和「旧版」NT 技术的 Windows 版本上下功夫。而后在一周前,我突然想来试一试「现代」的 Windows 版本,因为我对它们一无所知。我相信,从 Windows 10 测试版开始是一个不错的主意。
所以我选择了 Build 9841:Windows 10 的第一个正式的预发布版本。事实证明我犯了个大错!我认为,这可能是最容易拆除时间炸弹的版本,而我忽略了一个事实,即目前有多少人讨论拆除它的时间炸弹——完全没有!然后我便读到只有产生于 fbl_partner_eeap 分支的时间炸弹可以被拆除,因为「它们包含非功能的时间炸弹,因此可以在当前日期安装」。
——好的,我想挑战一下只有 fbl_partner_eeap 版本可以拆除时间炸弹的流行观念,所以我选择继续折腾 Build 9841(它来自 fbl_release 分支,所以和你想的一样:一个真正的 flightsigned 版本)。
我无疑高估了自己的能力,意识到拆除 flightsigned 版本的时间炸弹就像在五行山下掘地求生。唔,既然我在说如何拆除它的时间炸弹,事情便明朗起来:我最终成功了。这是一场漫长而艰苦卓绝的战斗,我整整花了将近一周的时间,才完全击败 flightsigning。
最初我修改内核程序以启用 NOINTEGRITYCHECKS 选项,然后,我把所有系统文件所有数字签名中的所有证书替换为来自 Build 9838 的非 flight 证书。是的,它成功了,但目录文件也用 flight 证书进行了数字签名。好的,兵来将挡,我随即把所有的目录都换成了 Build 9838 的目录——没有用处。
那么在 ci.dll 上动手似乎是唯一的出路。
因此,最后我修改了 ci.dll,紧接着的是 Windows Loader (winload.exe/winload.efi) 以加载修补后的 ci.dll,其后是 Boot Manager 以加载修补后的 Windows Loader。听起来很容易,但实际上要难得多…其实也没那么难,只是,真的非常没意思。
我修补了 20 个文件,只是为了安全地让 Build 9841 的 x86 版本在当前日期启动,而替换 boot.wim、install.wim 和 winre.wim 的所有索引中出现的所有文件花费了更多的时间。由于这个过程极其恐怖,所以我不打算修补所有 flightsigned 版本的所有架构——我只想发布一个指南,并且希望它能适用于所有 flightsigned 版本的所有架构。
目标受众
本指南仅供有经验的读者阅读。要理解这些过程并亲自修补,您必须充分了解现代版本 Microsoft Windows 的引导过程,还需理解 C/C++ 和所需架构的汇编语言。
如果您自己没有能力执行修补,我可以为您修补它们,前提是您在原文章的评论部分上传所需的文件(因为我是一个大好人)。目前我只能修补 UEFI 的 Boot Manager 和 Windows Loader,因为传统 BIOS 的修补非常耗时。您必须上传 ci.dll、winload.efi、winresume.efi、bootmgr.efi 和 bootmgfw.efi。不要忘记,补丁只负责禁用证书检查,您仍然需要按照这个指南来拆除时间炸弹。
如果您希望自己修补这些文件,请继续阅读以下部分。
修补 Windows 系统文件以在当前日期启动
由于存在不同的架构,即使对于相同的架构,补丁也可能不同(不同的偏移量、不同的寄存器),因此我将主要使用 C/C++ 伪代码演示补丁。
ci.dll
要修补的第一个文件是 ci.dll,它是负责签名验证的代码完整性库。我们不会完全删除签名验证,我们只是让它忽略过期的证书。
有一个名为 STATUS_IMAGE_CERT_EXPIRED 的状态,我们不希望 return 该状态。因此,我们需要修补可能 return STATUS_IMAGE_CERT_EXPIRED 的函数。由于不同的版本具有不同数量能够 return STATUS_IMAGE_CERT_EXPIRED 的函数,并且这些函数可能都不同,因此您应该在 ci.dll 中搜索 0x0C0000605。当您发现类似 mov ???, 0C0000605h 的内容时,请检查是什么原因导致 0C0000605h 移动到 ??? 寄存器,您会发现这样的 if 语句:
if ( condition )
{
// certificate expired
result = STATUS_IMAGE_CERT_EXPIRED;
}
// certificate not expired
我们需要删除 if 语句,最好是将条件跳转修补为无条件跳转,或者 NOP 掉条件跳转(取决于具体情况)。确保找到所有的 mov ???, 0C0000605h 并对其进行修补。
由于我们修改了系统文件,所以为了安全起见,还应该对 CipReportAndReprieveUMCIFailure 进行修补。在这里,我们需要让 (g_CiDeveloperMode & 1) != 0 的 if 语句中的代码执行,无论 g_CiDeveloperMode 的值如何。
if ( (g_CiDeveloperMode & 1) != 0 )
{
// code here
EventDescriptor = (const EVENT_DESCRIPTOR *)CiPolicyFailureIgnored;
*(BYTE *)a10 = 1;
// code here
}
您应该在 TEST 指令之后找到一个条件跳转。同样,根据具体情况,你需要要么将条件跳转修补为无条件跳转,或者直接 NOP 掉它。
没有符号的额外步骤
要修补 g_CiDeveloperMode,首先需要找到 CipReportAndReprieveUMCIFailure 函数。查找该函数需搜索 ASCII 字符串 “This break indicates this binary is not signed correctly”,然后跳转到引用。向上滚动,直到找到对 PsGetProcessProtection 的调用,并在上面不远处看到一个 TEST 指令(test something, 10h)。一旦你找到了,你应该看到另一个 TEST 指令(test something, 1)在这条指令之上,而这个 TEST 指令就是 (g_CiDeveloperMode & 1) != 0 的 if 语句。现在,您可以像使用调试符号一样,在它的正下方修补条件跳转。
winload.exe / winload.efi 和 winresume.exe / winresume.efi
由于我们修补了 ci.dll,因此必须修补 Windows Loader 以加载修补的 ci.dll。有两个东西需要修补 – 签名有效性检查(导致蓝屏显示 Windows 无法加载 xxx 的原因)和引导选项检查(防止自动修复屏幕)。
对于签名有效性检查,我们需要删除对 ImgpValidateImageHash 函数 return 值的检查。您也可以将 ImgpValidateImageHash 本身修补为 return 零,但我建议您修补 return 值检查(将复杂函数修补为仅 return 0 可能会导致不希望的效果)。首先找到 ImgpValidateImageHash 函数,跳转到引用,您应该看到 return 值被移动到另一个寄存器,然后是一个 TEST 指令。您应该修补 TEST 指令下的条件跳转,如果其跳转到 if 语句之外,则修改为无条件跳转,以避免 if 语句内的代码被执行。如果它跳转到 if 语句之内,那么您应该 NOP 掉条件跳转。代码可能是这样子的:
result = ImgpValidateImageHash(something);
if ( result < 0 )
{
result = ImgpFilterValidationFailure(something);
if ( result < 0 )
{
if ( (something & 0x20) != 0 )
goto Label;
result = 0;
}
}
然后,您应该修补 BlImgQueryCodeIntegrityBootOptions,告诉 Windows Loader 忽略“已损坏”的文件,以避免每次启动 Windows 时出现“准备自动修复”屏幕。您只需要将第二个参数(IntegrityChecksDisabled,一个 PBOOLEAN)设置为 TRUE。
没有符号的额外步骤
要找到 ImgpValidateImageHash 函数,请搜索 ASCII 字符串 “1.3.6.1.4.1.311.61.4.1”,然后跳转到引用。要找到 BlImgQueryCodeIntegrityBootOptions 函数,请搜索 0x16000048 直到在其下方不远处看到 0x16000049,然后继续搜索 0x16000048 直到再次在其下方看到 0x16000049。
bootmgr / bootmgr.efi、memtest.exe / memtest.efi、cdboot.efi
Boot Manager 也必须进行修补,才能加载修补的 Windows Loader。该修补过程与 Windows Loader 修补过程相同,只是不需要修补 BlImgQueryCodeIntegrityBootOptions。最困难的部分是修改压缩的 bootmgr 文件以用于传统 BIOS 引导。如果您使用的是 UEFI,那么在修补完这些 Boot Manager .EFI 文件后,您就完成了所有操作。
要修补压缩的 bootmgr 文件,首先需要了解其结构。它可以分为 4 个部分 —— 一个原始的 16 位 stub 加载器,一个包含压缩部分信息的自定义结构,一个 stub PE 文件,最后是压缩的 Boot Manager。
第一步是解压缩它或找到一个未压缩的副本。您应该能够在 boot.wim (WinPE)中找到一个名为 bootmgr.exe 的文件,该文件与 bootmgr 中的 Boot Manager 文件相同,只是没有被压缩。然后,您需要按照上面的 Boot Manager 修补方法进行修补。最后,您必须压缩经过修补的可执行文件并将其添加回 bootmgr。对于压缩,您可以使用 joakim 的 BOOTMGR 重新编译程序 v2。它输出一个带有硬编码 16 位 stub 的组合 bootmgr,因此我强烈建议您从输出文件中提取压缩部分,并用它替换原始 bootmgr 文件的压缩部分。当然,您还必须修改 16 位 stub 下面的自定义结构和 PE stub 的 PE 校验和。jaokim 记录了自定义结构,因此请参见 BOOTMGR 重新编译程序 v2 项目页面。PE 校验和更正有点困难,所以我反编译了一些 bootmgr 签名检查函数并将其组合成了一个程序。只需运行它来显示正确的校验和,然后将 PE stub 的 PE 校验和更改为正确的校验和。
拆除时间炸弹
令人惊讶的是…也许不是,上面的长篇大论甚至与解除时间炸弹无关。如果您正确地修补了所有文件,您现在应该能够在当前日期启动 Windows,但定时炸弹仍然存在。与 Windows 10 的其他预发布版本一样,flightsigned 版本也可以按照这个 BetaArchive 的指南进行拆解。那个指南有点混乱,所以当我有时间的时候,我会为 BetaWorld 写一个更好的指南。
一些拆除时间炸弹的版本和修补后的文件
- 9841 x86 (UEFI & BIOS)
- 9926 x64 (UEFI)
- 14357.1000 (UEFI)
- 9879 x64 (UEFI)
- (Betajyst4094 或 Mintel)解除时间炸弹的 ISO: https://drive.google.com/file/d/1C0Abnp6iah77Yh_5YbAbclDzepufgXBB/view?usp=sharing
- 9841 x64 (UEFI)
- (Betajyst4094 或 Mintel)解除时间炸弹的 ISO: https://drive.google.com/file/d/17P2zHsRHKCXTehmrTi5CLHBalecCQIOL/view?usp=sharing
- 9860 x64 (UEFI)
- (Betajyst4094 或 Mintel)解除时间炸弹的 ISO: https://drive.google.com/file/d/1d9hM94oSQwMC7obvnVW-6gl9YNztjsfr/view?usp=sharing
- 9888 x64 (UEFI & BIOS)
- (Betajyst4094 或 Mintel)解除时间炸弹的 ISO: https://mega.nz/file/fUwUxQiR#0qjyARvo-3k73x2Irh2Wn2f4gbeollxYtmVOEUWgCqc
感谢我同事的翻译,他友好而礼貌的留言:”thank you so much lucas for giving me this f___ing piece of s__t to translate, after translating this s__t I know more about f___ing win 10 than anyone on the f___ing net m_____f___er“
编辑:感谢 Zammis Clark 提出文中错误,现已修正。同时他还指出,“最好”的方法可能是fork EfiGuard 项目并用于修改
作者原注ci.dll中的签名检查部分,这样就不需要使用拐弯抹角的方法处理文件。





留下评论