
■ 何故強制終了されるのか? ■
前回は、バッファオーバーフローにより何の情報が壊されてし
まうのか説明しました。
前回見てもらったイメージをもう1度見てみましょう。
レジスタESP
スタック領域├─────┤ ┌─────┐
│ i │←───┤ │●メイン関数で
├─────┤ └─────┘ 使用するスタ
│ │ ック領域の1
│ │ 番上を示す。
│バッファb│
│ │
│ │
├─────┤
│バッファa│
│ │ レジスタEBP
├─────┤ ┌─────┐
│ ebp │←───┤ │●メイン関数で
├─────┤ └─────┘ 使用するスタ
│呼出し元 │ ック領域の1
└─────┘ 番底を示す。
これはメイン関数がスタック領域を初期化した直後のイメージ
です。
この時点ではまだバッファオーバーフローは発生していません。
それでは、コピー処理が完了した直後のイメージを見てみまし
ょう。
レジスタESP
スタック領域├─────┤ ┌─────┐
│ i │←───┤ │●メイン関数で
├─────┤ └─────┘ 使用するスタ
│ │ ック領域の1
│ │ 番上を示す。
│バッファb├─┐
│ │ │
│ │ │コピー
├─────┤ │
│ │←┘
│ │ レジスタEBP
│バッファb│ ┌─────┐
│ │←───┤ │●メイン関数で
│ │ └─────┘ 使用するスタ
│ │ ック領域の1
└─────┘ 番底を示す。
バッファオーバーフローが発生し、「ebp」と「呼出し元」の
情報が上書きされています。
この時点ではまだプロセッサの保護機能は動作しません。
何故なら、バッファオーバーフローが発生した事にプロセッサ
は気付く事が出来ないからです。
それでは、このプログラムが強制終了させられる契機は何なの
でしょうか?
今度は具体的な値が設定されているイメージで見てみましょう。
まずはバッファオーバーフロー発生前です。
●バッファオーバーフロー発生前
スタック領域
┌┌────┐
││ 10 │
││ 11 │
││ 12 │
バッファb││ 13 │
││ 14 │
││ 15 │
││ 16 │
││ 17 │
││ 18 │
││ 19 │
││ 1A │
││ 1B │
││ 1C │
││ 1D │
││ 1E │
││ 1F │
││ FF │
││ FF │
││ FF │
││ FF │
├├────┤
││ 0 │
││ 1 │
││ 2 │
││ 3 │
バッファa││ 4 │
││ 5 │
││ 6 │
││ 7 │
└├────┤
│ │
│ebp │
│ │
│ │
├────┤
│ │
│呼出し元│
│ │
│ │
└────┘
次にバッファオーバーフロー発生後のイメージを見てみましょ
う。
●バッファオーバーフロー発生後
スタック領域
┌┌────┐┐
││ 10 ││
││ 11 ││
││ 12 ││バッファ「b」のサイズ分
バッファb││ 13 │├─┐  ̄
││ 14 ││ │
││ 15 ││ │
││ 16 ││ │
││ 17 ││ │
││ 18 ││ │
││ 19 ││ │
││ 1A ││ │コピー
││ 1B ││ │
││ 1C ││ │
││ 1D ││ │
││ 1E ││ │
││ 1F ││ │
││ FF ││ │
││ FF ││ │
││ FF ││ │
││ FF ││ │
├├────┤┤ │
││ 10 ││ │
││ 11 ││ │
││ 12 ││ │
││ 13 ││ │
バッファa││ 14 ││ │
││ 15 ││ │
││ 16 ││ │
││ 17 ││ │
├└────┘│ │
│× 18 ×│ │
ebp│× 19 ×│←┘
│× 1A ×│
│× 1B ×│
├××××××│
│× 1C ×│
呼出し元│× 1D ×│
│× 1E ×│
│× 1F ×│
└××××××│
× FF ×│
× FF ×│
× FF ×│
× FF ×┘
バッファオーバーフローが発生しました。
ここでちょっと「呼出し元」の情報に注目してみましょう。
この情報は何かというと、メイン関数の処理が「終了した後に」
どこに制御を移行させるか、という情報を表しています。
以下に、バッファオーバーフローが「発生しない」場合のプロ
グラムの動作イメージを示します。
「呼出し元」の情報がどのように使用されているかに気を付け
ながらご覧ください。
┌──────────────────────────┐
│ プログラム │
│ │
└───┬──────────────────────┘
│ スタック領域
│call命令 格納 ┌──────┐
├─────────────────────→│ 呼出し元 │
│ 制御を移行┌────────┐ └──────┘
├────────→│ main関数 │
│←──┐ │ │
│ │ └───┬────┘
│ │ │
続きの処理 │ ┌──┴──┐
・ │ │コピー処理│
・ │ └──┬──┘
・ │ │処理終了 スタック領域
・ │ │どこへ戻るか参照┌──────┐
│ 呼出し元に戻る │←───────┤ 呼出し元 │
└─────────┘ └──────┘
「call命令」により、スタック領域に「呼出し元」の情報が格
納され、メイン関数に制御が移行されます。
そしてメイン関数の処理が終了した後に、スタック領域にある
「呼出し元」の情報を参照しています。
プロセッサはこれによりどこへ戻れば良いかを認識し、「呼出
し元」で示されている場所へ制御を移行させます。
「呼出し元」の情報がどのように使用されるか分かりましたね?
それでは今度はバッファオーバーフローが「発生する」場合の
プログラムの動作イメージを見てみましょう。
先ほどのイメージとは、コピー処理が完了した後の動作が異な
ります。
┌──────────────────────────┐
│ OF1.EXE │
│ │
└───┬──────────────────────┘
│ スタック領域
│call命令 格納 ┌──────┐
├─────────────────────→│ 呼出し元 │
│ 制御を移行┌────────┐ └──────┘
├────────→│ main関数 │
│ │ │
│ └───┬────┘
│ │
┌──┴──┐
│コピー処理│
└──┬──┘
│×バッファオーバーフロー発生
│ スタック領域
│ ┌──────┐
├───────→│1F 1E 1D 1C │
│ └──────┘
│処理終了 スタック領域
│どこへ戻るか参照┌──────┐
│←───────┤1F 1E 1D 1C │
│ └──────┘
×××××××
×そんな所に×
×実行可能な×
×プログラム×
×ありません×
×××××××
「call命令」により、スタック領域に「呼出し元」の情報が格
納され、メイン関数に制御が移行されます。
ここまでは先ほどのイメージと同じです。
ところが、メイン関数のコピー処理においてバッファオーバー
フローが発生してしまいます。
これにより「呼出し元」の情報がおかしな値に書き換えられて
しまいました。
「呼出し元」の情報がどういう値に書き換わってしまったのか
を覚えておいてください。
覚えたら、ちょっと実験してみましょう。
もう1度、以前作成したプログラム"OF1.EXE"を実行してみて
ください。
ただし、今度は「不正な処理を行ったので強制終了します。」
と表示されたら、「閉じる」を選択せずに、「詳細情報」のボ
タンを押してみてください。(WindowsXPの方は、「エラー報
告に含まれるデータの参照」の項目をクリックし「エラー報告
に関する技術情報」の項目をクリックしてみてください。)
この詳細情報の中には、不正な処理を行ったアドレス情報が表
示されるようになっています。
以下に"OF1.EXE"をWindows98にて実行した場合の詳細情報を示
します。
─↓ここから──────────────────────
OF1 のページ違反です。
モジュール : <不明>、アドレス : 0084:1f1e1d1c
Registers:
EAX=00000015 CS=017f EIP=1f1e1d1c EFLGS=00010212
EBX=00530000 SS=0187 ESP=0063fe00 EBP=1b1a1918
ECX=0063fe00 DS=0187 ESI=816d9158 FS=66ef
EDX=0063fdff ES=0187 EDI=00000000 GS=6e1e
Bytes at CS:EIP:
─↑ここまで──────────────────────
アドレス情報の所が「1f1e1d1c」となっています。
先ほど覚えてもらった、「呼出し元」の情報と同じ値ですね。
この事は、メイン関数の処理が完了して、「呼出し元」に戻ろ
うとしたという事を表しています。
つまり「1f1e1d1c」という場所にある命令を実行しようとした
けれども「実行可能なコードが存在しない」ので、インテルプ
ロセッサが怒ってしまった、という事です。
ここで気を付けて頂きたい事は、インテルプロセッサは「バッ
ファオーバーフローが発生した」から怒っているのではなく、
「呼出し元のコードを実行しようとしたけど出来ませんでした
よ。」と言って怒っているという事です。
それでは、「呼出し元のコードを実行しようとしたけど出来ま
せんでしたよ。」とプロセッサに言わせないようにした場合、
どうなるでしょうか?
次回はインテルプロセッサが怒ってしまわないようにプログラ
ムを改造してみる予定です。