0

I am examining this code snippet from Ericksons Hacking: The Art of Exploitation:

void test_function(int a, int b, int c, int d) {
        int flag;
        char buffer[10];
        flag = 31337;
        buffer[0] = 'A';
}

int main() {
        test_function(1, 2, 3, 4); 
}
gcc -g stack_example.c

gdb -q ./a.out

gef➤list main


     0x555555555192 <main+0>         endbr64 
       0x555555555196 <main+4>         push   rbp
       0x555555555197 <main+5>         mov    rbp, rsp
     → 0x55555555519a <main+8>         mov    ecx, 0x4
       0x55555555519f <main+13>        mov    edx, 0x3
       0x5555555551a4 <main+18>        mov    esi, 0x2
       0x5555555551a9 <main+23>        mov    edi, 0x1
       0x5555555551ae <main+28>        call   0x555555555149 <test_function>
       0x5555555551b3 <main+33>        mov    eax, 0x0

I set breakpoints at main and at the test_function. When break main I got following output:

gef➤  i r rsp rbp rip

    rsp            0x7fffffffdfc8      0x7fffffffdfc8
    rbp            0x0                 0x0
    rip            0x555555555192      0x555555555192 <main>
gef➤ x/8i $rip

       0x555555555192 <main>:   endbr64 
       0x555555555196 <main+4>: push   rbp
       0x555555555197 <main+5>: mov    rbp,rsp
       0x55555555519a <main+8>: mov    ecx,0x4
       0x55555555519f <main+13>:    mov    edx,0x3
       0x5555555551a4 <main+18>:    mov    esi,0x2
       0x5555555551a9 <main+23>:    mov    edi,0x1
       0x5555555551ae <main+28>:    call   0x555555555149 <test_function>

gef➤continue

And now when I break at the test_function, registers contain:

gef➤  i r rsp rbp rip

    **rsp            0x7fffffffdfc0      0x7fffffffdfc0**
    **rbp            0x7fffffffdfc0      0x7fffffffdfc0**
    rip            0x55555555519a      0x55555555519a <main+8>

And my question is, why the register rsp changes from 0x7fffffffdfc8 to 0x7fffffffdfc0 after these instructions?

    0x555555555196 <main+4>         push   rbp
    0x555555555197 <main+5>         mov    rbp, rsp
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Miroslav Savel
  • 184
  • 2
  • 10
  • Because push pushes something to the stack. – tkausl Oct 13 '20 at 20:05
  • Yes but why it changes by 8? I am still missing something. – Miroslav Savel Oct 13 '20 at 20:07
  • 2
    @MiroslavSavel Do you know what “pushing something to the stack” means? Do you know what the “sp” in `rsp` means? – fuz Oct 13 '20 at 20:09
  • It points at the top of stack? – Miroslav Savel Oct 13 '20 at 20:10
  • @MiroslavSavel Yes! `rsp` is the stack pointer. It's the one register that tells you where the top of the stack is. – fuz Oct 13 '20 at 20:10
  • And the number 8 is because it is 64bit register or 8 Bytes? – Miroslav Savel Oct 13 '20 at 20:12
  • 3
    @MiroslavSavel recall that 64 bit is 8 byte, so yes. The `rsp` register is decreased by the amount of bytes you pushed. – fuz Oct 13 '20 at 20:25
  • Most of those 3 duplicates came up in the early google hits for "push changes RSP". (also [What is the function of the push / pop instructions used on registers in x86 assembly?](https://stackoverflow.com/q/4584089)) A quick search should have cleared this up for you, or as always just check Intel's instruction-set reference manual for any instruction when you aren't sure what it should do. https://www.felixcloutier.com/x86/push – Peter Cordes Oct 14 '20 at 04:24

1 Answers1

5

The instruction push rbp accomplishes the same as sub rsp, 8; mov QWORD PTR[rsp], rbp;. It first moves the stack pointer (rsp) up 8 bytes (this is because the size of a register in x86-64 is 8 bytes i.e. 64 bits), then moves the value of the register at the memory location pointed by it. Therefore, the value of rsp, which was 0x7fffffffdfc8, becomes 0x7fffffffdfc0, which is 8 less than before.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • 5
    `sub` is not an exact explanation because `push` does not alter flags. `lea rsp, [rsp - 8]` is closer. – ecm Oct 13 '20 at 20:43