■ 戻り番地書き換え

スタックフレーム

関数呼び出しの時、必要な情報はスタックで管理される。 次のような関数 func() を func(2,3) で呼んだとすると、 スタックフレームは、次のようになる。(x86系の場合。他の場合は未確認。)

int func(int x, int y) {
  int tmp[2];
  int i,j;
  ...
}
↑小さい番地
  ------------------------------
  |     ローカル変数 j         |
  ------------------------------
  |     ローカル変数 i         |
  ------------------------------
  |     ローカル変数 tmp[0]    |
  ------------------------------
  |     ローカル変数 tmp[1]    |
  ------------------------------
  |  退避したフレームポインタ  | ← フレームポインタ
  ------------------------------
  |         戻り番地           |
  ------------------------------
  |        引数 y(=3)          | 
  ------------------------------
  |        引数 x(=2)          |
  ------------------------------
  |           ...              |
  ------------------------------
↓大きい番地

戻り番地書き換え

スタックフレームの「戻り番地」の値を書き換えてしまえば、 関数の終了時に別のところにJUMPする。

#include <stdio.h>
#include <stdlib.h>

void g() {
  puts ("Function g() called");
  exit(1);
}

void f() {
  int x;
  *(&x + 2) = (int) ((void *) g); /*** 戻り番地を関数g()へのポインタの値に設定 ***/
}

int main () {
  f();
  puts ("Ended normally");
  exit(0);
}