基本的にプログラム全体。Printf はスタックから引数を取得し始めます。この場合、値を取りint
すぎます。これは通常、返信先住所のようなものです。したがって、printf が戻ると、たまたまスタックの次にある乱数に戻ります。運が良ければ、通常の結果はセグメンテーション違反です。
引数をスタックにプッシュするため、引数をポップして、int
最初の引数を取得しようとします。
運が悪ければ、アドレス指定可能なコードのチャンクが見つかります。これは、アドレスがランダムな文字のハッシュのアドレスになる 2 番目のケースにつながります。次に、ランダムな NUL 文字が見つかるまで文字列を出力しようとします。
アップデート
Joachim が指摘しているように、これの詳細は呼び出し規約によって決定されるため、明示的な例を作成しましょう。printf 関数が呼び出されるとき、戻りアドレスが最初にプッシュされるか、最後にプッシュされます。最初にプッシュされると仮定します (通常のアーキテクチャではより一般的です)。したがって、この呼び出しには PUSH return-address、フォーマット文字列の PUSH アドレス、int 値の PUSH (42 としましょう) が必要になります。これにより、次のスタックが得られます。
RTN ADDR
ADDR OF STRING
42
スタック ポインタ SP は、スタック上の次の位置を指しているままになります。
ここで、printf が文字列の解釈を開始します。パラメータのアドレスを探し、int
それが SP-1 であることを突き止めます。したがって、文字列パラメーターのアドレスは SP-2 である必要があります...しかし、文字列パラメーターがないため、これは書式文字列のアドレスです。次に、フォーマット文字列のアドレスを探すときに SP-3 を見つけようとしますが、それは戻りアドレス、つまり実行可能コードのアドレスです。これにより、ほとんどのマシンでセグメンテーション違反が発生するはずです。
呼び出し規則の他のオプションを調べてみると、それらのすべてが何か間違ったことを見ていることがわかります。なぜなら、printf はスタックから 2 つではなく、3 つのものを参照する必要があると考えているからです。