GDB入門 スタックを実際に見てみよう(episode-05)

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

エクスプロイト開発を学ぶうえで、必ず登場する言葉があります。それが「スタック(stack)」です。スタックは、プログラムが動くときに使われる重要なメモリ領域で、関数の呼び出しや戻り先、ローカル変数などが一時的に置かれます。
 
しかし初心者の方にとって、「スタック」と聞くと、難しそう、見えない、よく分からないと感じてしまうことが多いでしょう。
 
そこで今回は、GDB(GNU Debugger)を使って、実際にスタックを「目で見て確認する」ことを目標にします。
くろちゃんと白猫先生と一緒に、「スタックって何が入ってるの?」「GDBでどうやって見るの?」を、ひとつずつ丁寧に理解していきましょう。
 
[fuki-r]白猫先生……。プロセスとかメモリは少し分かってきたけど、スタックって急に難しく感じるよ……[/fuki-r]
[fuki-l]大丈夫よ、くろちゃん。
スタックは“考え方”が分かれば、とても素直な仕組みなの[/fuki-l] 
※イメージです。

スタックって何をしている場所?

[fuki-l]まず大事なのは、スタックは関数のための作業台だということよ[/fuki-l]
 プログラムは、次のように動いています。
・main関数が呼ばれる
・別の関数を呼ぶ
・処理が終わったら元の場所に戻る
 
このとき、
・どこから呼ばれたか
・戻るべき場所
・関数内の変数
これらを一時的に置いておく場所が スタック です。
[fuki-r]じゃあ、スタックがないと「戻り道」が分からなくなるんだね![/fuki-r]
[fuki-l]その通り。だからエクスプロイトでは、スタックの中身を操作できるかどうかが、とても重要になるの[/fuki-l]
それでは、Kali Linuxを利用してGDBの確認をしていきましょう。
 
 

Kali Linux には GDB が最初から入っています

Kali Linux では、通常 GDB は標準インストール済みです。
確認方法
gdb –version
以下のように表示されれば インストール済みです。

GDBで「スタック」をとりあえず触って見てみよう

ここからは実際に、Kali LinuxにインストールされているGDB を使います。
難しい操作はしません。「見る」ことだけに集中します。
 
1.サンプルプログラムを用意する
まずは、シンプルなCプログラムを用意します。
a.ファイル名:gdb-sample.c で、nanoエディターを立ち上げます。
nano gdb-sample.c
 
 
b.以下のプログラムを入力します。
 #include <stdio.h>
 void hello() {
    int x = 10;
    printf(“Hello\n");
}
 int main() {
    hello();
    return 0;
}
 
c.入力完了後
「Ctrl」+ o で、プログラムの書き込み
「Ctrl」+ x で、nanoエディター終了です。
 
d.ポイント
・"hello()"の中にローカル変数 “x"
・関数呼び出しが1回ある
これだけで、スタックは動いています。
  
2.デバッグ情報付きでコンパイル
gcc -g gdb-sample.txt -o gdb-sample
※"-g"は GDBで中身を見るためのおまじない です。
実行ファイルが作成されました。
 [fuki-r]えっ、これだけでいいの? 何か壊しそうでドキドキする……[/fuki-r]
[fuki-l]今日は「観察」だけ。壊すのは、もっと先のお楽しみよ。[/fuki-l]
  
3.GDBを起動する
gdb ./gdb-sample
そして実行します。
【gdbの画面】
run
 
4.ブレークポイントを置く
“hello()"の中で止めてみましょう。
break hello
run
※プログラムが hello関数の入口で停止します。
  
5.スタックを表示してみる
・コールスタック(プログラム実行中に関数が呼び出された履歴(呼び出し階層)を追跡・表示する機能)を見る
backtrace
すると、こんな表示が出ます。
#0 hello () at gdb-sample.c:4
#1 0x0000555555555168 in main () at gdb-sample.c:9
 [fuki-r]おおっ!"main → hello"って、呼ばれた順番が見えてる![/fuki-r]
[fuki-l]それが コールスタック。今どの関数の中にいるかが分かるの[/fuki-l]
 
6.スタックフレームを切り替える
frame 0
→ “hello()"の中
 
frame 1
→ “main()"の中
 [fuki-r]関数ごとに「場所」が分かれてる感じがするね。[/fuki-r]
[fuki-l]その“場所”が、スタックフレームよ。エクスプロイトでは、この構造を崩すことで制御を奪うの。[/fuki-l]
  
7.ローカル変数を確認する
next
info locals
 x = 10
[fuki-r]うわ、本当に中身が見える……![/fuki-r]
[fuki-l]これが「見えるようになる」という第一歩。スタックは、もう怖くないでしょう?[/fuki-l]
   
8.なぜエクスプロイトでスタックが重要なのか
・リターンアドレスが置かれている。
・バッファオーバーフローの標的になる。
・ROPや制御奪取の出発点になる。
つまり、
スタックを理解する=エクスプロイトの基礎体力になるのです。
 
 

GDB入門 スタックを実際に見てみようのまとめ

今回は、GDBを使ってスタックを実際に観察することを目標にしました。
スタックは、関数呼び出しを支える重要なメモリ領域であり、
「どこから来て、どこへ戻るのか」
「どんな変数を使っているのか」
といった情報が詰まっています。
 
GDBの"break"、"backtrace"、"frame"、"info locals"を使うことで、
目に見えないはずのスタック構造が、はっきりと確認できる
ことを体験できたはずです。
 
エクスプロイト開発は、いきなり攻撃する学習ではありません。
まずは、
観察 → 理解 → 操作
という順番を守ることが、最短ルートです。
 
上記より、「見て分かる」、「触って納得する」を積み重ねていけば、スタックは確実に味方になります。