GDBでスタックを見る「戻る場所」が積まれる現場をGDBで観測(第5回/全8回)
BOF(バッファオーバーフロー)を理解するうえで、最重要の土台は「スタック」です。
スタックが分かると、次が“論理”で説明できるようになります。
※スタックとは、プログラムの実行中に関数の呼び出し情報(引数、戻り先、ローカル変数など)を一時的に保存するメモリ領域のことです。
・関数呼び出しで、何が積まれるのか。
・どこに戻り先(return address)があるのか。
・なぜ破壊が起きると実行の流れが変わるのか。
今回のゴールは、GDBで「関数の呼び出し履歴」+「スタックの中身」を実際に見て、イメージと現物を一致させることになります。
※イメージです。
スタックは「積み重ね構造」=関数の作業セット
スタックは一言でいえば、「上に積んで上から使う箱(LIFO)」です。
関数が呼ばれるたびに、スタックに「その関数の作業セット」が積まれます。
関数呼び出しとスタックを超ざっくり説明すると
main()
└─ add() を呼ぶと
スタック(上が最新)
+———————+ ← RSP(先頭)
| add() の作業セット |
| – ローカル変数 |
| – 戻り先情報 | ← 重要
+———————+
| main() の作業セット |
+———————+
ポイントはこれだけです。
「戻り先情報(戻る場所)」もスタックに置かれます。
だからスタックは、BOFを理解する入り口になります。
スタックフレームと「戻り先アドレス」
関数の作業セットを「スタックフレーム」と呼びます。
スタックフレーム(概念)
(上) ローカル変数など
————
保存されたRBP(目印)
————
(下) 戻り先アドレス(return address) ← 超重要
・RSP:スタックの先頭(現場の起点)
・RBP:その関数フレームの基準点(目印)
今回は、「どの関数にいるか」と、「スタックに何が積まれているか」をGDBで観測します。
Kaliで手を動かす①:呼び出し履歴を読む(backtrace)
第3回の “gdb03.c"(add関数あり)を使います。
※詳しくは、こちらをご覧ください。
1.DGBで、gdb03を起動します。して add() の中で止める
gdb ./gdb03
2.add()で止めます。
break add
run
3.いまどの関数にいるかlistで確認します。
list
4.呼び出し履歴を見る。(ここが核心です!!)
backtrace
【解説】処理の履歴が見られます。
#0: 現在実行中の(一番新しい)関数です。
add (x=5, y=7): add関数が呼ばれ、引数 xに5、yに7を受け取って実行されています。
at gdb01-03.c:4: ソースファイル gdb01-03.c の 4行目 で停止していることを示します。
#1: #0を呼び出した元の関数です。
main (): main関数から呼び出されました。
at gdb01-03.c:11: main関数の 11行目 で add関数を呼び出したことを示します。
要するに、「main関数の11行目から呼ばれたadd関数の4行目で止まっている」という状態を指しています。
※「backtrace」は、関数の履歴(どこから来たか)を見せてくれます。
これが「スタック=履歴」という感覚につながります。
Kaliで手を動かす②:スタックフレームと現場を覗く(info frame / x)
1.フレーム情報を見る(戻り先のヒントが出る)
info frame
ここで表示される「saved rip」や「return address」等の表現は、環境で多少違います。
大事なのは、
・「戻り先」関する情報が出てくるという事実です。
2.レジスタで現場の起点(RSP/RBP)を見る
info registers rsp rbp rip
3.スタックの中身を覗く(RSPから)
x/24gx $rsp
※ x/24gx $rsp の意味
x … メモリを調べる
/24 … 24個ぶん
g … 8バイト単位(64bitの塊)
x … 16進表示
$rsp … スタック先頭から
とりあえず「読める」必要は今はありませんが、スタックが数値として見えていることが大切です。
4.1行進めて、もう一度覗く(変化を見る)
next
x/24gx $rsp
※変化の有無でOKです。
GDBは変化を観測する道具であることを確認してください。
5.終了
continue
quit
GDBでスタックを見る「戻る場所」が積まれる現場をGDBで観測のまとめ
スタックは、関数呼び出しのたびに「作業セット(スタックフレーム)」が積み重なる領域で、プログラムの“履歴”そのものです。
重要なのは、ローカル変数だけでなく「戻り先アドレス(return address)」の情報もスタックに保存される点で、ここがBOF理解の土台になります。
GDBでは `backtrace` により、どの関数がどこから呼ばれたかを履歴として確認でき、スタックが呼び出しの連鎖を保持していることを体感できます。さらに `info frame` と `info registers rsp rbp rip` でフレームの基準点と現場(RSP)を押さえ、`x/24gx $rsp` でスタックの中身を実際に覗くと、抽象概念が“現物”になります。今は読めなくても構いません。止める→見る→少し進めて変化を見る習慣が、論理的な解析力を育てることになります。