分かりやす〜い
コンピュータ技術情報

TOPに戻る
▼Processor
バッファオーバーフロー
┣ 異常な動作
┣ 本当に怖い事
┣ プログラムを作る
┣ バッファオーバーフロ
┃ ーを発生させる

┣ スタック領域の構成
┣ 何故強制終了されるの
┃ か?

┣ 強制終了されないよう
┃ に作り変える

┣ 任意のコードを実行す
┃ る

┣ of1.cとof2.cとの違い
┣ 例外
┣ 例外ハンドラの行う事
┣ of3.cを作る
┣ of1.cとof3.cとの違い
┣ of3.exeの例外ハンド
┃ ラ

┣ of3.exeを強制終了す
┃ るのは?

┣ 例外ハンドラの特徴
┣ Blue Screen
┣ BufferOverFlow応用
┃ プログラム

┗ 機械語の説明

Copyright(C) 2001-2002.ugpop. All rights reserved.




■デジタル用語辞典:
■ 強制終了されないように作り変える ■

前回はバッファオーバーフローにより、「呼出し元」の情報が
どのような値に書き換わってしまうのかを実際に見てみました。

そしてインテルプロセッサは「バッファオーバーフローが発生
した」から怒っているのではなく、「呼出し元のコードを実行
しようとしたけど出来ませんでしたよ。」と言って怒っている
事も説明しました。

前回のプログラム動作イメージをもう1度見てみましょう。


┌──────────────────────────┐
│         OF1.EXE          │
│                          │
└───┬──────────────────────┘
    │                       スタック領域
    │call命令               格納 ┌──────┐
    ├─────────────────────→│ 呼出し元 │
    │    制御を移行┌────────┐   └──────┘
    ├────────→│ main関数 │
    │         │        │
    │         └───┬────┘
    │             │
               ┌──┴──┐
               │コピー処理│
               └──┬──┘
                  │×バッファオーバーフロー発生
                  │         スタック領域
                  │        ┌──────┐
                  ├───────→│1F 1E 1D 1C │
                  │        └──────┘
                  │処理終了     スタック領域
                  │どこへ戻るか参照┌──────┐
                  │←───────┤1F 1E 1D 1C │
                  │        └──────┘
               ×××××××
               ×そんな所に×
               ×実行可能な×
               ×プログラム×
               ×ありません×
               ×××××××


今回はインテルプロセッサが怒り出さないようにプログラムを
改造してみます。

まず、どうすればインテルプロセッサが怒らないようになるの
か、という事から説明します。

前回説明したように、インテルプロセッサは「バッファオーバ
ーフローが発生した」から怒っているのではなく、「呼出し元
のコードを実行しようとしたけど出来ませんでしたよ。」と言
って怒っているのでした。

それでは「呼出し元」の情報を、「1F 1E 1D 1C」等というわ
けの分からない値でなく、「実行可能なコード」が格納されて
いる場所を示すように変更してあげれば怒らなくなるのではな
いでしょうか。

以下に怒り出さないように変更を加えた後の動作イメージを示
します。
先ほどのイメージとはバッファオーバーフローが発生した後の
動作が異なります。


┌──────────────────────────┐
│       変更を加えた後のプログラム      │
│                          │
└───┬──────────────────────┘
    │                       スタック領域
    │call命令               格納 ┌──────┐
    ├─────────────────────→│ 呼出し元 │
    │    制御を移行┌────────┐   └──────┘
    ├────────→│ main関数 │
    │         │        │
    │         └───┬────┘
    │             │
               ┌──┴──┐
               │コピー処理│
               └──┬──┘
                  │×バッファオーバーフロー発生
                  │         スタック領域
                  │        ┌──────┐
                  ├───────→│実行可能な │
                  │        │コードの場所│
                  │        └──────┘
                  │処理終了     スタック領域
                  │どこへ戻るか参照┌──────┐
                  │←───────┤実行可能な │
                  │        │コードの場所│
                  │        └──────┘
                  │  ┌───────┐
                  └─→│ 実行可能な │
                     │ コード実行 │
                     └───────┘
                              

「呼出し元」の情報が格納されていた場所が、バッファオーバ
ーフローにより「実行可能なコードの場所」に書き換えられて
います。

これにより、メイン関数の処理が終了する時に「実行可能なコ
ードの場所」に制御が移行され、指定したコードが実行される
ようになるはずです。


それでは実際に上のイメージの様に動作するか試してみましょ
う。

以前作成したプログラム"of1.c"をコピー&貼り付けして、新
しく出来たファイルを"of2.c"というファイル名に変更してく
ださい。

今回は"of2.c"ファイルに変更を加えていきます。

