【注意】このサイトに記載されていることを他人に試すことは「不正アクセス禁止法」に該当する場合があります。詳しくはこちらから

ハッカーくろちゃんが学ぶ、スタックフレームとRBP(episode-07)

黒ネコと学ぶ・論理的エクスプロイト開発への道

これまでの話で、私たちは「スタック」というメモリ領域について学んできました。関数が呼び出されるとスタックに積まれ、処理が終わると元の場所に戻る。この一連の流れは、プログラムが正しく動くための大前提です。
 
しかし、ここで一つ疑問が生まれます。
関数はどうやって、「自分の変数はどこか」「どこまでが自分の領域か」を判断しているのでしょうか。
その答えが、今回のテーマであるスタックフレームとRBP(ベースポインタ)です。
 
この2つは、GDBでスタックを読む、バッファオーバーフローを理解する、ROPを組み立てるといったすべての基礎になります。

くろちゃん

スタックの中にも“区切り”があるってこと?


白猫先生

ええ。その区切りを理解するのが、今日のゴールよ

 ※イメージです。

スタックフレームとは何か

スタックフレームの正体

 スタックフレームとは、
ある関数が実行されている間だけ使う、スタック上の専用領域」です。
関数が呼ばれるたびに、
"引数"、"ローカル変数"、"保存されたレジスタ"、"リターンアドレス"といった情報が、ひとまとめで確保されます。
この「ひとまとめ」が、スタックフレームです。
 

なぜフレームが必要なのか

 もしスタックフレームが存在しなければ、「複数の関数が、同じスタックを無秩序に使う」ことになり、どの変数がどの関数のものなのか分からなくなります。
つまり、スタックフレームは関数ごとの作業スペースを明確に分けるための仕組みなのです。

くろちゃん

関数ごとに“自分の机”がある感じですね


白猫先生

その通り。机があるから混乱しないのよ

 
 

スタックフレームの内部構造 

スタックフレームの中身

 一般的な関数のスタックフレームは、次のような要素で構成されます。
 
・上位アドレス側
   ・引数
・中央
   ・リターンアドレス
   ・保存されたRBP
・下位アドレス側
   ・ローカル変数
 
スタックは「上から下へ伸びる」ため、ローカル変数はアドレスが小さい方向に配置されるのが特徴です。
 

フレームは関数ごとに積み重なる 

関数Aが関数Bを呼び出すと、
1.Aのスタックフレームが存在
2.その上にBのスタックフレームが積まれる
3.Bが終わると、Bのフレームだけが破棄される
 
この積み重なりが、
コールスタックと呼ばれる状態です。

くろちゃん

箱の上に、さらに箱が積まれていく感じですね


白猫先生

ええ。だから“スタック(積み重ねる)”と呼ばれるの

 
 

RBPとは何か 

RBPの役割

 RBP(Base Pointer)は、「今の関数のスタックフレームの基準点」を指すレジスタです。
RBPがあることで、プログラムは、ローカル変数の位置や、保存された情報の位置を、安定して参照できます。
 

なぜRSPだけでは足りないのか 

スタックポインタであるRSPは、
・push
・pop
 関数呼び出し
などで、常に値が変化します。
 
もしRSPだけを頼りにすると、ローカル変数の位置が毎回ずれてしまいます。
そこで、
・RSPは「今のスタック先頭」
・RBPは「この関数の基準点」
という役割分担が生まれました。

くろちゃん

RBPは“原点”みたいなもの?


白猫先生

ええ。そこから距離で位置を測るの

 
 

関数プロローグとRBPの設定 

関数が始まるときに起きていること

 多くの関数は、冒頭で次のような処理を行います。
1.以前のRBPをスタックに保存
2.RBP ← 現在のRSP
3.ローカル変数分の領域を確保
これを 関数プロローグ と呼びます。
 

これが意味すること

 この処理により、「この関数のRBP」が固定され、ローカル変数は、"RBP - オフセット"という形で参照されます。
GDBで"info frame"や、"x/gx $rbp"を確認すると、この構造が見えてきます。

くろちゃん

最初に“基準点”を決めてから作業するんですね


白猫先生

だから後で迷子にならないのよ

 
 

エクスプロイト開発との関係

 なぜ攻撃者はRBPを理解するのか

 バッファオーバーフローでは、"ローカル変数"、"保存されたRBP"、"リターンアドレス"が、同じスタックフレーム内に並んで存在します。
つまり、「どこまで書き込むと何が上書きされるのか」を理解するには、スタックフレーム構造の理解が不可欠です。
 

RBPを知ると見える世界

 RBPを理解すると、
・オフセット計算
・ペイロード長の決定
・スタックレイアウトの把握
が、論理的に説明できるようになります。
 
これは「勘」ではなく、構造に基づくエクスプロイトへの第一歩です。

くろちゃん

攻撃も、仕組みを知るところからなんですね


白猫先生

ええ。理解が深いほど、再現性が生まれるの

 
 

ハッカーくろちゃんが学ぶ、スタックフレームとRBPのまとめ

 スタックフレームは、関数ごとの専用作業領域。RBPは、そのフレームの基準点。
RBPがあるから、ローカル変数を安定して参照できます。
エクスプロイト開発では、フレーム構造の理解が成功率を左右する
 
スタックフレームとRBPを理解したあなたは、「スタックを眺める側」から「スタックを読み解く側」へ進みました。