10. まとめ:最初の異常系を自分の言葉で説明する
10.1 一本の因果を再構成する
1. コンパイラはread_nameを含む命令列をELFへ入れる。
2. OSはELFを仮想メモリへ配置し、スレッドの実行を始める。
3. CPUはRIPが指す命令を取得し続ける。
4. call read_nameはmainの続きのアドレスをスタックへ保存する。
5. read_nameはその近くにname[16]を配置する。
6. readへはnameの先頭アドレスと128という最大値が渡る。
7. 長い入力はnameの範囲を超え、後続する戻り先を変える。
8. 書き込み時点では、まだRIPは変わらない。
9. 関数終了時のretが、壊れた戻り先をRIPへ入れる。
10. CPUは新しいRIPが指すwinから命令を取得する。
10.2 理解のセルフチェック
- 実行ファイルとプロセスの違いを説明できるか。
- 仮想アドレスと物理メモリが同じものではないと説明できるか。
- アドレス、ポインタ、配列長が別の情報だと説明できるか。
RIP,RSP,RBPの役割を区別できるか。- CPUが命令を取得してから次の
RIPを決めるまでを説明できるか。 - 関数呼び出しに戻り先が必要な理由を説明できるか。
callとretがRSPとRIPをどう変えるか説明できるか。- 戻り先と局所配列が同じスタック上にあることを図示できるか。
readのcountと実際の読み取り数を区別できるか。- 長さの契約違反が境界外書き込みになる理由を説明できるか。
- 書き込みの瞬間と、壊れた戻り先が使われる瞬間を区別できるか。
- 単なるクラッシュと
RIP制御の違いを説明できるか。 winへ到達した後、CPUが命令を取得するまでを説明できるか。- NXが防ぐことと、NXが防がないことを区別できるか。
- ASLRとPIEの関係を説明できるか。
- カナリやシャドウスタックが、因果のどの位置を切るか説明できるか。
この16問のうち、特に6、7、10、11、13を自分の言葉でつなげられれば、今回の目的は達成している。