スタックフレームを使用する必要はありませんが、確かにいくつかの利点があります。
まず、すべての関数がこれと同じプロセスを使用している場合、この知識を使用して、プロセスを逆にすることで一連の呼び出し(呼び出しスタック)を簡単に判別できます。call命令の後ESP、リターンアドレスを指し、呼び出された関数が最初に行うことはpush現在のものEBPであり、次ににコピーESPすることを知っていますEBP。したがって、いつでも、それが指すデータを見ることができます。EBPこれは前のデータであり、EBPそれEBP+4が最後の関数呼び出しの差出人住所になります。したがって、(さびたC ++を失礼します)のようなものを使用して、コールスタック(32ビットを想定)を出力できます。
void LogStack(DWORD ebp)
{
DWORD prevEBP = *((DWORD*)ebp);
DWORD retAddr = *((DWORD*)(ebp+4));
if (retAddr == 0) return;
HMODULE module;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*)retAddr, &module);
char* fileName = new char[256];
fileName[255] = 0;
GetModuleFileNameA(module, fileName, 255);
printf("0x%08x: %s\n", retAddr, fileName);
delete [] fileName;
if (prevEBP != 0) LogStack(prevEBP);
}
これにより、その時点までの一連の呼び出し全体(まあ、それらのリターンアドレス)が出力されます。
さらに、EBP明示的に更新しない限り変更されないため(/とは異なり、 /のESP場合に変更されます)、スタック上のデータを参照する方が、相対的ではなく、相対的である方が通常は簡単です。後者の場合は、注意する必要があります。関数の開始と参照の間に呼び出された可能性のある/命令。pushpopEBPESPpushpop
他の人が述べているように、他の関数に対して行うスタックアドレスは、これらのアドレスのデータを上書きする可能性があるため、以下の スタックアドレスの使用は避けてください。代わりに、通常の関数で使用するためにスタック上のスペースを予約する必要があります。ESPcall
sub esp, [number of bytes to reserve]
この後、初期ESPとの間のスタックの領域ESP - [number of bytes reserved]は安全に使用できます。関数を終了する前に、一致するものを使用して予約済みのスタックスペースを解放する必要があります。
add esp, [number of bytes reserved]