1
votes

So I'm currently reading Practical reverse engineering_ x86, x64, ARM, Windows Kernel, reversing tools, and obfuscation the book includes the following example to explain the stack frame.

addme(x, y)

01: 004113A0 55 push ebp
02: 004113A1 8B EC mov ebp, esp
03: ...
04: 004113BE 0F BF 45 08 movsx eax, word ptr [ebp+8]
05: 004113C2 0F BF 4D 0C movsx ecx, word ptr [ebp+0Ch]
06: 004113C6 03 C1 add eax, ecx
07: ...
08: 004113CB 8B E5 mov esp, ebp
09: 004113CD 5D pop ebp
10: 004113CE C3 retn

Function call

01: 004129F3 50 push eax ;param2
02: ...
03: 004129F8 51 push ecx ;param1
04: 004129F9 E8 F1 E7 FF FF call addme
05: 004129FE 83 C4 08 add esp, 8

I understand that on line 10 in the addme function we do mov ebp, esp to start a new stack frame for the current function, but what I dont understand is why we are calling mov esp, ebp right before the pop. If I understand correctly this is the current state of the stack before the call to mov esp, ebp is made.

           TOP
**************************
*         param2         *
**************************
*         param1         *
**************************
*         return addrs   *
**************************
*         old edp        *
**************************
esp and edp are pointing after odl edp.

So why was the call to mov esp, ebp made?

If there is something wrong with the way I asked the question please let me know. Thank you.

1
In this case it's not necessary as esp has not been modified. Otherwise its purpose would be to free the locals. Note that standard stack frame is not mandatory and frequently not used in optimized code.Jester
A smarter compiler would have realized it could just pop %ebp instead of doing a leave in that case. (And in fact GCC does do that optimization even in debug mode). I'm assuming there's not sub esp, 1234 or push / call (leaving stuff on the stack for the epilogue to clean up) inside the part not shown.Peter Cordes

1 Answers

2
votes

The instruction pair

mov esp, ebp
pop ebp

is used to restore the stack frame of the calling function. It can also be done through the single instruction

leave

but that could be slower than using the two instructions (see this post).

But is mov esp, ebp really necessary here?

That depends on what is done within the function. The compiler usually adds this line because it removes local variables from the stack. There could also be other values on the stack if a return is used in the middle of the function, where interim results can also be on the stack. imagine return is called with this stack assignment:

+---------------------------+
|         param 2           |
+---------------------------+
|         param 1           |
+---------------------------+
| old eip (return address)  |
+---------------------------+
| old ebp (last stackframe) | <-- ebp
+---------------------------+
|     local variable 1      |
+---------------------------+
|     local variable 2      |
+---------------------------+
|       interim value       | <-- esp
+---------------------------+

Interim values can arise if the registers are no longer sufficient to store all values. And ebp points to the old ebp, not to the memory location afterwards (see Does push esp update ESP before or after storing?)