なぜエクスプローラーは壊れたのか?犯人はアンインストーラーの「雑なコード」

Wait 5 sec.

Windowsの内部挙動をMicrosoftのベテランエンジニアRaymond Chen氏が紹介するブログThe Old New Thingで、またひとつ興味深いクラッシュ事例が紹介されていました。今回の主役はWindows本体ではなく、あるアンインストーラーの挙動です。64bit Windowsには互換性のために32bit版のエクスプローラーが含まれていますが、32bit版のエクスプローラーは、ユーザーが操作するためではなく、他のプログラムが何かの処理を行うために動作します。今回はこの32bit版のエクスプローラーがクラッシュしていたのが興味深い点でした。呼び出し規約のミスが引き起こした「自滅」問題のアンインストーラーは、エクスプローラーにコードを注入し、ファイル操作を繰り返すループを実装していました。しかし、ここに致命的なバグが潜んでいました。Windows APIを呼び出す際、本来は__stdcallを使うべきところを__cdeclで呼び出していた__stdcallは「呼ばれた側」がスタックを片付ける__cdeclは「呼び出し側」がスタックを片付けるこの食い違いにより、毎回APIを呼ぶたびにスタックが二重に消費されていくという恐ろしい事態が発生します。ループが続くほどスタックは削られ、ついにはスタック領域が注入コードそのものを上書きしてしまいました。結果として、実行中のコードが破壊され、エクスプローラーは無効命令でクラッシュします。Windowsチームが「OSのバグか?」と疑うほど大量のクラッシュダンプが残されたそうです。なお、コメント欄にも「なぜコードがスタックと同じ領域に?」という疑問も寄せられていました。どうやらこのアンインストーラーは、デバッガ向けの仕組みを悪用し、スタック上に直接シェルコードを置いて実行していたとのこと。安全性や保護を考慮した実装ではなく、「とりあえず動けばいい」式の危険な手法だったようです。まとめ:高度なアンインストーラーは"高度なマルウェア"と紙一重Raymond Chen氏は以前から「高度なアンインストーラーは高度なマルウェアと区別がつかない」と語っています。今回の事例はまさにその象徴で、エクスプローラーにコード注入スタック破壊自己崩壊によるクラッシュが発生という、OS側から見れば完全に"攻撃"とみなせる挙動となっていました。アンインストール処理は地味ながら難しく、誤った実装はシステムの安定性を大きく損ないます。今回のケースは、呼び出し規約という基本的な部分のミスが、どれほど深刻な結果を招くかを示す好例と言えそうです。