I have a pretty simple program for learning stack overflow.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[128];
if(argc < 2) return 1;
strcpy(buf, argv[1]);
printf("Hello\n");
return 0;
}
The strategy is to supply large string in argv[1] to overflow buf and overwrite the return address. But which return address? i thought it is the address saved before I entered strcpy, so when we return normally from strcpy, we will execute printf.
However, after I overflow the buffer with a shell code payload to change this return address to my shellcode. I see the printf is still executed. Even if I added a few more printf, they will all be executed. Apparently, the return address I change only affects the main function return, otherwise I should not even see the printfs being executed.
Why would this happen? Isn't that when I overrun the buffer to change the return address to my shellcode, the main program will jump to my shellcode directly without executing the next printf?
On your typical PC, the stack grows downward. That means the memory layout of the stack will look like this while calling strcpy
:
// ^^^ higher addresses ^^^
[stuff]
[return address of main]
[buf[127]]
[buf[126]]
...
[buf[1]]
[buf[0]]
[argument 2 (pointer to argv[1])]
[argument 1 (pointer to buf)]
[return address of strcpy (points into main)]
[local variables in strcpy]
// vvv lower addresses vvv
By overflowing buf
(writing to buf[128]
, buf[129]
, etc.), you write over main
's call frame (most importantly, main
's return address). You can't affect strcpy
's call frame because it lives before buf
in memory.