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 唯一允许的另一种语言。但谁知道呢。