CrawdStrike 関係で Windows のカーネルドライバーの動きを少し解説

NHK の解説

【最新】システム障害 マイクロソフトは「サービスは回復」と発表 影響は一部で継続も収束にむかう | NHK | 航空 https://www3.nhk.or.jp/news/html/20240720/k10014517151000.html

Microsoft からの復旧手順

KB5042421: CrowdStrike issue impacting Windows endpoints causing an 0x50 or 0x7E error message on a blue screen – Microsoft Support https://support.microsoft.com/en-us/topic/kb5042421-crowdstrike-issue-impacting-windows-endpoints-causing-an-0x50-or-0x7e-error-message-on-a-blue-screen-b1c700e0-7317-4e95-aeee-5d67dd35b92f

Recovery options for Azure Virtual Machines (VM) affected by CrowdStrike Falcon agent – Microsoft Community Hub https://techcommunity.microsoft.com/t5/azure-compute-blog/recovery-options-for-azure-virtual-machines-vm-affected-by/ba-p/4196798

どのようにNULLポインタアクセスなのか?

画面キャプチャだけ抜き出し。

このツイートでは、00000000 0000009c ゆえに NULL ポインタアクセスという指摘になっているが、(確か)実際は Windows 起動時はリアルモードで実行されるので DS レジスタが有効になっていて DS:009c がアクセスされる。つまり 002b:009c から DWORD の 4バイトの読み込みでアクセスエラーとなっているので、NULL ポインタアクセスとは限らない(まあ、この DS が間違っている可能性もあるので)。ここは私も見逃したところのなので、特に言及はしない(私が間違っている可能性あるし)以下はひとつの考察である。

アセンブリコードを見ると、

mov r9d,dword [r8] ds:002b:00000000`0000009c=????????

r8 レジスタが示すメモリつまり [002b:009c] なんだけど、きちんとデータセグメントにあるっぽいのに、read access error を起こしているのはかなり変な状況になっている感じがする。

が!!!、再考すると、

  • カーネルドライバは「プロテクトモード」で動いている
  • セグメントレジスタの値(特にcs=0010, ss=0018, ds=002bなど)は、プロテクトモードのGDT内のエントリを指している可能性が高い。

とのことなので、これは「プロテクトモード」っぽい。

となると、先のツイートにあるように、なんらかの変数A(配列の先頭と思われる)に対して「オフセットで、0x9cの場所を参照しようとしている。つまり

DWORD x = A[ 0x9c ] ;

みたいなコードで、DWORD(4バイト)読み込みをしようとしているのだが、最初の変数 A が NULL ポインタなので(おそらく初期化漏れ)、アクセスエラーを発生している。大抵のメモリでは、先頭 0x1000 位が NULL ポインタチェック用に用意されていることが多く、それに引っ掛かったのだろう。

DWORD *A = 0x00 ;
// ここで変数 A の初期化漏れ
DWORD x = A[ 0x9c ] ;

この部分、変数 A の初期化漏れを Rust で回避できるかどうかなんだけど、Rust の場合は初期化していない変数を利用しようとするとコンパイルエラーになるので、これは回避できるかも。

「インサイド Windows」下巻の第12章より

ここの「ドライバー実行環境(DXE)」のときに、既にプロテクトモードになっているそうだ。なので、リアルモードのセグメント化でつかう DS レジスタは、既に GDT を示しているのだけなので、00000000 0000009c がフラットなアドレスと指定される。メモリを示すときはこんな小さなアドレスを示すことはないので、配列の先頭を示すアドレスの初期化漏れ(しかもわざわざ 0x00 が入っていた)と考えるのが正しい。

参考先

Windows カーネルドライバーって Visual Studio のプロジェクトテンプレートにあるらしく、結構便利になっている。

Write a Hello World Windows Driver (KMDF) – Windows drivers | Microsoft Learn https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/writing-a-very-small-kmdf–driver

追記 2024/07/25

Technical Details: Falcon Update for Windows Hosts | CrowdStrike https://www.crowdstrike.com/blog/falcon-update-for-windows-hosts-technical-details/

名前付きパイプの読み取りで失敗して、C-00000291-*.sys の読み込みあたりでエラーが発声しているので、Although Channel Files end with the SYS extension, they are not kernel drivers. カーネルドライバーではないよ、という理由らしい。C-00000291-*.sys ファイルを消すと正常動作するので、読み取り先のデータ領域っぽい(多分、ファイルをオープンして、メモリマップドしていると思う)ので間違いではないが、NULLポインタエラーの否定にはなっていないし、おそらく

  • C-00000291-*.sys ファイルが新規に追加される
  • C-00000291-*.sys ファイルをオープンする。
  • 名前付きパイプでアクセスする(大抵はメモリマップドである)
  • このときに NULL チェックを怠ったか、C-00000291-*.sys の先が NULL 書き込みされていたかでアクセスエラー発生

という手順だろう。

ほぼ100%再現性があるっぽいので(何万台というレベルで発生いるため)、これ最初に動作確認したのか?が危ぶまれるのだが CrawdStrike社としてはどうなのだろうか?

単体テストというか、リリース前の運用テストをスキップしてしまった感じがする。

カテゴリー: 開発 パーマリンク