(gdb) run `python -c "print('A'*524+'\x55\x61\x55\x56')"`
Starting program: /home/xxx/Documents/ethical_hacking/buffer_overflow/simplebuffer `python -c "print('A'*524+'\x55\x61\x55\x56')"`
Off to 0x56556155
Program received signal SIGSEGV, Segmentation fault.
0x56556155 in register_tm_clones ()
(gdb) run `python -c "print('A'*524+'\xcd\x61\x55\x56')"`
Starting program: /home/nepalidai/Documents/ethical_hacking/buffer_overflow/simplebuffer `python -c "print('A'*524+'\xcd\x61\x55\x56')"`
Off to 0x55618dc3
Program received signal SIGSEGV, Segmentation fault.
0x55618dc3 in ?? ()
When I include \x55\x61\x55\x56 in my payload, I get the segmentation fault at 0x56556155, but when I include \xcd\x61\x55\x56 in my payload, I get the segmentation fault at 0x55618dc3.
I was thinking \xcd\x61\x55\x56 would give seg. fault at 0x565561cd.
I can only reproduce this when I use gdb in Arch Linux. In Kali linux this works as intended and I was successfully able to run a function by overwriting EBP, but not in Arch linux.
another example of payload that doesn't work:
(gdb) run `python -c "print('A'*524+'\xaa\x61\x55\x56')"`
Starting program: /home/nepalidai/Documents/ethical_hacking/buffer_overflow/simplebuffer `python -c "print('A'*524+'\xaa\x61\x55\x56')"`
Off to 0x5561aac2
Program received signal SIGSEGV, Segmentation fault.
0x5561aac2 in ?? ()
source code:
#include <stdio.h>
#include <string.h>
int BUFFER=500;
void win(void){
/*Win Condition
We Want to jump here
*/
printf("\n ===== Win ===== \n\n");
}
void lose(void){
/* Lose Condition */
printf("Current Memory Address is %p\n",lose);
printf("Aim for %p\n", win);
printf("Lose :(\n");
}
int main(int argc, char* argv[]){
/* Main Function*/
//Pointer to the lose function
void (*fp)(void) = lose;
char buffer[BUFFER];
if (argc != 2){
printf("Overflow the buffer\n");
printf("Hint! Try `python -c \"print 'A'*100\"`\n");
return -1;
}
memcpy(buffer, argv[1], strlen(argv[1]));
printf("Off to %p\n",fp);
fp();
return 0;
}
disassembly:
(gdb) disassemble main
Dump of assembler code for function main:
0x56556254 <+0>: lea ecx,[esp+0x4]
0x56556258 <+4>: and esp,0xfffffff0
0x5655625b <+7>: push DWORD PTR [ecx-0x4]
0x5655625e <+10>: push ebp
0x5655625f <+11>: mov ebp,esp
0x56556261 <+13>: push edi
0x56556262 <+14>: push esi
0x56556263 <+15>: push ebx
0x56556264 <+16>: push ecx
0x56556265 <+17>: sub esp,0x18
0x56556268 <+20>: call 0x565560d0 <__x86.get_pc_thunk.bx>
0x5655626d <+25>: add ebx,0x2d93
0x56556273 <+31>: mov esi,ecx
0x56556275 <+33>: mov eax,esp
0x56556277 <+35>: mov edi,eax
0x56556279 <+37>: lea eax,[ebx-0x2e08]
0x5655627f <+43>: mov DWORD PTR [ebp-0x1c],eax
0x56556282 <+46>: mov eax,DWORD PTR [ebx+0x28]
0x56556288 <+52>: lea edx,[eax-0x1]
0x5655628b <+55>: mov DWORD PTR [ebp-0x20],edx
0x5655628e <+58>: mov edx,eax
0x56556290 <+60>: mov eax,0x10
0x56556295 <+65>: sub eax,0x1
0x56556298 <+68>: add eax,edx
0x5655629a <+70>: mov ecx,0x10
0x5655629f <+75>: mov edx,0x0
0x565562a4 <+80>: div ecx
0x565562a6 <+82>: imul eax,eax,0x10
0x565562a9 <+85>: sub esp,eax
0x565562ab <+87>: mov eax,esp
0x565562ad <+89>: add eax,0x0
0x565562b0 <+92>: mov DWORD PTR [ebp-0x24],eax
0x565562b3 <+95>: cmp DWORD PTR [esi],0x2
0x565562b6 <+98>: je 0x565562e3 <main+143>
0x565562b8 <+100>: sub esp,0xc
0x565562bb <+103>: lea eax,[ebx-0x1fb2]
0x565562c1 <+109>: push eax
0x565562c2 <+110>: call 0x56556060 <puts#plt>
0x565562c7 <+115>: add esp,0x10
0x565562ca <+118>: sub esp,0xc
0x565562cd <+121>: lea eax,[ebx-0x1f9c]
0x565562d3 <+127>: push eax
0x565562d4 <+128>: call 0x56556060 <puts#plt>
0x565562d9 <+133>: add esp,0x10
0x565562dc <+136>: mov eax,0xffffffff
0x565562e1 <+141>: jmp 0x5655632e <main+218>
0x565562e3 <+143>: mov eax,DWORD PTR [esi+0x4]
0x565562e6 <+146>: add eax,0x4
0x565562e9 <+149>: mov eax,DWORD PTR [eax]
0x565562eb <+151>: sub esp,0xc
0x565562ee <+154>: push eax
0x565562ef <+155>: call 0x56556070 <strlen#plt>
0x565562f4 <+160>: add esp,0x10
0x565562f7 <+163>: mov edx,DWORD PTR [esi+0x4]
0x565562fa <+166>: add edx,0x4
0x565562fd <+169>: mov edx,DWORD PTR [edx]
0x565562ff <+171>: sub esp,0x4
0x56556302 <+174>: push eax
0x56556303 <+175>: push edx
0x56556304 <+176>: push DWORD PTR [ebp-0x24]
0x56556307 <+179>: call 0x56556050 <memcpy#plt>
0x5655630c <+184>: add esp,0x10
0x5655630f <+187>: sub esp,0x8
0x56556312 <+190>: push DWORD PTR [ebp-0x1c]
0x56556315 <+193>: lea eax,[ebx-0x1f76]
0x5655631b <+199>: push eax
0x5655631c <+200>: call 0x56556040 <printf#plt>
0x56556321 <+205>: add esp,0x10
0x56556324 <+208>: mov eax,DWORD PTR [ebp-0x1c]
0x56556327 <+211>: call eax
0x56556329 <+213>: mov eax,0x0
0x5655632e <+218>: mov esp,edi
0x56556330 <+220>: lea esp,[ebp-0x10]
0x56556333 <+223>: pop ecx
0x56556334 <+224>: pop ebx
0x56556335 <+225>: pop esi
0x56556336 <+226>: pop edi
0x56556337 <+227>: pop ebp
0x56556338 <+228>: lea esp,[ecx-0x4]
0x5655633b <+231>: ret
End of assembler dump.
(gdb) disassemble lose
Dump of assembler code for function win:
0x565561cd <+0>: push ebp
0x565561ce <+1>: mov ebp,esp
0x565561d0 <+3>: push ebx
0x565561d1 <+4>: sub esp,0x4
0x565561d4 <+7>: call 0x5655633c <__x86.get_pc_thunk.ax>
0x565561d9 <+12>: add eax,0x2e27
0x565561de <+17>: sub esp,0xc
0x565561e1 <+20>: lea edx,[eax-0x1ff8]
0x565561e7 <+26>: push edx
0x565561e8 <+27>: mov ebx,eax
0x565561ea <+29>: call 0x56556060 <puts#plt>
0x565561ef <+34>: add esp,0x10
0x565561f2 <+37>: nop
0x565561f3 <+38>: mov ebx,DWORD PTR [ebp-0x4]
0x565561f6 <+41>: leave
0x565561f7 <+42>: ret
End of assembler dump.
(gdb) disassemble lose
Dump of assembler code for function lose:
0x565561f8 <+0>: push ebp
0x565561f9 <+1>: mov ebp,esp
0x565561fb <+3>: push ebx
0x565561fc <+4>: sub esp,0x4
0x565561ff <+7>: call 0x565560d0 <__x86.get_pc_thunk.bx>
0x56556204 <+12>: add ebx,0x2dfc
0x5655620a <+18>: sub esp,0x8
0x5655620d <+21>: lea eax,[ebx-0x2e08]
0x56556213 <+27>: push eax
0x56556214 <+28>: lea eax,[ebx-0x1fe4]
0x5655621a <+34>: push eax
0x5655621b <+35>: call 0x56556040 <printf#plt>
0x56556220 <+40>: add esp,0x10
0x56556223 <+43>: sub esp,0x8
0x56556226 <+46>: lea eax,[ebx-0x2e33]
0x5655622c <+52>: push eax
0x5655622d <+53>: lea eax,[ebx-0x1fc6]
0x56556233 <+59>: push eax
0x56556234 <+60>: call 0x56556040 <printf#plt>
0x56556239 <+65>: add esp,0x10
0x5655623c <+68>: sub esp,0xc
0x5655623f <+71>: lea eax,[ebx-0x1fba]
0x56556245 <+77>: push eax
0x56556246 <+78>: call 0x56556060 <puts#plt>
0x5655624b <+83>: add esp,0x10
0x5655624e <+86>: nop
0x5655624f <+87>: mov ebx,DWORD PTR [ebp-0x4]
0x56556252 <+90>: leave
0x56556253 <+91>: ret
End of assembler dump.
What am I doing wrong? I am very new to assembly language and bufferoverflow, please go easy on me.
Related
I tried to set a break-point on GDB when a function strcpy() is called, but GDB stops, and i don't know how to find the error, im new to GDB and i want to study binary exploitation, so the forum i'm reading does not explain nothing about this, here is the output;
(gdb) disassemble main
Dump of assembler code for function main:
0x00000000000011c9 <+0>: endbr64
0x00000000000011cd <+4>: push rbp
0x00000000000011ce <+5>: mov rbp,rsp
0x00000000000011d1 <+8>: sub rsp,0x50
0x00000000000011d5 <+12>: mov DWORD PTR [rbp-0x44],edi
0x00000000000011d8 <+15>: mov QWORD PTR [rbp-0x50],rsi
0x00000000000011dc <+19>: mov rax,QWORD PTR fs:0x28
0x00000000000011e5 <+28>: mov QWORD PTR [rbp-0x8],rax
0x00000000000011e9 <+32>: xor eax,eax
0x00000000000011eb <+34>: cmp DWORD PTR [rbp-0x44],0x1
0x00000000000011ef <+38>: jne 0x1207 <main+62>
0x00000000000011f1 <+40>: lea rsi,[rip+0xe10] # 0x2008
0x00000000000011f8 <+47>: mov edi,0x1
0x00000000000011fd <+52>: mov eax,0x0
0x0000000000001202 <+57>: call 0x10c0 <errx#plt>
0x0000000000001207 <+62>: mov DWORD PTR [rbp-0x34],0x0
0x000000000000120e <+69>: mov rax,QWORD PTR [rbp-0x50]
0x0000000000001212 <+73>: add rax,0x8
0x0000000000001216 <+77>: mov rdx,QWORD PTR [rax]
0x0000000000001219 <+80>: lea rax,[rbp-0x30]
0x000000000000121d <+84>: mov rsi,rdx
0x0000000000001220 <+87>: mov rdi,rax
0x0000000000001223 <+90>: call 0x1090 <strcpy#plt> // breakpoint here
0x0000000000001228 <+95>: mov eax,DWORD PTR [rbp-0x34]
0x000000000000122b <+98>: test eax,eax
0x000000000000122d <+100>: je 0x1247 <main+126>
0x000000000000122f <+102>: mov eax,DWORD PTR [rbp-0x34]
0x0000000000001232 <+105>: mov esi,eax
0x0000000000001234 <+107>: lea rdi,[rip+0xde5] # 0x2020
0x000000000000123b <+114>: mov eax,0x0
0x0000000000001240 <+119>: call 0x10d0 <printf#plt>
0x0000000000001245 <+124>: jmp 0x1253 <main+138>
0x0000000000001247 <+126>: lea rdi,[rip+0xe12] # 0x2060
0x000000000000124e <+133>: call 0x10a0 <puts#plt>
0x0000000000001253 <+138>: mov eax,0x0
0x0000000000001258 <+143>: mov rcx,QWORD PTR [rbp-0x8]
0x000000000000125c <+147>: xor rcx,QWORD PTR fs:0x28
0x0000000000001265 <+156>: je 0x126c <main+163>
0x0000000000001267 <+158>: call 0x10b0 <__stack_chk_fail#plt>
0x000000000000126c <+163>: leave
0x000000000000126d <+164>: ret
End of assembler dump.
(gdb) break *0x0000000000001223 // I want to set the breakpoint here
Breakpoint 1 at 0x1223
(gdb) r AAAA // I try to run the program providing arguments
Starting program: /home/ryan/liveoverflow_youtube/0x05_simple_crackme_intro_assembler/stackReg AAAA
[1]+ Stopped gdb stackReg // This is the problem?
GDB stopping like this is a bug which occurs when GDB throws an error while trying to place a breakpoint, it was fixed in upstream GDB with this patch:
https://sourceware.org/ml/gdb-patches/2019-05/msg00361.html
Once you see GDB stopped like this:
[1]+ Stopped
you should be dropped back to a shell. Just resume GDB with the fg command and continue your debug session. Once GDB 9 is out this bug will be fixed.
As was pointed out in a comment the reason the breakpoint address is incorrect is that you are using a Position Independent Executable (PIE), the code will be relocated when the process starts.
Start GDB with starti, then you can disassemble main and see where the code has actually been placed.
I wrote a simple crack-me program. You see the assembly code of a function named check inside this program:
(gdb) disassemble check
Dump of assembler code for function check:
0x08048484 <+0>: push ebp
0x08048485 <+1>: mov ebp,esp
0x08048487 <+3>: sub esp,0x28
0x0804848a <+6>: mov DWORD PTR [ebp-0x8],0x0
0x08048491 <+13>: mov DWORD PTR [ebp-0xc],0x0
0x08048498 <+20>: mov eax,DWORD PTR [ebp+0x8]
0x0804849b <+23>: mov DWORD PTR [esp],eax
0x0804849e <+26>: call 0x8048384 <strlen#plt> <<<<<< Here!
0x080484a3 <+31>: cmp DWORD PTR [ebp-0xc],eax
0x080484a6 <+34>: jae 0x80484fb <check+119>
0x080484a8 <+36>: mov eax,DWORD PTR [ebp-0xc]
0x080484ab <+39>: add eax,DWORD PTR [ebp+0x8]
0x080484ae <+42>: movzx eax,BYTE PTR [eax]
0x080484b1 <+45>: mov BYTE PTR [ebp-0xd],al
0x080484b4 <+48>: lea eax,[ebp-0x4]
0x080484b7 <+51>: mov DWORD PTR [esp+0x8],eax
0x080484bb <+55>: mov DWORD PTR [esp+0x4],0x8048638
0x080484c3 <+63>: lea eax,[ebp-0xd]
0x080484c6 <+66>: mov DWORD PTR [esp],eax
0x080484c9 <+69>: call 0x80483a4 <sscanf#plt>
0x080484ce <+74>: mov edx,DWORD PTR [ebp-0x4]
0x080484d1 <+77>: lea eax,[ebp-0x8]
0x080484d4 <+80>: add DWORD PTR [eax],edx
0x080484d6 <+82>: cmp DWORD PTR [ebp-0x8],0xf
0x080484da <+86>: jne 0x80484f4 <check+112>
0x080484dc <+88>: mov DWORD PTR [esp],0x804863b
0x080484e3 <+95>: call 0x8048394 <printf#plt>
0x080484e8 <+100>: mov DWORD PTR [esp],0x0
0x080484ef <+107>: call 0x80483b4 <exit#plt>
0x080484f4 <+112>: lea eax,[ebp-0xc]
0x080484f7 <+115>: inc DWORD PTR [eax]
0x080484f9 <+117>: jmp 0x8048498 <check+20>
0x080484fb <+119>: mov DWORD PTR [esp],0x8048649
0x08048502 <+126>: call 0x8048394 <printf#plt>
0x08048507 <+131>: leave
0x08048508 <+132>: ret
As you see above, inside the check function, there is a function call to strlen. The question is how can I see the string that is passed to strlen()?
I'm reading Haching: the art of exploitation and I have found a difference in my compiled code and the one in the book.
The code:
void test_function(int a, int b, int c, int d) {
int flag;
char buffer[10];
flag = 31337;
buffer[0] = 'A';
}
int main() {
test_function(1, 2, 3, 4);
}
In the book he compiles with gcc and disassembles with gdb, I don't know the version of the softwares.
He uses the commands:
gcc -g program.c
gdb -q ./a.out
Then he does:
(gdb) disass main
Dump of assembler code for function main():
0x08048357 <main+0>: push ebp
0x08048358 <main+1>: mov ebp,esp
0x0804835a <main+3>: sub esp,0x18
0x0804835d <main+6>: and esp,0xfffffff0
0x08048360 <main+9>: mov eax,0x0
0x08048365 <main+14>: sub esp,eax
0x08048367 <main+16>: mov DWORD PTR [esp+12],0x4
0x0804836f <main+24>: mov DWORD PTR [esp+8],0x3
0x08048377 <main+32>: mov DWORD PTR [esp+4],0x2
0x0804837f <main+40>: mov DWORD PTR [esp],0x1
0x08048386 <main+47>: call 0x8048344 <test_function>
0x0804838b <main+52>: leave
0x0804838c <main+53>: ret
End of assembler dump
(gdb) disass test_function()
Dump of assembler code for function test_function:
0x08048344 <test_function+0>: push ebp
0x08048345 <test_function+1>: mov ebp,esp
0x08048347 <test_function+3>: sub esp,0x28
0x0804834a <test_function+6>: mov DWORD PTR [ebp-12],0x7a69
0x08048351 <test_function+13>: mov BYTE PTR [ebp-40],0x41
0x08048355 <test_function+17>: leave
0x08048356 <test_function+18>: ret
Instead this is my output (using the same command):
(gdb) disass main
Dump of assembler code for function main:
0x000000000040058b <+0>: push rbp
0x000000000040058c <+1>: mov rbp,rsp
0x000000000040058f <+4>: mov ecx,0x4
0x0000000000400594 <+9>: mov edx,0x3
0x0000000000400599 <+14>: mov esi,0x2
0x000000000040059e <+19>: mov edi,0x1
0x00000000004005a3 <+24>: call 0x400546 <test_function>
0x00000000004005a8 <+29>: mov eax,0x0
0x00000000004005ad <+34>: pop rbp
0x00000000004005ae <+35>: ret
End of assembler dump.
(gdb) disass test_function
Dump of assembler code for function test_function:
0x0000000000400546 <+0>: push rbp
0x0000000000400547 <+1>: mov rbp,rsp
0x000000000040054a <+4>: sub rsp,0x40
0x000000000040054e <+8>: mov DWORD PTR [rbp-0x34],edi
0x0000000000400551 <+11>: mov DWORD PTR [rbp-0x38],esi
0x0000000000400554 <+14>: mov DWORD PTR [rbp-0x3c],edx
0x0000000000400557 <+17>: mov DWORD PTR [rbp-0x40],ecx
0x000000000040055a <+20>: mov rax,QWORD PTR fs:0x28
0x0000000000400563 <+29>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400567 <+33>: xor eax,eax
0x0000000000400569 <+35>: mov DWORD PTR [rbp-0x24],0x7a69
0x0000000000400570 <+42>: mov BYTE PTR [rbp-0x20],0x41
0x0000000000400574 <+46>: nop
0x0000000000400575 <+47>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400579 <+51>: xor rax,QWORD PTR fs:0x28
0x0000000000400582 <+60>: je 0x400589 <test_function+67>
0x0000000000400584 <+62>: call 0x400420 <__stack_chk_fail#plt>
0x0000000000400589 <+67>: leave
0x000000000040058a <+68>: ret
End of assembler dump.
I'm using gcc version 5.4.0 and gdb version 7.11.1.
I have two question:
Why is the function proloque different? What's the difference in the instructions given by the book compiler and the ones given by mine ? (I am not talking about the name of the register, I'm talking about why my main prologue does just push and move)
Could the difference in test_function due to a possible stack overflow and the compiler (that is newer than the other one) knows and patches it ? If so, how does it knows that a flaw exists and what method does it employ to patches it?
EDIT: I know the book version was compiled on a 32 bit architecture. My question was about the different instruction of the compiler, if they have to do the same things, why are they different?
The book version was compiled on a 32 bit architecture, your one on 64. Use the -m32 switch to generate 32 bit executable.
This question already has answers here:
Compiler changes printf to puts
(2 answers)
Closed 3 years ago.
I'm pretty new to programming and wanted to ask why I get the same result with different code. I'm actually reading a book and the example in the book is with printf (also in Assembler). In this case it says <printf#plt>. The assembler code in the book differs from mine but C Code is the same. Is my processor just computing different?
(Problem is at call <+34> <puts#plt>)
Code 1:
#include <stdio.h>
int main()
{
int i;
for(i=0; i<10; i++)
{
printf("Hello, world!\n");
}
return 0;
}
Code 2:
#include <stdio.h>
int main()
{
int i;
for(i=0; i<10; i++)
{
puts("Hello, world!\n");
}
return 0;
}
Code 1 disassembled:
Dump of assembler code for function main:
0x080483eb <+0>: lea ecx,[esp+0x4]
0x080483ef <+4>: and esp,0xfffffff0
0x080483f2 <+7>: push DWORD PTR [ecx-0x4]
0x080483f5 <+10>: push ebp
0x080483f6 <+11>: mov ebp,esp
0x080483f8 <+13>: push ecx
=> 0x080483f9 <+14>: sub esp,0x14
0x080483fc <+17>: mov DWORD PTR [ebp-0xc],0x0
0x08048403 <+24>: jmp 0x8048419 <main+46>
0x08048405 <+26>: sub esp,0xc
0x08048408 <+29>: push 0x80484b0
0x0804840d <+34>: call 0x80482c0 <puts#plt>
0x08048412 <+39>: add esp,0x10
0x08048415 <+42>: add DWORD PTR [ebp-0xc],0x1
0x08048419 <+46>: cmp DWORD PTR [ebp-0xc],0x9
0x0804841d <+50>: jle 0x8048405 <main+26>
0x0804841f <+52>: mov eax,0x0
0x08048424 <+57>: mov ecx,DWORD PTR [ebp-0x4]
0x08048427 <+60>: leave
0x08048428 <+61>: lea esp,[ecx-0x4]
0x0804842b <+64>: ret
End of assembler dump.
Code 2 disassembled:
Dump of assembler code for function main:
0x080483eb <+0>: lea ecx,[esp+0x4]
0x080483ef <+4>: and esp,0xfffffff0
0x080483f2 <+7>: push DWORD PTR [ecx-0x4]
0x080483f5 <+10>: push ebp
0x080483f6 <+11>: mov ebp,esp
0x080483f8 <+13>: push ecx
0x080483f9 <+14>: sub esp,0x14
0x080483fc <+17>: mov DWORD PTR [ebp-0xc],0x0
0x08048403 <+24>: jmp 0x8048419 <main+46>
=> 0x08048405 <+26>: sub esp,0xc
0x08048408 <+29>: push 0x80484b0
0x0804840d <+34>: call 0x80482c0 <puts#plt>
0x08048412 <+39>: add esp,0x10
0x08048415 <+42>: add DWORD PTR [ebp-0xc],0x1
0x08048419 <+46>: cmp DWORD PTR [ebp-0xc],0x9
0x0804841d <+50>: jle 0x8048405 <main+26>
0x0804841f <+52>: mov eax,0x0
0x08048424 <+57>: mov ecx,DWORD PTR [ebp-0x4]
0x08048427 <+60>: leave
0x08048428 <+61>: lea esp,[ecx-0x4]
0x0804842b <+64>: ret
End of assembler dump.
The puts function is preferred because it is simpler to in both functionality (no format string decoding) and argument passing.
For instance, System V ABI x86 calling conventions require to set number of XMM (YMM) arguments (printf is variadic) in RAX. puts is easier, as there is only single argument passed with RDI.
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].