However, the injected shellcode lies in somewhere in the memory. The main challenge is how to change the program control so that the injected shellcode can be executed..

callq and ret instructions callq fn. When a function fn is called through the
callq instruction, the following takes place.
push %rip: Push %rip onto the stack. In other words, this
instructon stores the return address to the stack.
jmp to fn: Jump to where fn is so that in
the next CPU cycle, the function fn may be executed.
ret. When a function returns to the call via the
ret instruction the following takes place
pop %rip. What's on top of stack is popped into %rip.
Of course, what's on the stack should be the return address so that %rip
contains the correct return address and in the next CPU cyle, the control
gets back to where the return address is.
> gcc -g -fno-stack-protector -o callstack callstack.c
(gdb) l
...
9 int main()
10 {
(gdb)
11 char buf_main[] = "in main";
12 printf("%s\n", buf_main);
13 f1(10);
14 return 0;
15 }
16
17 int f1(int n)
18 {
19 char buf_f1[] = "in f1";
20 printf("%s: n= %d\n", buf_f1, n);
21 return 0;
22 }
...
|
Let's see how the program works
(gdb) r
Starting program: .../callstack
in main
in f1: n= 10
[Inferior 1 (process 30869) exited normally]
Let's set breakpoints to see the detailed memory dumps. In particular, we set
the breakpoints right before and after the function call, and also right before
returning.
// in main: right before calling f1 in main (gdb) b 13 Breakpoint 1 at 0x400588: file callstack.c, line 13. // in f1: right before returing to main (gdb) b 21 Breakpoint 2 at 0x4005ca: file callstack.c, line 21. // in main: after f1 is done (gdb) b 14 Breakpoint 3 at 0x400592: file callstack.c, line 14. |
main function.
main function.
f1 function.
n in the f1 function.
buf_1 in the f1 function.
(gdb) p &main $1 = (int (*)()) 0x400566 <main> (gdb) p &f1 $2 = (int (*)(int)) 0x400599 <f1> (gdb) r Starting program: .../callstack in main Breakpoint 1, main () at callstack.c:13 13 f1(10); (gdb) p $rip $3 = (void (*)()) 0x400588 <main+34> (gdb) p $rsp $4 = (void *) 0x7fffffffe7c0 (gdb) p $rbp $5 = (void *) 0x7fffffffe7d0 (gdb) hd $rsp $rbp-$rsp 0x7fffffffe7c0: 69 6E 20 6D i n . m 0x7fffffffe7c4: 61 69 6E 00 a i n . 0x7fffffffe7c8: 00 00 00 00 . . . . 0x7fffffffe7cc: 00 00 00 00 . . . . (gdb) c Continuing. |
Breakpoint 2, f1 (n=10) at callstack.c:21 21 return 0; (gdb) p $rip $6 = (void (*)()) 0x4005ca <f1+49> (gdb) p $rsp $7 = (void *) 0x7fffffffe790 (gdb) p $rbp $8 = (void *) 0x7fffffffe7b0 (gdb) p &n $9 = (int *) 0x7fffffffe79c (gdb) p &buf_f1 $10 = (char (*)[6]) 0x7fffffffe7a0 (gdb) hd $rsp 64 0x7fffffffe790: C0 E7 FF FF . . . . 0x7fffffffe794: FF 7F 00 00 . . . . 0x7fffffffe798: FA D7 A7 F7 . . . . 0x7fffffffe79c: 0A 00 00 00 . . . . 0x7fffffffe7a0: 69 6E 20 66 i n . f 0x7fffffffe7a4: 31 00 00 00 1 . . . 0x7fffffffe7a8: D0 E7 FF FF . . . . 0x7fffffffe7ac: FF 7F 00 00 . . . . 0x7fffffffe7b0: D0 E7 FF FF . . . . 0x7fffffffe7b4: FF 7F 00 00 . . . . 0x7fffffffe7b8: 92 05 40 00 . . @ . 0x7fffffffe7bc: 00 00 00 00 . . . . 0x7fffffffe7c0: 69 6E 20 6D i n . m 0x7fffffffe7c4: 61 69 6E 00 a i n . 0x7fffffffe7c8: 00 00 00 00 . . . . 0x7fffffffe7cc: 00 00 00 00 . . . . (gdb) c Continuing. |
Breakpoint 3, main () at callstack.c:14 14 return 0; (gdb) p $rip $11 = (void (*)()) 0x400592 <main+44> (gdb) p $rsp $12 = (void *) 0x7fffffffe7c0 (gdb) p $rbp $13 = (void *) 0x7fffffffe7d0 (gdb) hd $rsp $rbp-$rsp 0x7fffffffe7c0: 69 6E 20 6D i n . m 0x7fffffffe7c4: 61 69 6E 00 a i n . 0x7fffffffe7c8: 00 00 00 00 . . . . 0x7fffffffe7cc: 00 00 00 00 . . . . (gdb) c Continuing. [Inferior 1 (process 31033) exited normally] |
callq instruction] The return address has been written on top of the stack.
(gdb) p $rip $3 = (void (*)()) 0x400588 <main+34> (gdb) hd $rsp $rbp-$rsp 0x7fffffffe7c0: 69 6E 20 6D i n . m 0x7fffffffe7c4: 61 69 6E 00 a i n . 0x7fffffffe7c8: 00 00 00 00 . . . . 0x7fffffffe7cc: 00 00 00 00 . . . . |
0x7fffffffe7b8: 92 05 40 00 . . @ . 0x7fffffffe7bc: 00 00 00 00 . . . . 0x7fffffffe7c0: 69 6E 20 6D i n . m 0x7fffffffe7c4: 61 69 6E 00 a i n . 0x7fffffffe7c8: 00 00 00 00 . . . . 0x7fffffffe7cc: 00 00 00 00 . . . . |
Breakpoint 1, main () at callstack.c:13
13 f1(10);
(gdb) p $rip
$3 = (void (*)()) 0x400588 <main+34>
|
How? =====> =====> |
Breakpoint 3, main () at callstack.c:14
14 return 0;
(gdb) p $rip
$9 = (void (*)()) 0x400592 <main+44>
|
f1]. $rbp of
function main is pushed on to the stack. When function f1 is finished
and the control returns to the main function, the program now should go back
to use the stack frame of function main. In particular,
Breakpoint 1, main () at callstack.c:13
13 f1(10);
(gdb) p $rbp
$5 = (void *) 0x7fffffffe7d0
|
0x7fffffffe7a8: D0 E7 FF FF . . . . 0x7fffffffe7ac: FF 7F 00 00 . . . . 0x7fffffffe7b0: D0 E7 FF FF . . . . 0x7fffffffe7b4: FF 7F 00 00 . . . . 0x7fffffffe7b8: 92 05 40 00 . . @ . 0x7fffffffe7bc: 00 00 00 00 . . . . 0x7fffffffe7c0: 69 6E 20 6D i n . m 0x7fffffffe7c4: 61 69 6E 00 a i n . 0x7fffffffe7c8: 00 00 00 00 . . . . 0x7fffffffe7cc: 00 00 00 00 . . . . |
Breakpoint 3, main () at callstack.c:14 14 return 0; (gdb) p $rip $9 = (void (*)()) 0x400592 <main+44> (gdb) p $rsp $10 = (void *) 0x7fffffffe7c0 (gdb) p $rbp $11 = (void *) 0x7fffffffe7d0 |
f1 is executing.
Summary of call stacksPlease remember the figure on the right.What if?
|
|
However, the shellcode lies in somewhere in the stack (not the text area, where the normal program code lies). The main challenge is how to control the program counter (i.e., \$rip) so that it points to the shellcode. Now, we have a good idea to achieve this task: