ペネトレーションテスト&バグバウンティ
コンピュータの中にある「CPU(中央処理装置)」は、人間で言えば頭脳のようなものですね。
このCPUの中には「レジスタ」と呼ばれる、小さなメモ帳のような場所があります。
このメモ帳には、今、どの命令を実行中か、どこにデータがあるかなど、プログラムを正しく動かすための情報が入っています。
ハッカーは、このレジスタをのぞいて、プログラムが何をしているのかを調べるのです。

※イメージです。
レジスタとは
C言語で書かれたプログラムは、最終的にコンピュータの中の「CPU」が命令を実行します。
そのCPUには「レジスタ」と呼ばれる小さなデータ置き場があります。
レジスタは、変数や関数の実行位置など、一時的な情報を高速に扱うために使われます。
レジスタの役割と種類
GDBでよく見るレジスタには、次のようなものがあります:
RIP(Instruction Pointer)
役割:次に実行する命令(命令の番地)を指すレジスタ
RIPは「ここから次の命令を実行するよ!」という現在のプログラムの位置を覚えています。
GDBでステップ実行していると、次に動く命令のアドレスが常にこのRIPに入っています。
例)RIPが 0x555555554000 なら、その番地にある命令が次に実行されます。
例えると、
プログラムという本の「しおり」ですね。RIPが指しているページの命令が、次に実行されます。
RSP(Stack Pointer)
役割:スタックの一番上の位置を指すレジスタ
RSPは「今、スタックのどこが一番上か(=最新のデータ)」を記録しています。
スタックに値をpushしたり、popしたりするとRSPが上下に動きます。
バッファオーバーフローなどのエクスプロイトでは、RSPの動きを操作して攻撃します。
例えると、
スタックが「お弁当箱」だとしたら、RSPはいま一番上にあるおかずの位置を教えてくれます。
RBP(Base Pointer)
役割:関数のはじまり(ローカル変数の基準位置)を記録するレジスタ
関数が呼ばれると、スタックに情報が積まれます。その中で「ローカル変数はここから」と指し示すのがRBP。
GDBでスタックフレームをたどるときにも使います。
関数を抜けるとRBPは元に戻されます。
例えると、
ローカル変数を保管する「引き出しのスタート地点」。そこから何バイト分ズレた場所に変数があるかを探すときの目印。
RDI、RSI、RAX、RCXなど(汎用レジスタ)
1.RDI(1番目の引数)
関数に渡す第1引数の値が入る場所(例:printf("Hello") なら "Hello" のアドレスが入る)
2.RSI(2番目の引数)
関数に渡す第2引数(memcpy(dest, src, size) の src など)
3.RAX(戻り値、または演算結果)
関数が返す値(return 42; の「42」)は、RAXに格納されます。
4.RCX
状況に応じてさまざまな用途に使われます。たとえば、
① 関数の第4引数
LinuxのSystem V ABIでは、RCXは関数に渡す4番目の引数として使われることがあります。
② 繰り返し(ループ)回数のカウンタ
rep命令(繰り返し命令)や loop命令と一緒に使われることが多く、「何回繰り返すか」のカウンタとして使われます。
③ シフト命令の回数指定
shl,shr(ビットシフト)命令で、シフトするビット数をRCXに入れて操作することがあります。
④ 汎用計算用
その他の演算、デバッグ、関数の一時的な変数保持などにも使われます。
また、整数の計算や、システムコール番号などにも使われます。
例えると、
RDIやRSIは「プレゼント(引数)を受け取るための箱」。
RAXは「プレゼントの答え(戻り値)を入れる箱」。
関数呼び出し時のレジスタの流れ(System V ABI)
64bit Linuxでは、関数呼び出し時に以下のレジスタが引数として使われます(最大6つまで):
引数の順番、使用レジスタ
第1引数は、RDI
第2引数は、RSI
第3引数は、RDX
第4引数は、RCX
第5引数は、R8
第6引数は、R9
※R8とR9は、64ビットCPUアーキテクチャ(特にx86_64)で使用される 汎用レジスタ(general-purpose registers) の一部です。特に「関数に引数を渡すとき」に使われることが多く、第5引数・第6引数 を受け取るために使われます。
たとえば、以下のようなC関数があった場合、
void example(int a, int b, int c, int d, int e, int f);
このとき、各引数は次のレジスタに格納されて渡されます(LinuxのSystem V ABI):
引数 レジスタ
a RDI
b RSI
c RDX
d RCX
e R8
f R9
となります。
GDBでレジスタを見る方法
GDBでは、次のコマンドでレジスタの中身を表示できます。
(gdb) info registers
実行すると、すべてのレジスタの値が16進数で表示されます。関数を呼ぶ前・後で、どの値がどう変わるのか見ることができます。
GDBで RIP, RSP, RBP の動きを観察する方法について
サンプルプログラム(kurochan2.c)
// kurochan2.c
#include <stdio.h>
void kurochan() {
printf("Hello, kurochan!\n");
}
int main() {
kurochan();
return 0;
}
ステップ1:コンパイル(-g オプション付き)
gcc -g kurochan2.c -o kurochan2
-g オプションは、GDBでのデバッグに必要な情報(シンボル情報)をバイナリに埋め込むために使います。
動作確認
./kurochan2
ステップ2:GDBを起動する
gdb ./kurochan2
ステップ3:main関数にブレークポイントを置く
(gdb) break main
main() 関数の先頭でプログラムが一時停止します。
ステップ4:プログラムを開始する
(gdb) run
プログラムが main() の先頭で止まります。
ステップ5:ステップ実行してレジスタ確認
(gdb) step
main() の中の kurochan(); に進みます。
ステップ6:レジスタの内容を見る
(gdb) info registers
すると、次のような出力が表示されます:
rip ← 次に実行される命令のアドレス
rsp ← 現在のスタックの先頭アドレス
rbp ← 関数の基底ポインタ(ローカル変数の基準)
スタックフレームとRBP/RSPの関係
関数が呼ばれるたびに、新しい「スタックフレーム」が作られます。
RBPがフレームの底、RSPが天井のような関係です。
これを理解すると、スタックオーバーフローやROP攻撃の土台がわかるようになります。
レジスタから見える「攻撃の入り口」
攻撃者は、RIPを書きかえて任意の命令を実行させることを狙います。
つまり、レジスタの動きを読む力があれば、「どこに入り込めるか」を見つけられるのです。
ハッカーは、レジスタを読めると強くなる。GDBでCPUの中身をのぞく方法のまとめ
コンピュータの頭脳であるCPUには「レジスタ」と呼ばれる小さなメモ帳のような場所があり、今どの命令を実行しているか、どこにデータがあるかなどの重要な情報を高速に扱っています。
ハッカーや開発者はGDBを使ってこれらのレジスタをのぞくことで、プログラムの動きや弱点を理解します。代表的なレジスタには、次に実行する命令の位置を指すRIP、スタックの先頭を示すRSP、関数の基準位置を記録するRBPがあります。
さらに関数の引数や戻り値をやりとりするRDI・RSI・RAX・RCX・R8・R9などの汎用レジスタもあります。GDBのinfo registersを使えばこれらの値を実際に観察でき、ステップ実行することで関数呼び出しやスタックフレームの変化も追えます。
レジスタを理解することは、プログラム解析やセキュリティ学習の第一歩だと考えます。