投稿

10月, 2020の投稿を表示しています

10月28日(水)1コマ目

イメージ
今日、やったこと バッファオーバーフロー2(リターンアドレス書き換え) 今日のホワイトボード プログラムを実行すると プログラムを実行すると、コンパイルで出力された実行ファイル(実行命令のリスト)がメモリ上のコードエリアにロードされ、順番に命令が実行される。 図 コードエリア上の命令(左側) 関数を呼び出すと コードエリア上では関数の命令にジャンプする。 ジャンプ先の命令を順番に実行し、関数呼び出し元に戻る。このとき、どこに戻ればいいかを関数実行時にメモリのスタックエリア上に確保されたエリアに書き込む。 図 スタックエリア上のリターンアドレス なお、スタックエリアは関数呼び出し時にエリアが確保され、 引数 ローカル変数 関数実行時に退避すべきデータ(リターンアドレスはこれ) のためのエリアが確保され、データが保存される。 デバッガ上でサンプルプログラムを確認 デバッガの機能を使って、サンプルプログラムのアセンブラコードを出力してみる。 main()関数の最初の命令がメモリのコードエリアの0x・・40075a番地にあるかがわかる。 また、0x・・4007a0番地にはcheck()関数へジャンプする命令があることもわかる。 図 デバッガで確認したコードエリアのアドレス check()関数は check()関数の最初の命令はメモリの0x・・4006b6番地にある。ここから順に命令を実行し、main()関数へ戻る。戻る先は0x・・4007a5番地(「check()関数へジャンプ」の次の命令)。この戻るアドレスはスタックエリア上のリターンアドレスに書き込まれており、この値を参照してmain()関数に戻る。 図 リターンアドレス check()関数の結果を無視する プログラムでは  if(check(argv[1])) {   認証成功  } else {   認証失敗  } となっている。check()関数から戻る際に参照するリターンアドレスを書き換えて、check()関数の戻り値に関係なく、認証成功にする。 図 リターンアドレスを書き換える 図のように、リターンアドレスを書き換えるにはcheck()関数のstrcpy()で変数pass_buffの先頭から41バイト目以降を認証成功時の実行する命令のアドレス(0x・・4007a9番地)に書き換えればよい。

10月21日(水)1コマ目

イメージ
今日、やったこと Docker導入 前回の復習 Dockerについて 簡単に言うと、Windows上の1プロセスをLinux(CentOS)と同じ内容で動かす。このプロセス上ではLinux(CentOS)とまったく同じ処理をすることができる。 が、完全なLinuxの仮想化ではないため、使えるのはLinux上の一部の機能のみ。ただしこの機能も動かすLinuxを構築する際に変更できる。 図 Docker上のLinuxのイメージ 今後のこの授業では USBで起動したLinuxを利用するスタイルはやめます。 以下の手順で使ってください。 ①Docker上のLinux起動 以前、共有フォルダからコピーしてもらった「 start_centos_gdb.bat 」をダブルクリック。 コマンドプロンプトが起動して、最終的にLinuxのプロンプトになる。 ②Zドライブを接続 /homeディレクトリ以下にZドライブを接続(UNIX的に言うとマウント)します。 [root@docker-desktop /]# mountZ password for 自分のユーザー名@//172.16.4.2/xxx: Windowsログイン時のパスワード これで、/home以下にZドライブが接続されます。 ③サンプルプログラム作成 Windows上でできます。 文字コードはUTF-8で保存してください 。 Zドライブに保存するとLinux側でもアクセスできます。 ④コンパイル、実行、デバッガ実行 Linux上で起動します。 今日のホワイトボード 前回の復習 Docker上のLinuxに慣れるため、前回やった内容を再度なぞってみました。 結局、サンプルプグラム「bof_sample1.c」のcheck()関数ではstrcpy()で意図した変数だけでなく、別の変数のメモリエリアも上書きしてしまい、おかしな振る舞いをしてしまうのでした。 図 メモリ上の2つの変数 次回はサンプルプログラム「bof_sample2.c」を使ってもう少しディープな世界に行きます。

10月14日(水)1コマ目

イメージ
きょう、やったこと バッファオーバーフロー 今日のホワイトボード コンピュータのメモリ とりあえず下図の3つのエリアをおさえてください。 図 コンピュータのメモリ バッファオーバーフロー コンピュータシステムの重大インシデントの要因ナンバーワン(と思われる)がバッファオーバーフロー。C言語特有の「メモリ操作が簡単にできる」+「メモリ保護がない」から簡単に発生する。いやらしいのは発生するパターンが限定されるケースが多いこと。(再現性が高くない) サンプルプログラムについて (一応)認証を行うプログラム。 コマンドライン引数にて、認証に使うパスワードを指定して実行。 なお、認証OKなパスワードは「SamplePassword」。 しかしながら、パスワード「aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa」(aを30個)を指定すると、認証OKとなる。 原因はバッファオーバーフローによるメモリ破壊。実際にメモリがどうなっているかをデバッガを使って確認してみる。 バッファオバーフローはstrcpy()で発生している デバッガで、strcpy()直後にブレークポイントを設定。 ブレークポイントで停止後、2つの変数(auth_flag、password_buffer)の中身を見てみると、以下のようになっていた。 図 strcpy()後のメモリの様子 おかしな動きをした理由はいくつかの要素が複合的に重なっているためです。 要素1 strcpy() strcpy()は   コピー先のサイズに関係なく、コピー元の先頭から'\0'までをコピーする ため、password_bufferのエリアを超えて、auth_flagのエリアまで上書きをしてしまった。 要素2 check()の戻り値 変数auth_flagの値を返しているが、auth_flagは 初期値0で宣言 strcmp()で”SamplePassword”と一致したら、1を代入 の2か所で代入を行っている。 strcpy()でバッファオーバーフローして上書きされても正しい値を代入することがない。 要素3 c言語のif() カッコ内が  0なら条件不成立  0以外なら条件成立 です。c言語にはtrue、falseがないため、このような仕様になっています。 よって、check()関数の戻り値が0以外なら認証...