01
事件回顾
2024年7月19日,全球的Windows设备,爆发了大面积的蓝屏现象!

除了大家喜提半天的额外假日外,这场蓝屏事件的波及之广,爆发之迅速,在计算机历史上,也不多见!
但有趣的是:问题的原因并不是Windows系统本身。而是某头部软件公司CrowdStrike,更新的一款安全软件,或者说是杀毒软件所导致的。
如上期的文章“CPU眼里的:虚拟内存”所说,普通应用程序会被MMU隔离起来,如果单个进程、或应用程序崩溃,是不会祸及其他程序和操作系统的。能让系统蓝屏,往往是操作系统本身的缺陷、或是硬件设备的驱动程序。
由于处理此次蓝屏问题的办法之一,就是删除一个驱动程序文件(C:\Windows\system32\drivers\crodstrike\csagent.sys)所以,造成蓝屏的原因是CrowdStrike发布的驱动程序。
02
自制蓝屏程序
那为什么杀毒软件可以让Windows蓝屏,而我不行呢?这里就让我们一起写一个简单的Windows蓝屏程序吧!
打开VS code,在文件blue.c中定义一个函数blue_func,并在里面作一次除法运算,最后在main函数中作调用,我们故意把参数设置为:0,从而实现:1除以0的情况:
#include <stdio.h>int blue_func(int input){ return 1/input;}int main(){ return blue_func(0);}
代码写完,做一下编译,然后在运行一下。在linux环境下,我们会得到一个异常提示,程序也会被强制退出:
同样,在Windows下,程序也会被强行退出;有时候,也可能会出现一个弹窗提醒,但并不会看到:蓝屏。
这是因为如文章“CPU眼里的:进程vs线程”所说:操作系统会把每一个进程或应用程序通过MMU等手段隔离起来,甚至通过系统调用的方式,让应用程序和操作系统内核隔离。
一旦某个进程或程序错误,操作系统就可以让此应用程序强行退出,并返还系统资源。这样某一个程序的错误,并不会伤及其他应用程序和整个系统。
但如果这个函数运行在驱动程序里面,会怎么样呢?让我们快速写一个最简单的驱动程序,它只有一个入口函数:DriverEntry,然后就可以调用函数blue_func来作除0运算了:
int blue_func(int input){ return 1/input;}#include <wdm.h>#include <ntddk.h>NTSTAUS DriverEntry(void a, void b){ blue_func(0); return STATUS_SUCCESS;}
通过下面的visual studio命令行,编译这个驱动程序:
cl.exe /c blue.c /I "C:\Program FIles(x86)\Windows Kits\10\Include\10.0.22621.0\km"cl.exe blue.obj "C:\Program FIles(x86)\Windows Kits\10\Lib\10.0.22621.0\km\x64\ntoskrnl.lib" /subsystem:native /driver:wdm -entry:DriverEntry
如果一切顺利的话,我们会得到一个驱动程序:blue.sys
接着,通过Windows命令行,创建一个名字叫做:blueDriver的服务,然后,就可以start这个服务了:
sc create blueDriver binpath= D:\blue.sys type= kernelsc start blueDriver
但系统拒绝了我们的运行请求!
有时候Windows也会给我们弹出一个这样的提示窗口:
原因是这个驱动程序没有签名。这是一个非常重要的安全保障。
由于Windows的生态特性,没有对开发者进行必要的限制,2000年左右的时候,一天遭遇几次蓝屏都是非常正常的事情。
为了安抚用户,当时的微软CEO鲍尔默,还亲自撰写了蓝屏后的提示信息。但经过分析后,发现大部分蓝屏问题,并不是Windows的内核问题,而是各种驱动程序。
随后,就加强了驱动程序的管理,任何驱动程序必须数字签名,并经过必要的测试,这样当驱动程序出问题后,微软也可以定位到驱动程序的责任人或企业。
也正是因为这一通操作,才让蓝屏的情况,越来越少了。这一方面是Windows内核越来越成熟稳定;另外一方面也是Windows生态系统,越来越完善、规范。
所以,从这个角度上说,操作系统的竞争,不仅仅是性能之争,更是生态之争。Windows可能不是最优秀的操作系统,但一定是生态十分完善、健康的系统。
好了,为了跳过检查,我们需要以管理员的身份,在命令行中输入以下命令:
bcdedit /set testsigning on
并重启计算机,如果一切顺利的话,计算机的右下角会有一个:test mode的水印:
接着我们再次启动这个服务:
sc start blueDriver
强烈建议大家在虚拟机上作这个危险动作!
阿布就直接在真机上,测试这个驱动程序了。如你所见,蓝屏如约而至:
03
冰山之下
那为什么操作系统不能像处理应用程序一样,强制让这个有问题的驱动程序退出,而非要波及整个系统呢?
这是因为驱动程序和内核之间并没有很好的隔离,二者之间不仅相互调用,而且内存空间完全共享。这也是杀毒软件、游戏软件,尽管没有提供任何硬件设备,但仍然热衷于添加驱动程序的原因。
因为在内核态,驱动程序有很高的访问权限,可以扫描系统整个系统内存,包括其他应用程序使用的内存,而这是普通程序无法做到的。
回到正题,正因为驱动程序和内核是你中有我,我中有你的关系;操作系统无法精准的让有故障的驱动程序、以及正在使用驱动的应用程序强制退出。
所以,对于驱动程序的运行故障,操作系统往往没有比较好的善后方法。Windows给个蓝屏提示,已经算比较人性化了,Linux则往往招呼都不打,直接重启。
如你所见,造成一次蓝屏,不需要什么特别的技术,一个小小的低级错误就足够了!
此次蓝屏能快速蔓延的原因,可能源于CrowdStrike巨大的用户群里,作为一个老牌的软件公司,有这么多航空、医疗、交通、银行的客户并奇怪。
而奇怪的是,这么一个很容易重现的bug,居然能够畅通无阻的通过:开发、review、自动测试、代码合并、小范围推送、大范围推送等这一系列的流程,确实有点不可思议!
让我们静待CrowdStrike的解释吧。
04
更多知识
好了,基础不牢,地动山摇!
如果喜欢阿布这种解读方式,希望更加系统学习这些编程知识的话,也可以考虑看看由阿布亲自编写,并由多位微软大佬联袂推荐的新书《CPU眼里的C/C++》