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番地)に書き換えればよい。