1
votes

I picked up the book "Practical reverse engineering" and i have the following example on page 14 (x86 assembly):

mov eax, 0AAAAAAAh
mov ebx, 0BBBBBBBh
push eax
push ebx
pop esi
pop edi

Now, I push eax and ebx on to the stack, but i pop esi and edi of the stack. Why is that? I thought I would push and pop the same registers.

3
You can pop into whatever register (or memory) you want. The stack does not keep track of how the values got there. This trick is usually seen in exploit code optimized for size where it's used instead of a mov that is more bytes. - Jester
Aaah! Pop esi tells the destination of the pop. I thought you had to specify what you wanted to pop. But now I see that it's pointless since you can only pop what's on top of the stack. Thanks! - A39-A20
yes, push/pop/call/ret are quite similar to ordinary memory storing/loading, except those instructions use implicitly esp value as "top of stack pointer", so it's very natural to use them for "LIFO" (last in, first out) type of queue. But you should be able to recognize the simple memory manipulation behind it (although more atomic than if you would try to replicate it by something like sub esp,4 mov [esp],eax instead of push eax) - Ped7g
@Jester: It only saves bytes in x86-64 long mode for copying a 64-bit register, like push rbx / pop rdi (1 + 1 bytes) vs. mov rdi, rbx (3 bytes: REX + opcode + modrm). In 32-bit code, copying a register with mov esi,ebx still only 2 bytes. Perhaps you're thinking of setting a register to a small immediate constant, like push 1 / pop eax (2 + 1 bytes) vs. mov eax, 1 (5 bytes because there's no mov r/m32, sign_extended_imm8 encoding available). - Peter Cordes
You push values, not registers. These values usually come from registers, but even that is not necessary. You pop into registers here. - Rudy Velthuis

3 Answers

2
votes

You don't push registers, you push the values held in these registers. In this case, the values come from registers eax and ebx. The registers themselves remain where they were (somewhere inside the CPU).

You can pop these values into any registers, like esi and edi here. It simply depends on where in your code and in which registers you need these values.

Sometimes you only want to clear a few values from the stack. Then you could do

pop esi   ; or whatever register is not in use at the moment
pop esi    

That would not affect any other register. (Of course, you could also simply change the stack pointer, but that is another topic.)

1
votes

push — Push on stack

The push instruction places its operand onto the top of the hardware supported stack in memory. Specifically, push first decrements ESP by 4, then places its operand into the contents of the 32-bit location at address (%esp). ESP (the stack pointer) is decremented by push since the x86 stack grows down — i.e. the stack grows from high addresses to lower addresses.

pop — Pop from stack

The pop instruction removes the 4-byte data element from the top of the hardware-supported stack into the specified operand (i.e. register or memory location). It first moves the 4 bytes located at memory location (%esp) into the specified register or memory location, and then increments ESP by 4.

look for more details: push / pop explanation

1
votes

When you pop the stack, the register operand just receives the value that was on the top of the stack. As long as the same number of bytes are pushed and popped, the stack is balanced, regardless of where the bytes go. You do not necessarily need to pop back into the same location that was the source of your earlier pushed value.

For example, this code loads the register ecx with whatever was currently in eax, without misaligning the stack:

push eax
pop ecx

You can also perform an effective "operand-less" pop by manually adjusting the stack pointer:

push eax
...
add esp, 4 ; Discard 32-bits on top of stack 
; Stack is now balanced (assuming the intermediate instructions did not misalign the stack)