This is my C function:
int reg(struct my_callback_struct *p, int data)
{
return p->data = data;
}
This is the same in assembly:
0x000000000040057d <+0>: push %rbp
0x000000000040057e <+1>: mov %rsp,%rbp
0x0000000000400581 <+4>: mov %rdi,-0x8(%rbp)
0x0000000000400585 <+8>: mov %esi,-0xc(%rbp)
0x0000000000400588 <+11>: mov -0x8(%rbp),%rax
0x000000000040058c <+15>: mov -0xc(%rbp),%edx
0x000000000040058f <+18>: mov %edx,(%rax)
0x0000000000400591 <+20>: mov -0x8(%rbp),%rax
0x0000000000400595 <+24>: mov (%rax),%eax
0x0000000000400597 <+26>: pop %rbp
0x0000000000400598 <+27>: retq
I think I understand what's going on. $rdi holds the pointer (address) and $esi the number 12.
This is how I called the function:
p->callback_func(p,12);
What I don't understand is:
0x0000000000400591 <+20>: mov -0x8(%rbp),%rax
Because on <+11> we have already filled $rax with the pointer address. Why load it twice?
Indeed, the code is correct in that the instructions perform the functions called for by the C code. But not even the most trivial of optimizations have been performed.
This is easily corrected by turning on some level of compiler optimization. Probably the first level will clean up redundant loads no matter which compiler is used.
Note that extreme levels of optimization can result in correct code which is very difficult to follow.
Related
I've got an NX enabled, canary enabled x64 ELF and can only view the assembly, not the source code but I do know its written in c. When run, it only accepts command line args and returns nothing. Inside of the main function there's only one function call of note, to evil;
0x000000000040060e <+0>: push rbp
0x000000000040060f <+1>: mov rbp,rsp
0x0000000000400612 <+4>: sub rsp,0x10
0x0000000000400616 <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000400619 <+11>: mov QWORD PTR [rbp-0x10],rsi
0x000000000040061d <+15>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400621 <+19>: add rax,0x8
0x0000000000400625 <+23>: mov rax,QWORD PTR [rax]
0x0000000000400628 <+26>: mov rdi,rax
0x000000000040062b <+29>: call 0x4005a7 <evil>
0x0000000000400630 <+34>: mov eax,0x0
0x0000000000400635 <+39>: leave
0x0000000000400636 <+40>: ret
and inside evil, it pulls the command line args and checks... something... against 0xdeadbeef and if they match, passes a "you win!" message, then verifies the canary and if either of those fail, it runs call __stack_chk_fail#plt, which indirects through a GOT entry which is in writeable memory somewhere.
0x00000000004005a7 <+0>: push rbp
0x00000000004005a8 <+1>: mov rbp,rsp
0x00000000004005ab <+4>: sub rsp,0x70
0x00000000004005af <+8>: mov QWORD PTR [rbp-0x68],rdi
0x00000000004005b3 <+12>: mov rax,QWORD PTR fs:0x28
0x00000000004005bc <+21>: mov QWORD PTR [rbp-0x8],rax
0x00000000004005c0 <+25>: xor eax,eax
0x00000000004005c2 <+27>: mov DWORD PTR [rbp-0x54],0x0
0x00000000004005c9 <+34>: mov rdx,QWORD PTR [rbp-0x68]
0x00000000004005cd <+38>: lea rax,[rbp-0x50]
0x00000000004005d1 <+42>: mov rsi,rdx
0x00000000004005d4 <+45>: mov rdi,rax
0x00000000004005d7 <+48>: mov eax,0x0
0x00000000004005dc <+53>: call 0x4004b0 <sprintf#plt>
0x00000000004005e1 <+58>: mov eax,DWORD PTR [rbp-0x54]
0x00000000004005e4 <+61>: cmp eax,0xdeadbeef
0x00000000004005e9 <+66>: jne 0x4005f7 <evil+80>
0x00000000004005eb <+68>: lea rdi,[rip+0xd6] # 0x4006c8
0x00000000004005f2 <+75>: call 0x400490 <puts#plt>
0x00000000004005f7 <+80>: nop
0x00000000004005f8 <+81>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004005fc <+85>: xor rax,QWORD PTR fs:0x28
0x0000000000400605 <+94>: je 0x40060c <evil+101>
0x0000000000400607 <+96>: call 0x4004a0 <__stack_chk_fail#plt>
0x000000000040060c <+101>: leave
0x000000000040060d <+102>: ret
In ghidra and with cyclic strings I'm able to verify that the buffer is 72 characters. I've found a bunch of old info from liveoverflow that's about 5 years old now with the exact same problem (protostar format0), except his buffer is 64. For some reason, this buffer mismatch is causing me all sorts of problems I believe.
I've tried hundreds of inputs to achieve the winning statement;
I've tried overwriting the buffer of 72 with 72 A's followed by variations of 0xdeadbeef such as little endian, strings, hex, etc
I've played around with the buffer and offset, so for example putting 0xdeadbeef and then the buffer after, or putting 72 A's with a nop sled of 8 or so after it then 0xdeadbeef
I've tried following liveoverflow's method of overwriting __stack_chk_fail's GOT entry completely, via a format-string vulnerability like %1640d which you can see here, but either have the wrong numbers or am misunderstanding how it works/if it will work on my binary and machine
None of these have given me the winning statement, and I'd really like to understand the why and how and the assembly reasoning behind it.
edit
I've noticed that if I pass python -c 'print ("%x."*5)' as an argument, my stack fills up with ffffe374.f7fa3658.4006b0.f7fcbb60.0, which repeats if I up the number from 5. When checking these with x 0x4006b0, 0x4006b0 is the only value that reuturns anything of use - 0x4006b0 <__libc_csu_fini>: "\363", <incomplete sequence \303>. I've tried creating a rop chain using this and looking up ret2csu guides but I've had no luck so far.
I am trying to understand reasoning for seg fault with dissemble code.
Case 1.
char *p = NULL;
printf("%s", p);
O/p: No crash. it give me null. Further looking at disassemble code, it shows this one.
Dump of assembler code for function printf#plt:
0x00000000004003b8 <+0>: jmpq *0x2004aa(%rip) # 0x600868 <printf#got.plt>
0x00000000004003be <+6>: pushq $0x0
0x00000000004003c3 <+11>: jmpq 0x4003a8
End of assembler dump.
While i am trying to further go beyond this but do not know how to move to next set of instructions and what exactly it does.
Case 2.
int
main()
{
char *p = NULL;
printf("%s\n", p);
}
It leads to seg fault.
Disassemble code:
Dump of assembler code for function main:
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: sub $0x10,%rsp
0x00000000004004cc <+8>: movq $0x0,-0x8(%rbp)
0x00000000004004d4 <+16>: mov -0x8(%rbp),%rax
0x00000000004004d8 <+20>: mov %rax,%rdi
0x00000000004004db <+23>: callq 0x4003b8 <puts#plt>
0x00000000004004e0 <+28>: leaveq
0x00000000004004e1 <+29>: retq
End of assembler dump.
(gdb) disassemble puts
Dump of assembler code for function puts#plt:
0x00000000004003b8 <+0>: jmpq *0x2004aa(%rip) # 0x600868 <puts#got.plt>
0x00000000004003be <+6>: pushq $0x0
0x00000000004003c3 <+11>: jmpq 0x4003a8
End of assembler dump.
Can u please help me to identify what assembler instruction is leading to seg fault?
0x00000000004003b8 <+0>: jmpq *0x2004aa(%rip) # 0x600868 <puts#got.plt>
Two important codewords here:
GOT -> Global Offset Table
PLT -> Procedure Linkage Table
This indicates it calls puts from dynamic library. Address of puts is not know at disassembly only time. Program must be run in order to allow dynamic linker bind address of library function to PLT slot.
What you need is:
(gdb) start
Temporary breakpoint 1 at 0x40053e: file c.c, line 9.
Starting program: /home/josef/DEVEL/test/test/a.out
Temporary breakpoint 1, main () at c.c:9
9 char *p = NULL;
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400536 <+0>: push %rbp
0x0000000000400537 <+1>: mov %rsp,%rbp
0x000000000040053a <+4>: sub $0x10,%rsp
=> 0x000000000040053e <+8>: movq $0x0,-0x8(%rbp)
0x0000000000400546 <+16>: mov -0x8(%rbp),%rax
0x000000000040054a <+20>: mov %rax,%rdi
0x000000000040054d <+23>: callq 0x400410 <puts#plt>
0x0000000000400552 <+28>: leaveq
0x0000000000400553 <+29>: retq
End of assembler dump.
(gdb) disassemble puts
Dump of assembler code for function _IO_puts:
0x00007ffff7a84d60 <+0>: push %r12
0x00007ffff7a84d62 <+2>: mov %rdi,%r12
0x00007ffff7a84d65 <+5>: push %rbp
0x00007ffff7a84d66 <+6>: push %rbx
0x00007ffff7a84d67 <+7>: callq 0x7ffff7a9d9b0 <strlen>
0x00007ffff7a84d6c <+12>: mov 0x34fafd(%rip),%rbx # 0x7ffff7dd4870 <stdout>
0x00007ffff7a84d73 <+19>: mov %rax,%rbp
0x00007ffff7a84d76 <+22>: mov (%rbx),%eax
0x00007ffff7a84d78 <+24>: mov %rbx,%rdi
0x00007ffff7a84d7b <+27>: and $0x8000,%eax
0x00007ffff7a84d80 <+32>: jne 0x7ffff7a84ddf <_IO_puts+127>
0x00007ffff7a84d82 <+34>: mov 0x88(%rbx),%r8
......
Now you see what is inside puts. You can go forward and disassemble strlen
(gdb) disassemble strlen
Dump of assembler code for function strlen:
0x00007ffff7a9d9b0 <+0>: pxor %xmm8,%xmm8
0x00007ffff7a9d9b5 <+5>: pxor %xmm9,%xmm9
0x00007ffff7a9d9ba <+10>: pxor %xmm10,%xmm10
0x00007ffff7a9d9bf <+15>: pxor %xmm11,%xmm11
0x00007ffff7a9d9c4 <+20>: mov %rdi,%rax
0x00007ffff7a9d9c7 <+23>: mov %rdi,%rcx
0x00007ffff7a9d9ca <+26>: and $0xfff,%rcx
0x00007ffff7a9d9d1 <+33>: cmp $0xfcf,%rcx
0x00007ffff7a9d9d8 <+40>: ja 0x7ffff7a9da40 <strlen+144>
0x00007ffff7a9d9da <+42>: movdqu (%rax),%xmm12
0x00007ffff7a9d9df <+47>: pcmpeqb %xmm8,%xmm12
0x00007ffff7a9d9e4 <+52>: pmovmskb %xmm12,%edx
0x00007ffff7a9d9e9 <+57>: test %edx,%edx
0x00007ffff7a9d9eb <+59>: je 0x7ffff7a9d9f1 <strlen+65>
......
Good luck with analyzing all the code :)
I'm doing some security CTF practice and have this problem which I am stuck on. This is the C source code of the compiled program:
int main(int i, long **a) {
if(*a[1] * 0x1064deadbeef4601u == 0xd1038d2e07b42569u)
//exec shell
return 0;
}
Things confusing me:
long** main argument won't compile when I turn off gcc flags so I can't reproduce problem on my computer. Is this a different compiler being used? Compiled program runs fine on CTF server.
program repeatedly overflows during multiplication before testing equality. I tried using a linear congruence equation (mod 2^64) and extended euclidian algorithm to find the required x input but this did not work for me.
Can someone help me out with this? I'm trying to find *a[1], the correct program argument.
disassembling main in gdb gives:
(gdb) disas main
Dump of assembler code for function main:
0x000000000040050c <+0>: push %rbp
0x000000000040050d <+1>: mov %rsp,%rbp
0x0000000000400510 <+4>: sub $0x10,%rsp
0x0000000000400514 <+8>: mov %edi,-0x4(%rbp)
0x0000000000400517 <+11>: mov %rsi,-0x10(%rbp)
0x000000000040051b <+15>: mov -0x10(%rbp),%rax
0x000000000040051f <+19>: add $0x8,%rax
0x0000000000400523 <+23>: mov (%rax),%rax
0x0000000000400526 <+26>: mov (%rax),%rax
0x0000000000400529 <+29>: mov %rax,%rdx
0x000000000040052c <+32>: movabs $0x1064deadbeef4601,%rax
=> 0x0000000000400536 <+42>: imul %rax,%rdx
0x000000000040053a <+46>: movabs $0xd1038d2e07b42569,%rax
0x0000000000400544 <+56>: cmp %rax,%rdx
0x0000000000400547 <+59>: jne 0x400562 <main+86>
0x0000000000400549 <+61>: mov $0x0,%edx
0x000000000040054e <+66>: mov $0x40061c,%esi
0x0000000000400553 <+71>: mov $0x40061f,%edi
0x0000000000400558 <+76>: mov $0x0,%eax
0x000000000040055d <+81>: callq 0x4003f0 <execl#plt>
0x0000000000400562 <+86>: mov $0x0,%eax
0x0000000000400567 <+91>: leaveq
0x0000000000400568 <+92>: retq
End of assembler dump.
There's no real overflow here -- it's just doing a multiply mod 264 and testing that the result is what is expected. To figure out the desired input, you just need to find the inverse (mod 264) of the factor 0x1064deadbeef4601, and multiply it by 0xd1038d2e07b42569
For power-of-2 modulus, it's generally easiest to find the inverse using Euler's formula:
x-1 (mod m) ≡ xφ(m)-1 (mod m)
when m is a power of two, φ(2k) = 2k-1, so you can calculate this with just 2(k-1) multiplies.
So for my class project I am given a binary and what I have to do is bypass the security authentication. Now, once you change the binary to bypass the authentication, you have to create a C program that will replicate the "same" binary. Now, I have been struggling to understand what this area of the assembly code dump that I obtained does.
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
I'm not very familiar with assembly, but I got most of it figured out. This is the original/entire assembly dump that I got using GDB.
0x08048a23 <function8+0>: push %ebp
0x08048a24 <function8+1>: mov %esp,%ebp
0x08048a26 <function8+3>: sub $0x28,%esp
0x08048a29 <function8+6>: movl $0xd6a1a,-0x18(%ebp)
0x08048a30 <function8+13>: mov 0x8(%ebp),%eax
0x08048a33 <function8+16>: mov %eax,-0x14(%ebp)
0x08048a36 <function8+19>: mov 0xc(%ebp),%eax
0x08048a39 <function8+22>: mov %eax,-0x10(%ebp)
0x08048a3c <function8+25>: movl $0x0,-0xc(%ebp)
0x08048a43 <function8+32>: mov -0xc(%ebp),%eax
0x08048a46 <function8+35>: mov %eax,%edx
0x08048a48 <function8+37>: sar $0x1f,%edx
0x08048a4b <function8+40>: idivl -0x18(%ebp)
0x08048a4e <function8+43>: imul -0x14(%ebp),%eax
0x08048a52 <function8+47>: imul -0x10(%ebp),%eax
0x08048a56 <function8+51>: mov %eax,-0xc(%ebp)
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
0x08048a73 <function8+80>: test %eax,%eax
0x08048a75 <function8+82>: jne 0x8048a8d <function8+106>
0x08048a77 <function8+84>: mov $0x8048cdc,%eax
0x08048a7c <function8+89>: mov -0xc(%ebp),%edx
0x08048a7f <function8+92>: mov %edx,0x4(%esp)
0x08048a83 <function8+96>: mov %eax,(%esp)
0x08048a86 <function8+99>: call 0x8048414 <printf#plt>
0x08048a8b <function8+104>: jmp 0x8048a99 <function8+118>
0x08048a8d <function8+106>: movl $0x8048cfa,(%esp)
0x08048a94 <function8+113>: call 0x8048424 <puts#plt>
0x08048a99 <function8+118>: mov -0xc(%ebp),%eax
0x08048a9c <function8+121>: leave
0x08048a9d <function8+122>: ret
And so far I have managed to convert it to this in C:
int function8(one, two){
int a = 879130;
int b = one;
int c = two;
int d = 0;
d = (d / a * b * c);
if(strcmp(b, (d + 4)) == 0){
printf("You may enter using token %d", d);
}
else{
puts("You may not enter.");
}
return d;
}
int main(){
int one, two = 0;
function8(one, two);
}
I am know that
0x08048a59 <function8+54>: mov 0x8049e50,%eax
and
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
are pointing to a particular address and value respectively (correct me if I'm wrong), but don't know if I have to call it or not. If so, how do I call that particular address?
Any help would be appreciated!
Just in case you guys need it, this is my current output when I create a binary using GCC and then run GDB to get the dump:
0x08048434 <function8+0>: push %ebp
0x08048435 <function8+1>: mov %esp,%ebp
0x08048437 <function8+3>: sub $0x28,%esp
0x0804843a <function8+6>: movl $0xd6a1a,-0x18(%ebp)
0x08048441 <function8+13>: mov 0x8(%ebp),%eax
0x08048444 <function8+16>: mov %eax,-0x14(%ebp)
0x08048447 <function8+19>: mov 0xc(%ebp),%eax
0x0804844a <function8+22>: mov %eax,-0x10(%ebp)
0x0804844d <function8+25>: movl $0x0,-0xc(%ebp)
0x08048454 <function8+32>: mov -0xc(%ebp),%eax
0x08048457 <function8+35>: mov %eax,%edx
0x08048459 <function8+37>: sar $0x1f,%edx
0x0804845c <function8+40>: idivl -0x18(%ebp)
0x0804845f <function8+43>: imul -0x14(%ebp),%eax
0x08048463 <function8+47>: imul -0x10(%ebp),%eax
0x08048467 <function8+51>: mov %eax,-0xc(%ebp)
0x0804846a <function8+54>: mov -0xc(%ebp),%eax
0x0804846d <function8+57>: add $0x4,%eax
0x08048470 <function8+60>: mov %eax,0x4(%esp)
0x08048474 <function8+64>: mov -0x14(%ebp),%eax
0x08048477 <function8+67>: mov %eax,(%esp)
0x0804847a <function8+70>: call 0x8048364 <strcmp#plt>
0x0804847f <function8+75>: test %eax,%eax
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
0x08048483 <function8+79>: mov $0x80485a0,%eax
0x08048488 <function8+84>: mov -0xc(%ebp),%edx
0x0804848b <function8+87>: mov %edx,0x4(%esp)
0x0804848f <function8+91>: mov %eax,(%esp)
0x08048492 <function8+94>: call 0x8048344 <printf#plt>
0x08048497 <function8+99>: jmp 0x80484a5 <function8+113>
0x08048499 <function8+101>: movl $0x80485bd,(%esp)
0x080484a0 <function8+108>: call 0x8048354 <puts#plt>
0x080484a5 <function8+113>: mov -0xc(%ebp),%eax
0x080484a8 <function8+116>: leave
0x080484a9 <function8+117>: ret
strcmp compares two strings that are passed in as pointers. The code here :
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
is passing two pointers to strcmp, both of which are static/global data (not local, like you have in your C code). One is at 0x8048cd4 (that's the address of the string.. it's probably something in quotes : "example"). The other is a pointer load + 4 that's dereferenced. I'd recommend : (1) look at those addresses to see what's stored in them, and (2) if you're confused by the assembly pointer chasing, try writing simple C programs that call strcmp and seeing the resulting assembly.
good luck.
This is the part who is calling the strcmp as drivingon9 pointed.
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
The result value of the strcmp will be stored in the register EAX.
Thats why we have a test eax,eax in the code bellow:
0x0804847f <function8+75>: test %eax,%eax
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
0x08048483 <function8+79>: mov $0x80485a0,%eax
0x08048488 <function8+84>: mov -0xc(%ebp),%edx
The test eax, eax tests if eax register is equal to 0.
i'm not sure what part of the code will let you do what you want, but you can try change the line
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
and change the instruction to a je(JUMP IF EQUAL) or a incondicional jump(JMP)
I have been trying to skip an instruction by changing the return address through stack smashing. The following code skips a++ in main and prints an output of "1 3". I have executed this code on a 32-bit intel machine.
#include<stdio.h>
void fun(int a,int b) {
// buffer
char buf[8];
char *p;
p = (char *)buf+24;
*p=*p+5;
return;
}
int main() {
int a=1,b=2;
fun(a,b);
a++;
b++;
printf("%d %d",a,b);
}
I am unable to understand why return address is stored at a displacement of 24 bytes from starting address of buf. I have tried executing the same code on a different 32-bit intel machine and I had to use a displacement of 20 bytes instead of 24 bytes. I have put my understanding in the following figure. I am not sure about what fills the gap represented by "?" in the figure. Does gcc put any canary value there or am I missing something ?
Link to figure: http://www.cse.iitb.ac.in/~shashankr/stack.png
Smashing the stack example3.c confusion asked the same question but could not explain the reason for displacement in general.
The following figure gives a view of the stack obtained by placing a breakpoint in function.
(source: shashankr at www.cse.iitb.ac.in)
The following is the assembly code for main and fun:
Dump of assembler (fun):
0x08048434 <+0>: push %ebp
0x08048435 <+1>: mov %esp,%ebp
0x08048437 <+3>: sub $0x18,%esp
0x0804843a <+6>: mov %gs:0x14,%eax
0x08048440 <+12>: mov %eax,-0xc(%ebp)
0x08048443 <+15>: xor %eax,%eax
0x08048445 <+17>: lea -0x14(%ebp),%eax
0x08048448 <+20>: add $0x18,%eax
0x0804844b <+23>: mov %eax,-0x18(%ebp)
0x0804844e <+26>: mov -0x18(%ebp),%eax
0x08048451 <+29>: movzbl (%eax),%eax
0x08048454 <+32>: add $0x5,%eax
0x08048457 <+35>: mov %eax,%edx
0x08048459 <+37>: mov -0x18(%ebp),%eax
0x0804845c <+40>: mov %dl,(%eax)
0x0804845e <+42>: mov -0xc(%ebp),%eax
0x08048461 <+45>: xor %gs:0x14,%eax
0x08048468 <+52>: je 0x804846f <fun+59>
0x0804846a <+54>: call 0x8048350 <__stack_chk_fail#plt>
0x0804846f <+59>: leave
0x08048470 <+60>: ret
Dump of assembler (main)
0x08048471 <+0>: push %ebp
0x08048472 <+1>: mov %esp,%ebp
0x08048474 <+3>: and $0xfffffff0,%esp
0x08048477 <+6>: sub $0x20,%esp
0x0804847a <+9>: movl $0x1,0x18(%esp)
0x08048482 <+17>: movl $0x2,0x1c(%esp)
0x0804848a <+25>: mov 0x1c(%esp),%eax
0x0804848e <+29>: mov %eax,0x4(%esp)
0x08048492 <+33>: mov 0x18(%esp),%eax
0x08048496 <+37>: mov %eax,(%esp)
0x08048499 <+40>: call 0x8048434 <fun>
0x0804849e <+45>: addl $0x1,0x18(%esp)
0x080484a3 <+50>: addl $0x1,0x1c(%esp)
0x080484a8 <+55>: mov $0x80485a0,%eax
0x080484ad <+60>: mov 0x1c(%esp),%edx
0x080484b1 <+64>: mov %edx,0x8(%esp)
0x080484b5 <+68>: mov 0x18(%esp),%edx
0x080484b9 <+72>: mov %edx,0x4(%esp)
0x080484bd <+76>: mov %eax,(%esp)
0x080484c0 <+79>: call 0x8048340 <printf#plt>
0x080484c5 <+84>: leave
0x080484c6 <+85>: ret
I believe the answer is nothing. Are you having different gcc versions? Anyway a compiler is allowed to allocate a bit more stack than necessary. Perhaps it's the initial "guess" based on the number of variables, but which isn't reduced by optimization stages, which are allowed to move any variable to a register. Or it's some reservoir to save ecx,ebp or other registers in case the subroutine needs to.
There's anyway one fixed address variable to overcome the problem: a.
Return address = &a[-1].