スタックフレームを使用する必要はありませんが、確かにいくつかの利点があります。
まず、すべての関数がこれと同じプロセスを使用している場合、この知識を使用して、プロセスを逆にすることで一連の呼び出し(呼び出しスタック)を簡単に判別できます。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
場合に変更されます)、スタック上のデータを参照する方が、相対的ではなく、相対的である方が通常は簡単です。後者の場合は、注意する必要があります。関数の開始と参照の間に呼び出された可能性のある/命令。push
pop
EBP
ESP
push
pop
他の人が述べているように、他の関数に対して行うスタックアドレスは、これらのアドレスのデータを上書きする可能性があるため、以下の スタックアドレスの使用は避けてください。代わりに、通常の関数で使用するためにスタック上のスペースを予約する必要があります。ESP
call
sub esp, [number of bytes to reserve]
この後、初期ESP
との間のスタックの領域ESP - [number of bytes reserved]
は安全に使用できます。関数を終了する前に、一致するものを使用して予約済みのスタックスペースを解放する必要があります。
add esp, [number of bytes reserved]