How to retrieve arguments of a function in gdb? - c

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()?

Related

Why is my gdb converting to wrong hexadecimal value?

(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.

Need clarification on understanding this code (Finding two secret numbers given C code and Assembly Code)

This is for one of my classes. We are given two files. One that contains C code:
guess_two_numbers.c
#include <stdio.h>
void print_error()
{
printf("\n Oooops, incorrect guess!\n");
exit(1);
}
int main()
{
int num1, num2;
printf("\n Guess my two secret numbers: ");
scanf("%d %d", &num1, &num2);
if(num1 > 11)
print_error();
if(num2 != num1 + 2)
print_error();
printf("\nCongratulations, you've found my two secret numbers!\n");
return 0;
}
And one that is Assembly Code:
0x08048462 <+0>: lea ecx,[esp+0x4]
0x08048466 <+4>: and esp,0xfffffff0
0x08048469 <+7>: push DWORD PTR [ecx-0x4]
0x0804846c <+10>: push ebp
0x0804846d <+11>: mov ebp,esp
0x0804846f <+13>: push ecx
0x08048470 <+14>: sub esp,0x24
0x08048473 <+17>: mov DWORD PTR [esp],0x80485c0
0x0804847a <+24>: call 0x8048360 <printf#plt>
0x0804847f <+29>: lea eax,[ebp-0xc]
0x08048482 <+32>: mov DWORD PTR [esp+0x8],eax
0x08048486 <+36>: lea eax,[ebp-0x8]
0x08048489 <+39>: mov DWORD PTR [esp+0x4],eax
0x0804848d <+43>: mov DWORD PTR [esp],0x80485e0
0x08048494 <+50>: call 0x8048350 <scanf#plt>
0x08048499 <+55>: mov eax,DWORD PTR [ebp-0x8]
0x0804849c <+58>: cmp eax,0xb
0x0804849f <+61>: jle 0x80484a6 <main+68>
0x080484a1 <+63>: call 0x8048444 <print_error>
0x080484a6 <+68>: mov eax,DWORD PTR [ebp-0x8]
0x080484a9 <+71>: lea edx,[eax+0x2]
0x080484ac <+74>: mov eax,DWORD PTR [ebp-0xc]
0x080484af <+77>: cmp edx,eax
0x080484b1 <+79>: je 0x80484b8 <main+86>
0x080484b3 <+81>: call 0x8048444 <print_error>
0x080484b8 <+86>: mov DWORD PTR [esp],0x80485e8
0x080484bf <+93>: call 0x8048370 <puts#plt>
0x080484c4 <+98>: mov eax,0x0
0x080484c9 <+103>: add esp,0x24
0x080484cc <+106>: pop ecx
0x080484cd <+107>: pop ebp
0x080484ce <+108>: lea esp,[ecx-0x4]
0x080484d1 <+111>: ret
The question that I was asked was to identify which line/lines in the assembly code that match the secret number/numbers requirement, which are the first secret number is < 11, and the second secret number is within +2 of the first secret number.. Given these files, I said that the line containing the <+58> is one line, as it compares the eax to 0xb, which is 11 in hexademical. I also said that <+71> is also a line that contains it, as it adds +2 to eax to check the second requirement. Is this sufficient or should I add more detail?

Why it shows puts when I disassemble no matter whether I'm using printf or puts? [duplicate]

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.

Performance in conditional statements

I noticed that a code I'm working on uses conditionals like:
if(A != val) {
// code B
}
if(B != val) {
// code A
}
But to me, reading the same code like below is much easier. Maybe a personal preference.
if( B == val) {
//code B
}
if( A == val) {
//code A
}
This is a very latency sensitive code so is there a performance difference between the two? Is there a performance difference between != or == ? or > or < for that matter? Also, I realize that this code leaves room for a 3rd condition though I'm pretty sure the code leaves only 2 paths possible so an if/else is more appropriate.
Much appreciated.
Here is the assembly code when in the condition you use !=, note that I added a printf inside the if statement in order to have a better output from gdb:
0x0000000000400526 <+0>: push rbp
0x0000000000400527 <+1>: mov rbp,rsp
0x000000000040052a <+4>: sub rsp,0x10
0x000000000040052e <+8>: mov DWORD PTR [rbp-0xc],0x0
0x0000000000400535 <+15>: mov DWORD PTR [rbp-0x8],0x1
0x000000000040053c <+22>: mov DWORD PTR [rbp-0x4],0x1
0x0000000000400543 <+29>: mov eax,DWORD PTR [rbp-0xc]
0x0000000000400546 <+32>: cmp eax,DWORD PTR [rbp-0x4]
0x0000000000400549 <+35>: **je** 0x400555 <main+47>
0x000000000040054b <+37>: mov edi,0x4005f4
0x0000000000400550 <+42>: call 0x400400 <puts#plt>
0x0000000000400555 <+47>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000400558 <+50>: cmp eax,DWORD PTR [rbp-0x4]
0x000000000040055b <+53>: **je** 0x400567 <main+65>
0x000000000040055d <+55>: mov edi,0x4005f4
0x0000000000400562 <+60>: call 0x400400 <puts#plt>
0x0000000000400567 <+65>: mov eax,0x0
0x000000000040056c <+70>: leave
0x000000000040056d <+71>: ret
While this is for ==:
0x0000000000400526 <+0>: push rbp
0x0000000000400527 <+1>: mov rbp,rsp
0x000000000040052a <+4>: sub rsp,0x10
0x000000000040052e <+8>: mov DWORD PTR [rbp-0xc],0x0
0x0000000000400535 <+15>: mov DWORD PTR [rbp-0x8],0x1
0x000000000040053c <+22>: mov DWORD PTR [rbp-0x4],0x1
0x0000000000400543 <+29>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000400546 <+32>: cmp eax,DWORD PTR [rbp-0x4]
0x0000000000400549 <+35>: **jne** 0x400555 <main+47>
0x000000000040054b <+37>: mov edi,0x4005f4
0x0000000000400550 <+42>: call 0x400400 <puts#plt>
0x0000000000400555 <+47>: mov eax,DWORD PTR [rbp-0xc]
0x0000000000400558 <+50>: cmp eax,DWORD PTR [rbp-0x4]
0x000000000040055b <+53>: **jne** 0x400567 <main+65>
0x000000000040055d <+55>: mov edi,0x4005f4
0x0000000000400562 <+60>: call 0x400400 <puts#plt>
0x0000000000400567 <+65>: mov eax,0x0
0x000000000040056c <+70>: leave
0x000000000040056d <+71>: ret
As you may notice the only difference is the usage of je (jump if equal) or jne (jump if not equal) so in terms of performance you could say that it' exactly the same
You will have a conditional jump in your assembler code whether you check for equality or any kind of unequality. Performance difference should be marginal.
Though it can be beneficial to write branchless code, see e.g. Why is it faster to process a sorted array than an unsorted array?.

Why are there 12 bytes for parameters and locals on the stack in C on Ubuntu?

I'm looking at how the memory is laid out on the stack, but I don't understand why it looks like there are 12 bytes worth of space for storing each of the variables. Here is a simple C program that prints out the location of various variables:
#include <stdio.h>
#include <stdlib.h>
int test (long p1, long p2){
int l1 = 9999;
int l2 = 99993333;
printf("%p p1\n", &p1);
printf("%p p2\n", &p2);
printf("%p l1\n", &l1);
printf("%p l1\n", &l2);
}
int main(int argc, const char** argv)
{
register void* stack asm("esp");
int x = 22;
int y = 1000;
printf("%p stack\n", stack);
printf("%p argv\n", &argv);
printf("%p argc\n", &argc);
printf("%p l1\n", &x);
printf("%p l2\n", &y);
test(1, 888);
return 0;
}
When run, here is the output:
~/gc$ ./a.out
0x7fff5496b200 stack
0x7fff5496b200 argv
0x7fff5496b20c argc
0x7fff5496b218 l1
0x7fff5496b21c l2
0x7fff5496b1d8 p1
0x7fff5496b1d0 p2
0x7fff5496b1e8 l1
0x7fff5496b1ec l1
Why is there 12 space between the address of argv and argc, and 12 space between l1 and l2? I expected 8 for the longs and pointers, and for the int parameter of main, I would understand either 4 or 8 characters, but I don't see any reason it should be 12.
Someone mentioned that the assembly code would be useful, so I got that as well:
Dump of assembler code for function main:
0x0000000000400614 <+0>: push %rbp
0x0000000000400615 <+1>: mov %rsp,%rbp
0x0000000000400618 <+4>: sub $0x20,%rsp
0x000000000040061c <+8>: mov %edi,-0x14(%rbp)
0x000000000040061f <+11>: mov %rsi,-0x20(%rbp)
0x0000000000400623 <+15>: movl $0x16,-0x8(%rbp)
0x000000000040062a <+22>: movl $0x3e8,-0x4(%rbp)
0x0000000000400631 <+29>: mov %rsp,%rax
0x0000000000400634 <+32>: mov %rax,%rsi
0x0000000000400637 <+35>: mov $0x40079c,%edi
0x000000000040063c <+40>: mov $0x0,%eax
0x0000000000400641 <+45>: callq 0x400410 <printf#plt>
0x0000000000400646 <+50>: lea -0x20(%rbp),%rax
0x000000000040064a <+54>: mov %rax,%rsi
0x000000000040064d <+57>: mov $0x4007a6,%edi
0x0000000000400652 <+62>: mov $0x0,%eax
0x0000000000400657 <+67>: callq 0x400410 <printf#plt>
0x000000000040065c <+72>: lea -0x14(%rbp),%rax
0x0000000000400660 <+76>: mov %rax,%rsi
0x0000000000400663 <+79>: mov $0x4007af,%edi
0x0000000000400668 <+84>: mov $0x0,%eax
0x000000000040066d <+89>: callq 0x400410 <printf#plt>
0x0000000000400672 <+94>: lea -0x8(%rbp),%rax
0x0000000000400676 <+98>: mov %rax,%rsi
0x0000000000400679 <+101>: mov $0x400780,%edi
0x000000000040067e <+106>: mov $0x0,%eax
0x0000000000400683 <+111>: callq 0x400410 <printf#plt>
0x0000000000400688 <+116>: lea -0x4(%rbp),%rax
0x000000000040068c <+120>: mov %rax,%rsi
0x000000000040068f <+123>: mov $0x400787,%edi
0x0000000000400694 <+128>: mov $0x0,%eax
0x0000000000400699 <+133>: callq 0x400410 <printf#plt>
0x000000000040069e <+138>: mov $0x14d,%ecx
0x00000000004006a3 <+143>: mov $0x1589e,%edx
0x00000000004006a8 <+148>: mov $0x378,%esi
0x00000000004006ad <+153>: mov $0x1,%edi
0x00000000004006b2 <+158>: callq 0x40052c <test>
0x00000000004006b7 <+163>: mov $0x0,%eax
0x00000000004006bc <+168>: leaveq
0x00000000004006bd <+169>: retq
End of assembler dump.
(gdb) disassemble test
Dump of assembler code for function test:
0x000000000040052c <+0>: push %rbp
0x000000000040052d <+1>: mov %rsp,%rbp
0x0000000000400530 <+4>: sub $0x40,%rsp
0x0000000000400534 <+8>: mov %rdi,-0x28(%rbp)
0x0000000000400538 <+12>: mov %rsi,-0x30(%rbp)
0x000000000040053c <+16>: mov %rdx,-0x38(%rbp)
0x0000000000400540 <+20>: mov %rcx,-0x40(%rbp)
0x0000000000400544 <+24>: movl $0x270f,-0x18(%rbp)
0x000000000040054b <+31>: movq $0x5f5c6f5,-0x10(%rbp)
0x0000000000400553 <+39>: movl $0x63,-0x14(%rbp)
0x000000000040055a <+46>: movq $0x371,-0x8(%rbp)
0x0000000000400562 <+54>: lea -0x28(%rbp),%rax
0x0000000000400566 <+58>: mov %rax,%rsi
0x0000000000400569 <+61>: mov $0x400764,%edi
0x000000000040056e <+66>: mov $0x0,%eax
0x0000000000400573 <+71>: callq 0x400410 <printf#plt>
0x0000000000400578 <+76>: lea -0x30(%rbp),%rax
0x000000000040057c <+80>: mov %rax,%rsi
0x000000000040057f <+83>: mov $0x40076b,%edi
0x0000000000400584 <+88>: mov $0x0,%eax
0x0000000000400589 <+93>: callq 0x400410 <printf#plt>
0x000000000040058e <+98>: lea -0x38(%rbp),%rax
0x0000000000400592 <+102>: mov %rax,%rsi
0x0000000000400595 <+105>: mov $0x400772,%edi
0x000000000040059a <+110>: mov $0x0,%eax
0x000000000040059f <+115>: callq 0x400410 <printf#plt>
0x00000000004005a4 <+120>: lea -0x40(%rbp),%rax
0x00000000004005a8 <+124>: mov %rax,%rsi
0x00000000004005ab <+127>: mov $0x400779,%edi
0x00000000004005b0 <+132>: mov $0x0,%eax
0x00000000004005b5 <+137>: callq 0x400410 <printf#plt>
0x00000000004005ba <+142>: lea -0x18(%rbp),%rax
0x00000000004005be <+146>: mov %rax,%rsi
0x00000000004005c1 <+149>: mov $0x400780,%edi
0x00000000004005c6 <+154>: mov $0x0,%eax
0x00000000004005cb <+159>: callq 0x400410 <printf#plt>
0x00000000004005d0 <+164>: lea -0x10(%rbp),%rax
0x00000000004005d4 <+168>: mov %rax,%rsi
0x00000000004005d7 <+171>: mov $0x400787,%edi
0x00000000004005dc <+176>: mov $0x0,%eax
0x00000000004005e1 <+181>: callq 0x400410 <printf#plt>
0x00000000004005e6 <+186>: lea -0x14(%rbp),%rax
0x00000000004005ea <+190>: mov %rax,%rsi
0x00000000004005ed <+193>: mov $0x40078e,%edi
0x00000000004005f2 <+198>: mov $0x0,%eax
0x00000000004005f7 <+203>: callq 0x400410 <printf#plt>
0x00000000004005fc <+208>: lea -0x8(%rbp),%rax
0x0000000000400600 <+212>: mov %rax,%rsi
0x0000000000400603 <+215>: mov $0x400795,%edi
0x0000000000400608 <+220>: mov $0x0,%eax
0x000000000040060d <+225>: callq 0x400410 <printf#plt>
0x0000000000400612 <+230>: leaveq
0x0000000000400613 <+231>: retq
End of assembler dump.
You are using a 64-bit system (based on the size of the pointers printed), which means you're probably using x86-64.
Certain arguments to functions in the x86-64 ABI do not have addresses because they are passed in registers. However, according to the C standard, you are allowed to take their addresses. So, when you write &argc, the compiler reserves space for it on the stack and returns that address.
So it is just another local variable. The compiler is free to put argc anywhere on the stack. This behavior is not mandatory, it's just the way that your compiler works.
As for the reason why 12 happens coincidentally on this particular occasion to be the spacing, remember that the stack grows downwards on x86-64. So if you push argc onto the stack the stack pointer will go down 4 bytes, and if you push argv onto the stack it will first go down another 4 bytes to be aligned correctly, then it will go down 8 bytes after argv is pushed. Of course the compiler is free to do other things, like put argv and argc in arbitrary other locations on the stack.
Demonstration
C code:
void otherfunc(int *ptr);
int func(int value)
{
otherfunc(&value);
return 0;
}
Assembly code:
func:
subq $24, %rsp ; Allocate 24 bytes on the stack
movl %edi, 12(%rsp) ; Store 'value' on the stack
leaq 12(%rsp), %rdi ; Calculate the address of 'value'
call otherfunc ; Call 'otherfunc'
xorl %eax, %eax ; Return value 0
addq $24, %rsp ; Deallocate stack
ret ; Return
Remember that %rsp is the stack pointer, %edi / %rdi is the first parameter to a function, and %eax is the return value of a function.

Resources