messageソフトウェア開発の仕事をしているsimotinといいます。GDB結構前に買っていたセキュリティコンテストチャレンジブックという本を最近になって読んで、脆弱性のあるコードを動かしたりして遊んでいました。見るスタックオーバーフローとかSEGVの結果が本で書いていた通りにはならなかったのでとりあえずgdbを使ってアセンブラとかスタックの状態を見て386の動作を思い出すための復習をしてみました。復習がてらメモを取りたかったので、どうせならと思いブログに書いておきたいと思います。LT;DR;※ちゃんとまとめれていないと思うので後から整理したいと思います。gdbinitアセンブラの解析目的でgdbを使う場合は.gdbinitを整備しておいた方が色々と便利です。長くなるコマンドやよく使うコマンドに省略系(alias)をつけておくと余計なタイプをしなくて済むのでコードの解析に集中できます。普段デバッグにはgdbを使いません(printfデバッグが多い)のでカスタム度合いは低いですが、私の.gdbinitはこんな感じです。onネットで調べた内容からよさげなものを流用させてもらってる感じです。アセンブラの勉強用サンプルとりあえずスタックの状態とか、関数の呼び出しと復帰を思い出したかったので簡単な関数を解析してみます。0;}32bitのバイナリを出力させる64bit環境で普通にGCCとかでビルドすると、当然ですが64bitのバイナリが出力されます。アセンブラなのですが、セキュリティ関係の書籍ではx86(32bit)のアセンブラを前提として解説が書かれていることが多いので、勉強のために32bitのバイナリを出力できるようにします。いうオプションが必要です。このオプションを動かすためにはいくつか32bit用のライブラリ等をインストールしておく必要があるようです。※インストールしていないと必要なヘッダファイルが見つからなかったりリンクエラーが出たりします。指定してもエラーにはなりません。./a.outまずはメインでブレークを貼って実行します。0x3の行が見えます。4と3というのはソースコード上のaddの引数に対応しているというのはすぐにわかると思います。引数は後ろから前へとスタックに積まれているのが面白いですね。各レジスタの状態(特にスタックポインタとプログラムカウンタ)を意識しておくと何が起こっているのかが理解しやすくなります。0x3が実行された時点でスタックの状態は、のような状態になっています。0x3の後に進み、スタックの状態を出力してみます。スタックに載っています。gdbのダンプは直感的ではないため、頭の弱い私は(真面目に解析をするときは)紙にスタックの状態を書きながらデバッガを動かしています。ここまでで引数をスタックに積むところまではアセンブラレベルでイメージできました。関数の呼び出しに入ります。callの次の命令のアドレスが自動的にスタックに積まれます。プログラムカウンタがcallのジャンプ先のアドレスに変わります。いう値が格納されています。esp,0x8の命令行のアドレスであることがアセンブラのコードを見ると分かります。要するにC言語レベルだと関数を呼び出した後の行の場所をスタックに積んで覚えておくという形ですね。スタックオーバーフロースタックオーバーフローの脆弱性は、このスタックに積まれた「関数を呼び出した後の行の場所」を呼び出し先の関数を実行中に書き換えることで、関数から戻ってくるときに、思いもよらぬ場所から処理が始まるということです。32bit(i386)にはMMUを使った仮想メモリの機構があるので、悪意のあるコードの場所へジャンプさせようとしても、そのコードが同一セグメント内になければセグメンテーションフォルト(通称SEGV・セグフォ・セグ落ち)でプログラムが停止するのが普通です。やってスタックオーバーフローの脆弱性をついて悪意のあるコードを実行させるのかというとそれは自分もよく知らないのでこれからもう少し勉強してみます。セグメント機構がある以上、バイナリを改ざんするしかないと思うのですが、具体的にどうやってやるのかわかっていません。レジスタの値をスタックに積んでいます。要するにベースポインタで現在のスタックの底となるアドレスを保持するレジスタです。ローカル変数にアクセスする時とかはこのアドレスからのオフセットを使って値を場所を指定します。ebp,espはスタックポインタの値でベースポインタを更新しています。関数呼び出しの直後に、ここからが新しいフレームだよということでスタックフレームを切りなおすというイメージですね。[ebp+0xc]という命令が続いています。edxに[ebp+0x8]と[ebp+0xc]にある値をコピーしています。+0xcは要するに、スタックの中身です。何が入っているかというと、3と4になります。addは文字通り加算命令です。edxの値を足すという命令で結果はeaxに格納されます。していますが、スタック上に退避されていたebpの値をレジスタに復帰させています。※今回解析したadd関数内では、ローカル変数を使っていないので、ebpを切りなおしたもののスタックにはそれ以上何も積まれることはありませんでした。命令で呼び出しもとに戻ります。呼び出し元に戻るといっても、ret命令はスタックトップの値を取り出し、プログラムカウンタに設定するという動作をするだけです。0x0とか積まれてたら0x0番地にアクセスしようとしてSEGVが発生します。まとめだらだらと長くなってしまいましたが386の関数呼び出し・復帰の基本的な流れを思い出すために書いてみました。これだけだとスタックオーバーフローからの攻撃の原理までは分からないので、攻撃については続けて勉強が必要。スタックの状態に関しては「今何が入っているのか」がグラフィカルに表示できるツールが欲しい。ptrace系のAPIとかelectronとかと連携させればカッチョよさげなのが作れると思いますが、時間と根性と体力がなくて作れていません。electronアプリ作る能力が注射打ったり薬飲んだり、あるいは自動販売機にお金払ったりしたら身につくというようなことができればと思います。現実はそうは行かないので、地道な勉強とか、少しでも手を動かすというのが大切なんですよね・・・楽しくもあり、つらくもあり。23:37gdbを使って386アセンブラの復習をするTweet広告を非表示にする関連記事2018-05-31スタックの状態を可視化するWebツールを書いてみた。Webページ上でStackの状態を可視化するコードを書いてみました...2018-03-16レジスタの値を取得するx86のアセンブラ関数を書いてみた前回書いたPINE64+の続きとして、とりあえずBROMの処理が終わっ...2018-01-05LLVMのビルドについてまとめてみる2018年早々、LLVMのビルドで消耗しておりました。mcommit.hatenadiary.comARMの命令では最上位4ビッ...2018-03-16レジスタの値を取得するx86のアセンブラ関数を書いてみた前回書いたPINE64+の続きとして、とりあえずBROMの処理が終わっ...2018-02-28PINE64のBROMのコードをダンプして逆アセンブルしてみた。記事はさっきあげましたが、昨日(2/26)Lチカを...2017-02-18ビルドしたモジュールに使われているDWARFのバージョンを調べる最近デバッガに興味を持ってDWARFのフォーマットについて調べて...2014-07-03モトローラSレコードフォーマットをRubyで吐く。(4)月別アーカイブはてなブログをはじめよう!simotin13さんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。