Crowdstrike 分析 [译]
Crowdstrike 分析:
这是一个由于 C++ 语言的不安全内存导致的空指针问题。
作为一名专业的 C++ 程序员,让我为你解析这个堆栈跟踪转储。
计算机内存被组织成一个巨大的数字数组。我们在这里用十六进制表示这些数字,因为这样处理起来更方便。
问题出在哪儿呢?计算机尝试读取内存地址 0x9c(即 156)。
为什么这样不好呢?
这是任何程序都不应访问的无效内存区域。任何尝试从该区域读取数据的程序将会被 Windows 立即终止。
这就是你在这个堆栈转储中看到的情况。
那么为什么会读取内存地址 0x9c 呢?因为……程序员犯了错误。
事实证明,Crowdstrike 使用的 C++ 语言喜欢用地址 0x0 作为特殊值,表示“这里什么都没有”,不要尝试访问它,否则程序会崩溃。
C++ 程序员在传递对象时应该检查这一点,通常通过“空值检查”来实现。
通常你会看到这样的代码:
string* p = get_name();if (p == NULL) {print("无法获取名称");}
string* 表示我们有一个指向字符串起始位置的“指针”。如果它是空的,那么那里什么都没有,不要尝试访问。
举个例子:
struct Obj {int a;int b;};
如果我们创建一个指向它的指针:
Obj* obj = new Obj();
我们可以获得它的起始地址,假设它是 0x9030=36912(这是个随机小数字)
然后地址如下:
- obj 是 0x9030
- obj->a 是 0x9030 + 0x4
- obj->b 是 0x9030 + 0x8
每个成员是从起始地址的偏移量。
现在假设如下:
Obj* obj = NULL;
然后地址如下:
- obj 是 0
- obj->a 是 0 + 4
- obj->b 是 0 + 8
所以如果我在一个空指针上执行以下操作:
print(obj->a);
程序会像你上面看到的那样崩溃。它无法读取值 0x000000004
在这个堆栈转储中,你会看到它试图读取内存值 0x9c。换算成人类数字,这是值 156。
所以发生的事情是程序员忘记检查正在处理的对象是否有效,试图访问对象的成员变量之一...
NULL + 0x9C = 0x9C = 156。
这是一个无效的内存区域。
更糟糕的是,这是一个叫做系统驱动程序的特殊程序,它拥有对计算机的特权访问。因此,操作系统出于谨慎考虑,被迫立即崩溃。
这就是导致蓝屏死机的原因。计算机可以通过简单地终止程序从非特权代码崩溃中恢复,但不能从系统驱动程序中恢复。当你的计算机崩溃时,95% 的情况是因为系统驱动程序崩溃。
如果程序员进行了空值检查,或者他们使用了现代工具来检查这些问题,它本可以被捕获。但不知怎的,它进入了生产,并被 Crowdstrike 推送为强制更新……哎呀!
未来的解决方案是 Microsoft 需要有更好的策略来回滚有缺陷的驱动程序,而不仅仅是将风险更新直接推送给客户。
Crowdstrike 可能会提拔他们的代码安全官员,安装代码清理工具,以自动捕获这些问题。
并且 Crowdstrike 可能会认真考虑将他们的系统驱动程序从目前的 C++ 重写为更现代的语言,如 Rust,后者没有这个问题。
对于那些寻找阴谋论的人来说,替代 C++ 的语言 Rust 被一群奇怪的觉醒人士控制
着,他们在做奇怪的事情。
可能这是一个将关键任务代码转移到 Rust 的阴谋。除了 C,这是 Linux 唯一允许的另一种语言。但谁知道呢。