どのように変更を加えるかというと、新しく「sub」という関
数を作成し、メイン関数の処理が終了した時に「sub」関数が
実行されるようにします。

"of2.c"ファイルを以下のように変更してみて下さい。
"of1.c"との差分がある部分にはコメントを付けておきます。


─↓ここから──────────────────────
#include <stdio.h>
#include <string.h>
#include <stdlib.h> //←●追加
void main(void);
void sub(void); //←●追加

void main()
{
  unsigned char* i;
  unsigned char b[]= { 0x10, 0x11, 0x12, 0x13,
             0x14, 0x15, 0x16, 0x17,
             0x18, 0x19, 0x1a, 0x1b,
             0x1c, 0x1d, 0x1e, 0x1f,
             0xff, 0xff, 0xff, 0xff,
          };
  unsigned char a[]= { 0x0, 0x1, 0x2, 0x3,
             0x4, 0x5, 0x6, 0x7,
          };

  // スタック領域を表示する。
  for(i=&b[0];i<=&a[7];i+=4)
  {
    printf("%08X:%02X %02X %02X %02X\n", i, *i, *(i+1), *(i+2), *(i+3));
  }
  // バッファbをバッファaにコピーする。
  strncpy((char*)a, (char*)b, sizeof(b));
  printf("copy end\n");
  // スタック領域を表示する。
  for(i=&b[0];i<=&a[7];i+=4)
  {
    printf("%08X:%02X %02X %02X %02X\n", i, *i, *(i+1), *(i+2), *(i+3));
  }
  printf("&sub:%08X\n", &sub); //←●追加
}
// ●↓ここから追加
void sub(void)
{
  printf("Overflow call!!\n");
  exit(0);
}
// ●↑ここまで追加
─↑ここまで──────────────────────


変更部分は4箇所だけです。
まず上から3行目の部分に

#include <stdlib.h> //←●追加

というのがあります。
これは後で出てきますが、「exit」というC言語の標準関数を
呼出す為に必要になります。
この記述を追加する事により「exit」関数を呼出す事が出来る
ようになります。

次に、2行下の方に、

void sub(void); //←●追加

というのがあります。
これは、「sub」という関数を呼出しますよ、という宣言です。

そして1番下の方に、

printf("&sub:%08X\n", &sub); //←●追加

というのがあります。
ここでは、「sub」という関数がメモリ上のどこにあるか、と
いうアドレス情報を表示させるようにしています。

最後に、

// ●↓ここから追加
void sub(void)
{
  printf("Overflow call!!\n");
  exit(0);
}
// ●↑ここまで追加

というのがあります。
ここで「sub」関数を定義しています。
この関数は「Overflow call!!」という文字列を表示して、
「exit」関数を呼出すだけの処理です。
「exit」関数というのは、プログラムを終了させる関数です。


ここまで変更したところで、とりあえず1度実行してみます。
上記の変更を加えただけではまだ「sub」関数が実行される事
はありませんが、これにより「sub」という関数がメモリ上の
どこにあるのか、というアドレス情報を知る事が出来ます。

それでは、とりあえずビルド&実行させてみましょう。
例によって、このプログラムもバグを含んだ物ですので、自己
責任において実行させてください。


実行結果を以下に示します。

─↓ここから──────────────────────

0063FDDC:10 11 12 13
0063FDE0:14 15 16 17
0063FDE4:18 19 1A 1B
0063FDE8:1C 1D 1E 1F
0063FDEC:FF FF FF FF
0063FDF0:00 01 02 03
0063FDF4:04 05 06 07
copy end
0063FDDC:10 11 12 13
0063FDE0:14 15 16 17
0063FDE4:18 19 1A 1B
0063FDE8:1C 1D 1E 1F
0063FDEC:FF FF FF FF
0063FDF0:10 11 12 13
0063FDF4:14 15 16 17
&sub:004020D9

─↑ここまで──────────────────────

「不正な処理を行ったので強制終了します。」と表示されたら、
「閉じる」を押して終了させてください。

ここで1番注目して頂きたいのが、最後の行にある、

&sub:004020D9

です。
これは「sub」という関数が「メモリ上のどこにあるか」を示
す、アドレス情報になります。
この実行結果はWindows98の物ですが、環境によりさまざまな
値になると思います。

「sub」という関数は正常に処理が行える「実行可能なコード」
です。

そして、この「sub」という関数がメモリ上のどこにあるのか
も分かりました。

後はこれを「呼出し元」の部分に設定してあげれば、インテル
プロセッサが怒り出す事は無くなるはずです。

続きは次回に。



▲このページの上へ

▲このページの上へ

▲このページの上へ

←前に戻る    ▲このページの上へ    続きを読む→

▲このページの上へ