windowsのログインプロンプトをクラッシュさせたお

Windows/Office武勇伝Surface Proが当たる! 開発者グループOPENキャンペーン>

windowsのsocketには、LSPというドライバーとシステムフックの中間にあるようなフック層がある。
まー当時から長い年月がたったので、ちょっとLSPの話をしたいと思う。
とても、心があたたまる話だ。



LSPは、ネットワークをフックする手段の一つだ。
今は、ネットワークをフックする他のフック手段が潤沢にあるので、そう多様されることもない(?)、LSPは、windows 98SE(winsock2) から動作してくれるという、下位互換性最高な特典がある。
この広い下位互換性は、広く広めたいソフトウェアを書きたい人にとっては、とてもメリットがあるのだ。
そして、一度インストールさえしてしまえば、それ以降、特別な方法を使わずとも、すべてのネットワークを使うプロセスをフックすることができる。(VISTA以降はインスコ時にのみ管理者特権が必要。)


とても、素晴らしいメリットだが、光あるところに影があるように、必ずデメリットが有る。
中二病の自意識過剰な高揚のあとにある、黒歴史のように。


LSPのデメリットはどこだろうか。

LSPは、一応フック扱いなのでとても扱いづらい。
フックルーチンを書いたことがある人ならわかると思うが、フックはとても扱いづらいのだ。
フックされるプログラムはたぶんこー動作するから、それなら俺様はこう返して・・・みたいな相手のコードの書き方を意識してプログラムする必要がある。
アクション映画のリハーサルみたいなものだ。悪役がこう切りかかるから、ヒーローはこうジャンプしてかわして・・・というのを延々と書いていく。
フックされるプログラムは、当然変なフックがあることなんて意識しないでデータを送ってくるので、それらをぬらりひょらりと処理してあげる必要がある。

また、フックして自分がやりたい追加処理もある。
その追加処理を挟んでなお、全体が破綻しないように、矛盾しないように空気を読む必要がある。

そのため、フックするプログラムは、状態を保持するためのステート管理と、相手のプログラムへの配慮と、そして無限の愛で埋もれることになる。
これの努力をフックするメソッド分繰り返すことになる。


さて、LSPの場合は、winsockのフックであるため、フックしなくてはいけない命令は膨大である。
何しろ、selectなんて3種類もあるのだ。WSPSelect WSPEventSelect WSPEventSelectの三種類がある。(それぞれが、WSASelect WSAEventSelect WSAEventSelect に対応している。)
この他にも、WSPSend (同期/非同期イベント/ノンブロック) WSPRecv(同期/非同期イベント/ノンブロック)などがある。
同じAPIであっても、動作モードで話が変わってくるだ。
WSPIoCtlとかで相手が動いていそうなモードを見て、それに合わせて、自分も動きを変えてあげないといけない。
ここで、相手とうまく息を合わせられなかったが最後、まっているのはフリーズである。
ちょっとしたミスがネットワークを使う全てのプロセスを壊してしまう。

たぶん、相手のプログラムを書いた人は、こう処理を書いてくるはずだ、、、という空気を読んだプログラミングが必要だ。
相手のプログラムへの思いやりの心、それは愛である。
愛が無ければ、フック処理はやってられない。

相手のプログラムがこうくるだろうからこう返してなどのような処理を積み重ねていくと、当然プログラムは複雑化する。
モードの把握や、ステート管理などでスコープの広い変数がたくさん使われることになる。
とても複雑なプログラムである。
複雑なプログラムには、バグが入り込む隙間はとても多くなる。

もし、LSPがメモリ空間とかを破壊したらどうなるか?
フックされたプロセスが死ぬことになる。



問題なのは、LSPがフックする対象のプログラムは、ネットワークを使っているすべてのプログラムになる。
ネットを使うプログラムといっても、それはブラウザだけではない。
windows標準のネット共有プログラムだったり、ネットワークプリンタだったり、その他の様々なソフトウェアがある。
LSPをバグらせると、こいつらが面白いように死ぬ。


たいていは、もっとも処理が複雑でバグが入りやすい送受信部で落ちることになる。
ただ、たまに、プログラム開始部に100%落ちるバグを仕込んでしまったりする。
まー書いている方も人間だから、起動時のバグが絶対にはいらないとは言い切れない。


送受信部で死ぬのは、データを送受信しなければ死なないからまだいいんだけど、
起動時に死ぬのはとてもいただけない。
起動時に死んでしまうと、通信をするすべてのプログラムがクラッシュすることになるのだ。



