関数呼び出しの時、必要な情報はスタックで管理される。 次のような関数 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);
}