There is no need to use a stack frame, but there are certain advantages:
First, if every function uses the same process, we can use this knowledge to easily determine the call sequence (call stack) by changing the process. We know that after the call command, the ESP points to the return address and the first thing the call function will call is push current EBP , and then copy the ESP to the EBP . So, at any time, we can look at the data indicated by the EBP , which will be the previous EBP , and that EBP+4 will be the return address of the last function call. Therefore, we can print the call stack (suppose 32 bits) using something like (sorry rusty C ++):
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); }
Then the entire sequence of calls (well, their return address) to this point will be printed.
Also, since EBP does not change unless you explicitly update it (as opposed to ESP , which changes when you push / pop ), it is usually easier to refer to the data on the stack relative to EBP and not relative to ESP , since with the latter you must know any push / pop statements that might be called between the start of the function and the link.
As mentioned above, you should avoid using stack addresses below ESP , since any call you make to other functions will most likely overwrite data at these addresses. Instead, you should reserve a stack space for your function to use as normal:
sub esp, [number of bytes to reserve]
After that, the stack area between the initial ESP and ESP - [number of bytes reserved] safe to use. Before exiting your function, you must free the reserved stack space using the match:
add esp, [number of bytes reserved]