さて、windowsには、ログインプロンプトがある。
ログインといえば、Active Directory認証とかで外と通信することがある。
外と通信するということは、LSPを読み込んでいるのだ。
そして、そのLSPはバグっている。読み込んだら、読み込んだプロセスは100%死ぬ。
するとどうなるか、LSPが死ぬとログインプロンプトも死んでしまうだ。
(当時は、ADログインを利用していなかった、関係ないみたいだった。OSはwindows2000だったかなー?)


ログインプロンプトが死ぬとどうなるか?
死に方にもよるが、win2000だと、ローカルのユーザでログインだったように思う。(OSのバージョンや設定によるとは思うけどね)
で、ログイン後に、重大なエラーが発生したので、シャットダウンするとかいうメッセージが表示されて、60秒のシャットダウンタイマーが起動する。で、カウントダウンが0になったら、システムがシャットダウンされる。

当然、また起動しても死ぬので、なんとかしてバグったLSPを除去しないといけない。
このままでは、行きてすぐ死んでまた生きるという、無限ザオリク状態であり、これでは電力の無駄遣いだ。
レスキューしなくてはいけない。


OSが変な死に方をすると、出てくるのがSAFEモードだ。
最近のwindowsはとても安定していて、めったにSAFEモードを見ることが無くなったが、昔はいろいろお世話になったものだ。
SAFEモードでは、ドライバ等をできる限り排除した最低限の環境でwindowsが動く。(ようするに、LSPみたいな危険なプログラムをできるだけ止めるという方針だな)
このSAFEモードでバグったLSPを除去するプログラムを動かせば回復する・・と思うかもしれないが、そうとも言い切れない。
LSPのバグらせ方によっては、SAFEモードにすらも入れなくなるのだ。
SAFEモードでも通信はするので、winsockが読み込まれるため、バグったLSPが読み込まれて、SAFEモードがクラッシュしてしまうことがある。
(今のwindowsだと、LSPも無効になるのかなどうなのかな?)


SAFEにも入れなくなると、winsockを読み込まないコマンドラインモードとかを使うか、諦めてディスクをリストアするしかない。コマンドラインでも速攻LSPを除去できるように、C:\a.batとか作っておいていたものだ。
こーゆー開発は、普通は、VMを使って開発する感じになると思う。
(普通に実機開発して泣いていたのは、恥ずかしいから秘密だ。)


ちなみに、VisualStudioには、ネットワーク経由でプログラムをデバッグする機能がある。
こーゆー実機でテストしづらいものには大変便利ではあるが、LSPのテストでは使えない。
LSPは、ネットワークで動くすべてのプロセスをフックしてしまう。
そのため、ネットワークデバッグ自体もフックしてしまって、とてもおもしろい状態になる。LSPは、ネットワークデバッグできない。
、というかできんかった。当たり前だ。


やはり、フックルーチンはとても難しい。
OSすらも巻き込んで落としてしまうぐらいの威力がある。
なにか間違えると、即死である。そして、間違える可能性がとても高い複雑なルーチンを書くことが要求される。
フックは、地雷原でフラダンスを踊っているようなものだ。
とても危険だ。(人間がフックを楽に書ける日がくるのだろうか。)


こんなに大変なフックなのに、なぜ人はフックを書くのだろうか。
やはり、フックを使ったメリットが大きいためだろう。
フックのメリットは、とても大きく、普通はできないような処理ができてしまう。
フックを使えば、ソースコードが公開されていないアプリケーションの機能を自在に拡張できたりもする。
このアプリケーションはとても素敵だけど、こんな機能もついでにあると嬉しいよね、みたいなことがフックを使えば可能になる。
より皆に愛される気の利いたアプリケーションを作ることができる。
この蜜はとても甘い。


例えば、windowsマウスジェスチャだったり、便利なユーテリティや、アンチウイルスだったり、セキュリティソフトであったりは、まさにフックの恩恵の上に成り立っている。
これはwindowsだけではなく、例えばlinuxでは、RPMdebパッケージを自動で作ってくれる checkinstall とかがフックを大胆に使って動いている(checkinstall は、 LD_PRELOAD でやっているはず・・・)

こんな機能があったらいいのに・・・という拡張欲をフックは満たしてくれる。
フックを使えば、一層上のUXを体験できる。
一味違ったアプリケーションを作れる。
そんな体験が面白いから、人は、今日も死(クラッシュ)を恐れないでフックルーチンを書くのだろう。