popping from the stack without having pushed
I am learning High Level Assembly language at the moment and have been going over the concept of the stack. I think I understand it fairly well, however in practice I have some questions.
The stack grows down, with the ESP register always pointing to the top of the stack..an address in low memory. If something is pushed onto the stack, then ESP should be decremented.
EBP is useds as a frame pointer and as to my understanding, should always be more than ESP.
Yet, with the following program:
stdout.put(esp, nl);
stdout.put(ebp, nl);
push(ike);
stdout.put(esp, nl);
stdout.put(ebp, nl);
push(ike);
stdout.put(esp, nl);
stdout.put(ebp, nl);
pop(eax);
pop(eax);
pop(eax);
pop(eax);
stdout.put(esp, nl);
stdout.put(ebp, nl);
This does not seem to be the case. Looking at the output:
0018FF6C 0018FF70
0018FF68 0018FF70
0018FF64 0018FF70
0018FF74 0018FF70
EBP is always the same, ESP is decremented by 4 bytes for the first push, then another 4 bytes for the second push.
It is after this I am confused. After my first 2 pops, E开发者_StackOverflowSP should be back to where it started. How can I then do a further two pops if I have not pushed anything on to the stack? What am I popping?
Further popping and printing out of EAX shows some numbers, and then 0's and then further numbers. So, I am definitely popping something...but what? What part of my program memory does it belong to, and why is nothing being affected?
Why is EBP not being affected at all?
Also, why is ESP being decremented by 4 bytes, and not by 8?
If someone could help me to understand this, I would be most grateful.
EBP doesn't get modified by push/pop instructions, it's setup manually, so unless you change it yourself it will just stay the same.
The pushes of IKE are resulting in 4-byte changes, so apparently you're in 32-bit mode here.
The 4 pops of EAX (32-bit) will result in a 16-byte (10h) change, just like they do.
Not sure what the problem is here. Seems to be working as I expect?
The stack is used to hold, besides the things you push and pop, every function's stack frame (i.e. local variables), return address, the old ebp (specifically where the current ebp is) and function's parameters. So what you're popping out is the stack frame of your function.
If you disassemble a program, you'll see:
push param3 ; suppose func takes 3 parameters, they're
push param2 ; pushed in reversed order (C-style)
push param1
call func ; call pushes also the return address, i.e. the
; address of the instrucion after the call
...
func:
push ebp ; this is done for preserving the caller's stack frame
mov ebp, esp ; now we set up the beginning of func's stack frame
sub esp, smth. ; and its width, enough to fit all func's variables
At this point the stack will be something like:
00000058: arg3
00000054: arg2
00000050: arg1
0000004c: return address
ebp -> 00000048: caller's ebp
... ... -
00000034: random stuff | func's stack frame, random because they
00000030: random stuff | are uninitialized
esp -> 0000002c: random stuff -
Finally, you push and pop only 4 bytes at time because that's the word size of your machine, this way you can save entire registers. in one shot
精彩评论