The 0x414141 and 0x424242 that are placed in books for educational purposes because these are simply the strings "AAAAAAAA" and "BBBBBBB" in hexadecimal notation. Assigning them to integers has nothing to do with buffer overflows.
A much better example can be had by using a full program, and a function call that will allow us to know when the overflow happens. For example, let's alter the program to:
#include <stdio.h>
#include <unistd.h>
void
fun(void)
{
char buffer[10];
if (read(STDIN_FILENO, &buffer, 60) < 0) {
perror("read");
}
write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
return;
}
int
main(void)
{
fun();
write(STDOUT_FILENO, "WILL NOT PRINT.\n", 16);
return 0;
}
Since you're using read and write we are assuming a UNIX system, so let's compile it and run a couple of times. Assuming that the program source is in buf.c:
[~]$ gcc -g -o prog buf.c
[~]$ perl -e 'print "A"x10' | ./prog
So long and thanks for all the fish.
WILL NOT PRINT.
[~]$ perl -e 'print "A"x20' | ./prog
So long and thanks for all the fish.
WILL NOT PRINT.
Perl is handy because we can control exactly the number of bytes that we give the program. Since STDIN is connected to a pipe, we can give a varying amount of bytes, up to the hardcoded 60 bytes in the program source.
The first run gives what we expected, 10 bytes are places in the 10bytes long buffer. In the second run the 10 byte buffer should have overflow with 20 bytes of input. But nothing appears to happen. Well, the buffer did overflow, but did not overflow far enough to hinder process execution. Let's do more:
[grochmal@haps tmp]$ perl -e 'print "A"x30' | ./prog
So long and thanks for all the fish.
Segmentation fault (core dumped)
Good, that's what we expect to see from an overflow. Now that we have a good input we save it and look at how the program works in GDB:
[grochmal@haps tmp]$ perl -e 'print "A"x30' > input
[grochmal@haps tmp]$ gdb -q prog
Reading symbols from prog...done.
(gdb) list
5 fun(void)
6 {
7 char buffer[10];
8
9 if (read(STDIN_FILENO, &buffer, 30) < 0) {
10 perror("read");
11 }
12 write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
13 return;
14 }
(gdb) list
15
16 int
17 main(void)
18 {
19 fun();
20 write(STDOUT_FILENO, "WILL NOT PRINT.\n", 16);
21 return 0;
22 }
23
(gdb) break 9
Breakpoint 1 at 0x40058e: file buf.c, line 9.
(gdb) break 12
Breakpoint 2 at 0x4005b3: file buf.c, line 12.
Since we added -g to the compiler line we have all debugging symbols (e.g. program code) we may want. This makes it very easy to setup the break points where the interesting part of the execution happens. We run the program with the input we just saved and check how the buffer looks:
(gdb) run <input
Starting program: /home/grochmal/tmp/prog <input
Breakpoint 1, fun () at buf.c:9
9 if (read(STDIN_FILENO, &buffer, 30) < 0) {
(gdb) p &buffer
$1 = (char (*)[10]) 0x7fffffffe800
(gdb) x/15wx 0x7fffffffe800
0x7fffffffe800: 0x004005f0 0x00000000 0x00400490 0x00000000
0x7fffffffe810: 0xffffe820 0x00007fff 0x004005d3 0x00000000
0x7fffffffe820: 0x004005f0 0x00000000 0xf7a5c291 0x00007fff
0x7fffffffe830: 0xf7dd0798 0x00007fff 0xffffe908
The buffer is only 10 bytes long, and we printed 60 bytes. But that gives away 0xffffe820 which is the location where the program needs to return to (in main) from the fun() call. When the buffer is overflowed, the bytes overwrite that. Let's see it by continuing with the read call.
(gdb) cont
Continuing.
Breakpoint 2, fun () at buf.c:12
12 write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
(gdb) x/15wx 0x7fffffffe800
0x7fffffffe800: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe810: 0x41414141 0x41414141 0x41414141 0x00004141
0x7fffffffe820: 0x004005f0 0x00000000 0xf7a5c291 0x00007fff
0x7fffffffe830: 0xf7dd0798 0x00007fff 0xffffe908
And now the program will try to return over to 0x41414141, which will cause the segfault.
(gdb) cont
Continuing.
So long and thanks for all the fish.
Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
And it did! As a bonus we have the 0x41414141 string you're using as an int assignment in your program, and now can deduce from where it comes.
The security implication of this is that I know exactly which part of the input overwrites that return value on the stack. And, thanks to that, I can drive the program to any instruction within its memory allocation.
(In practice that's much harder today because several measures are there to prevent such overflows from finding useful places to return to. But the concept is the same.)