4

I am learning assembly and I have this assembly code and having much trouble understanding it can someone clarify it?

Dump of assembler code for function main:
0x080483ed <+0>:    push   ebp
0x080483ee <+1>:    mov    ebp,esp
0x080483f0 <+3>:    sub    esp,0x10
0x080483f3 <+6>:    mov    DWORD PTR [ebp-0x8],0x0
0x080483fa <+13>:   mov    eax,DWORD PTR [ebp-0x8]
0x080483fd <+16>:   add    eax,0x1
0x08048400 <+19>:   mov    DWORD PTR [ebp-0x4],eax
0x08048403 <+22>:   leave  
0x08048404 <+23>:   ret

Until now, my understood knowledge is the following:

Push something (don't know what) in ebp register. then move content of esp register into ebp (I think the data of ebp should be overwritten), then subtract 10 from the esp and store it in the esp (The function will take 10 byte, This reg is never used again, so no point of doing this operation). Now assign value 0 to the address pointed by 8 bytes less than ebp.

Now store that address into register eax. Now add 1 to the value pointed by eax (the previous value is lost). Now store the eax value on [ebp-0x4], then leave to the return address of main.

Here is my C code for the above program:

int main(){

int x=0;
int y = x+1;
}

Now, can someone figure out if I am wrong at anything,and I also don't understand the mov at <+13> it adds 1 to the addrs ebp-0x8, but that is the address of int x so, x no longer contain 0. Where am I wrong?

nrz
  • 10,435
  • 4
  • 39
  • 71
Tamim Addari
  • 7,591
  • 9
  • 40
  • 59
  • 3
    Why not pick up a tutorial on assembly? `push ebp` doesn't push something into `ebp`, it pushes the current `ebp` contents on the stack, so that it can be restored later. – user4815162342 Nov 25 '13 at 10:11
  • Take a read of this: http://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames – Rob Nov 25 '13 at 10:18

2 Answers2

21

first of all, push ebp and then mov ebp, esp are two instructions that are common at the beggining of a procedure. ESP register is an indicator for the top of the stack - so it changes constantly as the stack grows or shrinks. EBP is a helping register here. First we push content of ebp on stack. then we copy ESP (current stack top adress) to ebp - that is why when we refer to other items on the stack, we use constant value of ebp (and not changing one of esp).

 sub esp, 0x10 ; means we reserve 16 bytes on the stack (0x10 is 16 in hex) 

now for the real fun:

 mov    DWORD PTR [ebp-0x8],0x0 ; remember ebp was showing on the stack
                                ; top BEFORE reserving 16 bytes.
                                ; DWORD PTR means Double-word property which is 32 bits.
                                ; so the whole instruction means
                                ; "move 0 to the 32 bits of the stack in a place which 
                                ; starts with the adress ebp-8.
                                ; this is our`int x = 0`

 mov    eax,DWORD PTR [ebp-0x8] ; send x to EAX register.
 add    eax,0x1`                ; add 1 to the eax register
 mov    DWORD PTR [ebp-0x4],eax ; send the result (which is in eax) to the stack adress
                                ; [ebp-4]
 leave                          ; Cleanup stack (reverse the "mov ebp, esp" from above).
 ret                            ; let's say this instruction returns to the program, (it's slightly more
                                ; complicated than that)

Hope this helps! :)

Simon
  • 2,643
  • 3
  • 40
  • 61
  • `(0x10 is 32 in hex)` Are you sure? –  Nov 25 '13 at 10:35
  • Here is the two question, what is the profit of `sub esp, 0x10` ,, this command neither does anything nor it is needed later, and the second question is, registers doesn't contain only address.It can contain values too? like in `add eax,0x1` ? – Tamim Addari Nov 25 '13 at 10:36
  • @skwllsp I edited that, of course it's 16 in hex. @TamimAdDari `sub esp 0x10` changes the top-of-the-stack pointer. The basic rule you have to remember is that "The stack grows in the direction of lower adresses" - so by subtracting 16 from the stack you actually reserve 16 bytes on the stack (this allows you to write x and y there as two 32-bit sequences) – Simon Nov 25 '13 at 10:41
  • @TamimAdDari yes, registers can contain values. EAX for example is a 32-bit register - which means it can store binary representations of numbers up to 4 billion. More on registers here: http://www.eecg.toronto.edu/~amza/www.mindsec.com/files/x86regs.html – Simon Nov 25 '13 at 10:43
  • @TamimAdDari: the stack pointer is being adjusted so that arguments to any function `main()` calls could be pushed on the stack without overwriting/clashing with `x` and `y`. You're correct in observing it's not actually needed/used. Some registers are specifically designed to hold pointers (e.g. "sp" stands for stack pointer, while some are general purpose and can contain data or pointers); some assembly instructions will allow use of data, pointers or counters only if they're in supported registers. – Tony Delroy Nov 25 '13 at 10:46
  • This means that this function won't be able to access a memory beyond esp-10x, So basically esp is the bottom of the stack and ebp is the top of the stack – Tamim Addari Nov 25 '13 at 10:49
  • @TamimAdDari on the contrary: esp shows ALWAYS the top of the stack, and you saved ebp in the second line (top of the stack will be lower in the memory than its other elements, because that's the way it was designed) and if for example you try to refer to the adress [esp - 10]- you will be refering to the memory which is NOT the stack anymore- it's just some random memory which will give random results. – Simon Nov 25 '13 at 10:52
  • @TamimAdDari, the function can use as much memory as it likes, but only 16 bytes were reserved here for local variables. The `ebp, esp` pair is not a limitation per se, so the code can address any value it wants. Doing so is usually a buffer overflow error or a stack corruption though. So your understanding that the function can not address beyond esp-10x is wrong and you should not think of it as such. It SHOULD not address beyond that. – Devolus Nov 25 '13 at 10:57
  • Hello, is it possible to have some resources to understand this assembly code clearly just like you answered? Thanks. @Simon – Tasfia Sharmin Feb 18 '22 at 19:50
7
0x080483ed <+0>:    push   ebp
0x080483ee <+1>:    mov    ebp,esp

Setting up the stack frame. Save the old base pointer and set the top of the stack as the new base pointer. This allows local variables and arguments within this function to be referenced relative to ebp (base pointer). The advantage of this is that its value is stable unlike esp which is affected by pushes and pops.

0x080483f0 <+3>:    sub    esp,0x10

On the x86 platform the stack 'grows' downwards. Generally speaking this means esp has a lower value (address in memory) than ebp. When ebp == esp the stack has not reserved any memory for local variables. This does not mean it is 'empty' - a common usage of a stack is [ebp+0x8] for instance. In this case the code is looking for something on the stack which was previously pushed on prior to the call (this could be arguments in the stdcall convention).

In this case the stack is extended by 16 bytes. In this case more space is reserved than necessary for alignment purposes:

0x080483f3 <+6>:    mov    DWORD PTR [ebp-0x8],0x0

The 4 bytes at [ebp-0x8] are initialised to the value 0. This is your x local variable.

0x080483fa <+13>:   mov    eax,DWORD PTR [ebp-0x8]

The 4 bytes at [ebp-0x8] are moved to a register. Arithmetic opcodes can not operate with two memory operands. Data needs to be moved to a register first before the arithmetic is performed. eax now holds the value of your x variable.

0x080483fd <+16>:   add    eax,0x1

The value of eax is increased so it now holds the value x + 1.

0x08048400 <+19>:   mov    DWORD PTR [ebp-0x4],eax

Stores the calculated value back on the stack. Notice the local variable is now [ebp-0x4] - this is your y variable.

0x08048403 <+22>:   leave  

Destroys the stack frame. Essentially maps to pop ebp and restores the old stack base pointer.

0x08048404 <+23>:   ret 

Pops the top of the stack treating the value as a return address and sets the program pointer (eip) to this value. The return address typically holds the address of the instruction directly after the call instruction that brought execution into this function.

Community
  • 1
  • 1
Mike Kwan
  • 24,123
  • 12
  • 63
  • 96
  • "In this case more space is reserved than necessary for alignment purposes". Ah, this is enlightening. I was wondering why the number of bytes used up by locals doesn't always add up to the number of bytes reserved. – Jet Blue Feb 08 '19 at 22:50