I'm working on a computer system exam review question asking for the stack pointer offset at various points in the program, and I'm confused about how the stack frame is set up. I've been learning assembly between 32-bit and 64-bit systems for a couple weeks but I haven't seen an instruction to AND the stack pointer before.
Can someone explain what purpose this serve, what would would be the offset of %esp after fourth instruction, and I calculating %esp correctly? I've commented the code with what I think the value of the offset would be after each instruction.
main:
0x0x0804848a <+0>: push %ebp ;-0x4
0x0x0804848b <+1>: mov %esp, %ebp
0x0x0804848d <+3>: and $0xfffffff0, %esp ;[-0x4, -0x13]
0x0x08048490 <+6>: sub $0x20, %esp ;[-0x24, -0x33]
0x0x08048493 <+9>: movl $0xa, 0x1c(%esp)
0x0x0804849b <+17>: movl $0xa, (%esp)
0x0x080484a2 <+24>: call 0x804841d <r> ;[-0x28, -0x37]
You will be enlightened if you repeat your math with a stack pointer that starts at 0x3.
Alignment. Many CPU types require alignment for best performance or for vector instructions like SSE, to work at all.
Related
It's said that the "leave" instruction is similar to:
movl %ebp, %esp
popl %ebp
I understand the movl %ebp, %esp part, and that it acts to release stored up memory (as discussed in this question).
But what is the purpose of the popl %ebp code?
LEAVE is the counterpart to ENTER. The ENTER instruction sets up a stack frame by first pushing EBP onto the stack and then copies ESP into EBP, so LEAVE has to do the opposite, i.e. copy EBP to ESP and then restore the old EBP from the stack.
See the section named PROCEDURE CALLS FOR BLOCK-STRUCTURED LANGUAGES in Intel's Software Developer's Manual Vol 1 if you want to read more about how ENTER and LEAVE work.
enter n,0 is exactly equivalent to (and should be replaced with)
push %ebp
mov %esp, %ebp # ebp = esp, mov ebp,esp in Intel syntax
sub $n, %esp # allocate space on the stack. Omit if n=0
leave is exactly equivalent to
mov %ebp, %esp # esp = ebp, mov esp,ebp in Intel syntax
pop %ebp
enter is very slow and compilers don't use it, but leave is fine. (http://agner.org/optimize). Compilers do use leave if they make a stack frame at all (at least gcc does). But if esp is already equal to ebp, it's most efficient to just pop ebp.
The popl instruction restores the base pointer, and the movl instruction restores the stack pointer. The base pointer is the bottom of the stack, and the stack pointer is the top. Before the leave instruction, the stack looks like this:
----Bottom of Caller's stack----
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack/Bottom of Callee's stack---- (%ebp)
...
Callee's
Variables
...
---Bottom of Callee's stack---- (%esp)
After the movl %ebp %esp, which deallocates the callee's stack, the stack looks like this:
----Bottom of Caller's stack----
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack/Bottom of Callee's stack---- (%ebp) and (%esp)
After the popl %ebp, which restores the caller's stack, the stack looks like this:
----Bottom of Caller's stack---- (%ebp)
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack---- (%esp)
The enter instruction saves the bottom of the caller's stack and sets the base pointer so that the callee can allocate their stack.
Also note, that, while most C compilers allocate the stack this way(at least with optimization turn'd off), if you write an assembly language function, you can just use the same stack frame if you want to, but you have to be sure to pop everything off the stack that you push on it or else you'll jump to a junk address when you return(this is because call <somewhere> means push <ret address>[or push %eip], jmp <somewhere>, and ret means jump to the address on the top of the stack[or pop %eip]. %eip is the register that holds the address of the current instruction).
I'm studying for a midterm tomorrow and one of the questions on a previous midterm is:
Consider the following C function. Write the corresponding assembly language function to perform the same operation.
int myFunction (int a)
{
return (a + 30);
}
What I wrote down is:
.global _myFunction
_myFunction:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
lea ($30, %edx), %eax
leave
ret
where a is edx and a+30 would be eax. Is the use of lea correct in this case? Would it instead need to be
lea ($30, %edx, 1), %eax
Thanks.
If you are looking to simply add 30 using leal then you should do it this way:
leal 30(%edx), %eax
The notation is displacement(baseregister, offsetregister, scalarmultiplier). The displacement is placed on the outside. 30 is added to edx and stored in eax. In AT&T/GAS notation you can leave off both the offset and multiplier. In our example this leaves us with the equivalent of base + displacement or edx + 30 in this example.
cHao also brings up a good point. Let us say the professor asks you to optimize your code. There are some inefficiencies in the fact that myFunction uses no local variables and doesn't need stack space for itself. Because of that all the stack frame creation and destruction can be removed. If you remove the stack frame then you no longer push %ebp as well. That means your first parameter int a is at 4(%esp) . With that in mind your function can be reduced to something this simple:
.global _myFunction
_myFunction:
movl 4(%esp), %eax
addl $30, %eax
ret
Of course the moment you change your function so that it needs to store things on the stack you would have to put the stack frame code back in (pushl %ebp, pushl %ebp, leave etc)
So we have the following code, setting up for a function call with its arguments, its main body omitted (etc etc etc), and then the popping at the end of the function.
pushl %ebp
movl %esp, %ebp
pushl %ebx
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
etc
etc
etc
//end of function
popl %ebx
popl %ebp
Here's what I (think) I understand.
Suppose we have %esp pointing to memory address 100.
pushl %ebp
So this essentially makes %ebp point to where %esp points (memory address 100) + 4. So now %ebp points to memory address 104. This leaves our current memory state looking like so:
----------
|100|%esp
|104|%ebp
----------
Then we have the next line of code:
movl %esp, %ebp
So from what I understand, ebp now pointers to memory address 100. I have a little intuition as to why we do this step, but my confusion is the next line:
pushl %ebx
What is the purpose of pushing ebx, which I assume will then point to memory address 104? I have a vague idea of how the space right below ebp (104) is supposed to be a reference to an "old stack pointer," so I can see why the next 2 lines add 8 and 12 to ebp to be the "arguments" of our function, rather than 4 and 8.
But I'm confused as to why we push ebx onto the stack, first.
I also do not understand popping, and why we pop ebx and ebp?
Talking to someone about this before he had to sleep, he mentioned that we have no reference to the fact that our stack pointer was at 100 -- until we pop ebp back. Now, I thought ebp's value was 100, so I don't understand the point he was trying to make.
So to clarify:
Is my understanding thus far correct?
Why do we push ebx onto the stack?
What is this "reference to the old stack pointer" that lives right below ebp? Is that the ebx that we push?
Is there something I'm not understanding, like some sort of difference between the ebx that we push, and the ebx in the line right after (our argument)? Is there a difference between the ebp that gets pushed and the ebp in the line right after?
Why are we popping at the end?
I apologize if this is difficult to understand. I understand similar questions have been asked about this, but I'm trying to intuitively understand and picture what exactly is going on in a function call in a way that makes sense to me.
Note: I edited some important things regarding my understanding of what's going on, particularly with regards to ebp.
As Joachim stated in a comment on your question, pushing a register pushes the contents of the register at that moment onto the stack; it doesn’t push a reference to the register or anything else. I’m not sure if you were saying that’s what was happening, but otherwise this diagram was unclear:
----------
|100|%esp
|104|%ebp
----------
Nevertheless, I’ll try to explain what it does and why.
Say %esp was 0x100 when the caller calls our function and the instruction after the call is at 0x200. When we execute call, we push 0x200 (the return address) and jump to the procedure. Our stack is then:
Address Value
%esp --> 0x100 0x200
And %ebp is some value or another; it might point into the stack or it might not. It doesn’t even need to represent an address. So %ebp is meaningless to us at this point.
But though it’s meaningless to us, the caller does expect it to stay the same before and after the call, so we have to preserve it. Let’s say it contained the value 0xDEADBEEF. We push it, so the stack now looks like this:
Address Value
0x100 0x 200
%esp --> 0x0fc 0xDEADBEEF
In most situations, we can address everything as an offset from %esp, and that applies here, too. But if the compiler is compiling some C code that deals with variable-length arrays or other features, we often will want to index from the first thing we pushed rather than the last thing we pushed. To do that, we’ll set %ebp to where we are right now. Then things look like this:
Address Value
0x100 0x 200
%esp, %ebp --> 0x0fc 0xDEADBEEF
Note that the value at the address pointed to by %ebp is the old value of %ebp, so you can walk the stack, as you mentioned you were aware of before.
Next, we push %ebx, which we’ll say has the value 0xBEEFCAFE. This is the first thing not directly related to a function prologue. Then our stack looks like this:
Address Value
0x100 0x 200
%ebp --> 0x0fc 0xDEADBEEF
%esp --> 0x0f8 0xBEEFCAFE
But why do we push %ebx? Well, as it turns out, the x86 C calling convention dictates that, like %ebp, %ebx must stay the same as it was before the call. So because the code you omitted presumably changes %ebx, it has to preserve the initial value so it can restore it for the caller.
After we’ve restored %ebx, we pop %ebp, restoring its value as well, since that, too, must be preserved after the call. And finally we return.
TL;DR: %ebp and %ebx are pushed and popped because they are manipulated during the execution of the body of the function, but the x86 C calling convention dictates that the values must remain the same before and after the call, so the initial values must be preserved so we can restore them.
pushl %ebp
Save the value of ebp on the stack. Any push command affects the value of %esp.
movl %esp, %ebp
Move the current value of esp into ebp. This sets the stack frame, you can now find function arguments above ebp (as the stack grows down).
pushl %ebx
Save the value of ebp (not 100% sure but most likely the ABI rules).
movl 8(%ebp), %ebx
Move the memory ebp+8 into ebx. As previously stated, since the stack grows down this is one of the function arguments.
movl 12(%ebp), %ecx
Similar to the previous instruction, this moves another function argument into ecx.
popl %ebx
Restore the value of ebx we saved on the stack earlier.
popl %ebp
And restore the value of ebp. At this point, there is a match pop for every push so the esp is back to what it was on function entry so we can return.
In the given url this function is given:
http://insecure.org/stf/smashstack.html
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = buffer1 + 12;
(*ret) += 8;
}
void main() {
int x;
x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}
The corresponding assembly code for main function is:
Dump of assembler code for function main:
0x8000490 <main>: pushl %ebp
0x8000491 <main+1>: movl %esp,%ebp
0x8000493 <main+3>: subl $0x4,%esp
0x8000496 <main+6>: movl $0x0,0xfffffffc(%ebp)
0x800049d <main+13>: pushl $0x3
0x800049f <main+15>: pushl $0x2
0x80004a1 <main+17>: pushl $0x1
0x80004a3 <main+19>: call 0x8000470 <function>
0x80004a8 <main+24>: addl $0xc,%esp
0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp)
0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax
0x80004b5 <main+37>: pushl %eax
0x80004b6 <main+38>: pushl $0x80004f8
0x80004bb <main+43>: call 0x8000378 <printf>
0x80004c0 <main+48>: addl $0x8,%esp
0x80004c3 <main+51>: movl %ebp,%esp
0x80004c5 <main+53>: popl %ebp
0x80004c6 <main+54>: ret
0x80004c7 <main+55>: nop
In the variable ret, they are pointing ret to the address of the next instruction to be run. I cannot understand that just by keeping the next instruction in the ret variable, how is the program going to jump to this next location?
I know how buffer overflow works, but by changing the ret variable, how is this doing buffer overflow?
Even by considering that this is a dummy program and is just supposed to let us understand how buffer overflow works, changing the ret variable seems wrong.
Explanation of how this is an example of a buffer overrun:
The local variables of function, including buffer1, are on the stack, along with the return address, which is calculated as being 12 bytes beyond buffer1. This is an example of a buffer overrun because writing to an address 12 bytes beyond buffer1 is writing outside the proper bounds of buffer1. By replacing the return address by a number 8 larger than it was, when function finishes, rather than popping off a return to the statement following the function call as usual (x = 1;, in this case), the return address will be 8 bytes later (at the printf statement, in this case).
Skipping the x = 1; statement is not the buffer overflow -- it's the effect of the buffer overflow which modified the return address.
Note on the calculation of 8 as the proper offset for skipping x = 1; statement:
See also FrankH's careful reevaluation of the calculation of 8 as the proper offset to add to the return address to achieve the intent of skipping x = 1;. His findings contradict the GDB-based analysis of the insecure.org source article. Regardless of this detail, the explanation of how a buffer overrun is used to change the return address remains the same -- it's just a question of what to write into the overrun.
For completeness, here is the GDB-based analysis of the insecure.org source article:
What we have done is add 12 to buffer1[]'s address. This new
address is where the return address is stored. We want to skip pass
the assignment to the printf call. How did we know to add 8 to the
return address? We used a test value first (for example 1), compiled
the program, and then started gdb:
[aleph1]$ gdb example3
GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(no debugging symbols found)...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000490 <main>: pushl %ebp
0x8000491 <main+1>: movl %esp,%ebp
0x8000493 <main+3>: subl $0x4,%esp
0x8000496 <main+6>: movl $0x0,0xfffffffc(%ebp)
0x800049d <main+13>: pushl $0x3
0x800049f <main+15>: pushl $0x2
0x80004a1 <main+17>: pushl $0x1
0x80004a3 <main+19>: call 0x8000470 <function>
0x80004a8 <main+24>: addl $0xc,%esp
0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp)
0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax
0x80004b5 <main+37>: pushl %eax
0x80004b6 <main+38>: pushl $0x80004f8
0x80004bb <main+43>: call 0x8000378 <printf>
0x80004c0 <main+48>: addl $0x8,%esp
0x80004c3 <main+51>: movl %ebp,%esp
0x80004c5 <main+53>: popl %ebp
0x80004c6 <main+54>: ret
0x80004c7 <main+55>: nop
We can see that when calling function() the RET will be 0x8004a8,
and we want to jump past the assignment at 0x80004ab. The next
instruction we want to execute is the at 0x8004b2. A little math
tells us the distance is 8 bytes.
A little better math tells us that the distance is 0x8004a8 - 0x8004b2 = 0xA or 10 bytes, not 8 bytes.
The layout on the stack is like this (addresses downwards - as stacks grow):
buffer + ... value found description
=================================================================================
+24 3 # from main, pushl $0x3
+20 2 # from main, pushl $0x2
+16 1 # from main, pushl $0x1
+12 <main+24> # from main, call 0x8000470 <function>
+8 <frameptr main> # from function, pushl %ebp
+4 %ebp(function) padding (3 bytes) # ABI - compiler will not _pack_ vars
+0 buffer[5];
... buffer1[12]; # might be optimized out (unused)
... int *ret # might be optimized out (reg used instead)
The tricky thing is that buffer starts at a four-byte-aligned address even though it's not sized a multiple of four bytes. The "effective size" is eight bytes, so if you add eight bytes to the start of it, you find the saved framepointer, and if you go another four bytes down, the saved return address (which, according to your disassembly, is main+0x24 / 0x80004a8. Adding 8 to that jumps "into the middle" of two intructions, the result is garbage - you're not skipping the x = 1 statement.
I'm still learning assembly and C, but now, I'm trying to understand how the compiler works. I have here a simple code:
int sub()
{
return 0xBEEF;
}
main()
{
int a=10;
sub();
}
Now I know already how the CPU works, jumping into the frames and subroutines etc.
What i don't understand is where the program "store" their local variables. In this case in the main's frame?
Here is the main frame on debugger:
0x080483f6 <+0>: push %ebp
0x080483f7 <+1>: mov %esp,%ebp
0x080483f9 <+3>: sub $0x10,%esp
=> 0x080483fc <+6>: movl $0xa,-0x4(%ebp)
0x08048403 <+13>: call 0x80483ec <sub>
0x08048408 <+18>: leave
0x08048409 <+19>: ret
I have in "int a=10;" a break point that's why the the offset 6 have that arrow.
So, the main's function starts like the others pushing the ebp bla bla bla, and then i don't understand this:
0x080483f9 <+3>: sub $0x10,%esp
=> 0x080483fc <+6>: movl $0xa,-0x4(%ebp)
why is doing sub in esp? is the variable 'a' on the stack with the offset -0x4 of the stack pointer?
just to clear the ideas here :D
Thanks in advance!
0x080483f9 <+3>: sub $0x10,%esp
You will find such an instruction in every function. Its purpose is to create a stack frame of the appropriate size so that the function can store its locals (remember that the stack grows backward!).
The stack frame is a little too big in this case. This is because gcc (starting from 2.96) pads stack frames to 16 bytes boundaries by default to account for SSEx instructions which require packed 128-bit vectors to be aligned to 16 bytes. (reference here).
=> 0x080483fc <+6>: movl $0xa,-0x4(%ebp)
This line is initializing a to the correct value (0xa = 10d). Locals are always referred with an offset relative to ebp, which marks the beginning of the stack frame (which is therefore included between ebp and esp).