第1章 総論
バッファオーバーフローを理解するための基礎知識

メモリ空間

コンパイルされた機械語プログラムは、ひと続きのメモリ空間において実行される。メモリの中は、バイトあるいはワードの単位ごとにアドレスが付けられており、プログラムはそれらのアドレスに自由にアクセスできる。

多くのオペレーティングシステムは「仮想メモリ」の仕組みを採用している。この場合プログラムの実行単位である「プロセス」ごとに異なるメモリ空間が用意される。他のプロセスとの間でメモリ空間を共有しない。ひとつのメモリ空間は、例えば次のような用途の領域に分けて使用される。

領域は分かれているが、ひと続きの空間であるため、これらは相互に干渉する可能性がある。プログラムが命令コード領域の一部をデータとして読み出したり、ヒープの一部を命令として実行することが(後者はプロセッサやオペレーティングシステムのデータ実行防止機能がはたらいていない限りにおいて)可能である。

図1-2: プロセスごとにメモリ空間が用意されている

命令カウンタ

機械語プログラムの実行は、メモリの命令コード領域から次々と命令が読み出され動作が実施されることによって進行する。 命令の実行の流れは、通常は並べられた順序どおりに進行するが、条件分岐や関数呼び出しのように、連続しない遠くのアドレスに置かれた命令へ「ジャンプ」することもある。

命令の実行順序を左右するのが「命令カウンタ」と呼ばれるレジスタである。 命令カウンタは、「次に実行すべき命令のアドレス」を保持している。プロセッサがメモリから命令を読み出すと、命令カウンタには「次の命令」のアドレスが自動で入れられる。メモリに並べられた命令が順に処理されてゆくのは、この仕組みによる。

演算命令と命令カウンタ

演算命令が実行される過程で命令カウンタが更新されてゆく様子を一連の図に示す。

(1) ひとつ前の命令の実行直後

図1-3: 命令カウンタはすでにADD命令のアドレスを指している

(2) ADD命令の取り出し

図1-4: 命令カウンタが次の命令のアドレスを指すようになる

(3) ADD命令の実行

図1-5: レジスタ間で加算演算が行われる

(4) 次の命令の取り出し

図1-6: 既にに命令カウンタが指していたところから命令が取り出される

ジャンプ命令と命令カウンタ

プログラムが「分岐」や「関数呼び出し」のジャンプを行えるのは、「分岐命令」や「関数呼び出し命令」が、命令カウンタを別の値に書き換えるはたらきをするからである。命令カウンタの値が書き換えられると、後続の命令は書き換えられたそのアドレスから読み出されるようになり、命令実行の流れが変化する。

命令カウンタを書き換えることによってジャンプ命令が効果を表す様子を一連の図に示す。

(1) ひとつ前の命令の実行直後

図1-7: 命令カウンタはJMP命令のアドレスを指している

(2) JMP命令の取り出し

図1-8: 命令カウンタはいったん次の命令のアドレスを指すようになる

(3) JMP命令の実行

図1-9: 命令カウンタが書き換えられる

(4) 次の命令の取り出し

図1-10: ジャンプ先のアドレスから命令が取り出される