ハッカーくろちゃんが学ぶ、スタックフレームとRBP(episode-07)
これまでの話で、私たちは「スタック」というメモリ領域について学んできました。関数が呼び出されるとスタックに積まれ、処理が終わると元の場所に戻る。この一連の流れは、プログラムが正しく動くための大前提です。
しかし、ここで一つ疑問が生まれます。
関数はどうやって、「自分の変数はどこか」「どこまでが自分の領域か」を判断しているのでしょうか。
その答えが、今回のテーマであるスタックフレームとRBP(ベースポインタ)です。
この2つは、GDBでスタックを読む、バッファオーバーフローを理解する、ROPを組み立てるといったすべての基礎になります。
[fuki-r]スタックの中にも“区切り”があるってこと?[/fuki-r][fuki-l]ええ。その区切りを理解するのが、今日のゴールよ[/fuki-l]
※イメージです。
スタックフレームとは何か
スタックフレームの正体
スタックフレームとは、
「ある関数が実行されている間だけ使う、スタック上の専用領域」です。
関数が呼ばれるたびに、
“引数"、"ローカル変数"、"保存されたレジスタ"、"リターンアドレス"といった情報が、ひとまとめで確保されます。
この「ひとまとめ」が、スタックフレームです。
なぜフレームが必要なのか
もしスタックフレームが存在しなければ、「複数の関数が、同じスタックを無秩序に使う」ことになり、どの変数がどの関数のものなのか分からなくなります。
つまり、スタックフレームは関数ごとの作業スペースを明確に分けるための仕組みなのです。
[fuki-r]関数ごとに“自分の机”がある感じですね[/fuki-r][fuki-l]その通り。机があるから混乱しないのよ[/fuki-l]
スタックフレームの内部構造
スタックフレームの中身
一般的な関数のスタックフレームは、次のような要素で構成されます。
・上位アドレス側
・引数
・中央
・リターンアドレス
・保存されたRBP
・下位アドレス側
・ローカル変数
スタックは「上から下へ伸びる」ため、ローカル変数はアドレスが小さい方向に配置されるのが特徴です。
フレームは関数ごとに積み重なる
関数Aが関数Bを呼び出すと、
1.Aのスタックフレームが存在
2.その上にBのスタックフレームが積まれる
3.Bが終わると、Bのフレームだけが破棄される
この積み重なりが、
コールスタックと呼ばれる状態です。
[fuki-r]箱の上に、さらに箱が積まれていく感じですね[/fuki-r][fuki-l]ええ。だから“スタック(積み重ねる)”と呼ばれるの[/fuki-l]
RBPとは何か
RBPの役割
RBP(Base Pointer)は、「今の関数のスタックフレームの基準点」を指すレジスタです。
RBPがあることで、プログラムは、ローカル変数の位置や、保存された情報の位置を、安定して参照できます。
なぜRSPだけでは足りないのか
スタックポインタであるRSPは、
・push
・pop
関数呼び出し
などで、常に値が変化します。
もしRSPだけを頼りにすると、ローカル変数の位置が毎回ずれてしまいます。
そこで、
・RSPは「今のスタック先頭」
・RBPは「この関数の基準点」
という役割分担が生まれました。
[fuki-r]RBPは“原点”みたいなもの?[/fuki-r][fuki-l]ええ。そこから距離で位置を測るの[/fuki-l]
関数プロローグとRBPの設定
関数が始まるときに起きていること
多くの関数は、冒頭で次のような処理を行います。
1.以前のRBPをスタックに保存
2.RBP ← 現在のRSP
3.ローカル変数分の領域を確保
これを 関数プロローグ と呼びます。
これが意味すること
この処理により、「この関数のRBP」が固定され、ローカル変数は、"RBP – オフセット"という形で参照されます。
GDBで"info frame"や、"x/gx $rbp"を確認すると、この構造が見えてきます。
[fuki-r]最初に“基準点”を決めてから作業するんですね[/fuki-r][fuki-l]だから後で迷子にならないのよ[/fuki-l]
エクスプロイト開発との関係
なぜ攻撃者はRBPを理解するのか
バッファオーバーフローでは、"ローカル変数"、"保存されたRBP"、"リターンアドレス"が、同じスタックフレーム内に並んで存在します。
つまり、「どこまで書き込むと何が上書きされるのか」を理解するには、スタックフレーム構造の理解が不可欠です。
RBPを知ると見える世界
RBPを理解すると、
・オフセット計算
・ペイロード長の決定
・スタックレイアウトの把握
が、論理的に説明できるようになります。
これは「勘」ではなく、構造に基づくエクスプロイトへの第一歩です。
[fuki-r]攻撃も、仕組みを知るところからなんですね[/fuki-r][fuki-l]ええ。理解が深いほど、再現性が生まれるの[/fuki-l]
ハッカーくろちゃんが学ぶ、スタックフレームとRBPのまとめ
スタックフレームは、関数ごとの専用作業領域。RBPは、そのフレームの基準点。
RBPがあるから、ローカル変数を安定して参照できます。
エクスプロイト開発では、フレーム構造の理解が成功率を左右する
スタックフレームとRBPを理解したあなたは、「スタックを眺める側」から「スタックを読み解く側」へ進みました。