Basics of pwn: Stack

2020-01-03

Key concept

stack grows down to lower memory addresses. (00000000~ffffffff)

The stack is simply an area in RAM that was chosen to be the stack.

rsp register holds the addres in memory where the bottom of the stack resides. When something is pushed to the stack, esp decreases by 8. When pop instruction is executed, the value at esp increases by 8.

ebp contains the top of the curretn stack frame. A stack frame is the space used on the stack by a given function

Examples

Stack is primarily used for the followings:

  1. Storing function arguments
  2. Storing local variables
  3. Storing processor state between function calls.

Here is an example code to build a basic idea of stack.

#include <stdio.h>

void say_hi(const char * name) {
    printf("Hello %s!\n", name);
}

int main(int argc, char ** argv) {
    char * name;
    if (argc != 2) {
        return 1;
    }
    name = argv[1];
    say_hi(name);
    return 0;
}

It’s a simple function where it takes a argument and print it out.

assembler code for function main

Dump of assembler code for function main:                                                              
   0x080488b3 <+0>:     lea    ecx,[esp+0x4]                                                           
   0x080488b7 <+4>:     and    esp,0xfffffff0                                                          
   0x080488ba <+7>:     push   DWORD PTR [ecx-0x4]                                                     
   0x080488bd <+10>:    push   ebp                                                                     
   0x080488be <+11>:    mov    ebp,esp                                                                 
   0x080488c0 <+13>:    push   ecx                                                                     
   0x080488c1 <+14>:    sub    esp,0x14                                                                
   0x080488c4 <+17>:    call   0x8048900 <__x86.get_pc_thunk.ax>                                       
   0x080488c9 <+22>:    add    eax,0x90293                                                             
   0x080488ce <+27>:    mov    eax,ecx                                                                 
   0x080488d0 <+29>:    cmp    DWORD PTR [eax],0x2                                                     
   0x080488d3 <+32>:    je     0x80488dc <main+41>                                                     
   0x080488d5 <+34>:    mov    eax,0x1                                                                 
   0x080488da <+39>:    jmp    0x80488f8 <main+69>                                                     
   0x080488dc <+41>:    mov    eax,DWORD PTR [eax+0x4]                                                 
   0x080488df <+44>:    mov    eax,DWORD PTR [eax+0x4]                                                 
   0x080488e2 <+47>:    mov    DWORD PTR [ebp-0xc],eax                                                 
   0x080488e5 <+50>:    sub    esp,0xc                                                                 
   0x080488e8 <+53>:    push   DWORD PTR [ebp-0xc]                                                     
   0x080488eb <+56>:    call   0x8048885 <say_hi>                                                      
   0x080488f0 <+61>:    add    esp,0x10                                                                
   0x080488f3 <+64>:    mov    eax,0x0                                                                 
   0x080488f8 <+69>:    mov    ecx,DWORD PTR [ebp-0x4]                                                 
   0x080488fb <+72>:    leave                                                                          
   0x080488fc <+73>:    lea    esp,[ecx-0x4]                                                    
   0x080488ff <+76>:    ret                                                                            
End of assembler dump. 

assembler code for function say_hi

Dump of assembler code for function say_hi:                                                            
   0x08048885 <+0>:     push   ebp                                                                     
   0x08048886 <+1>:     mov    ebp,esp                                                                 
   0x08048888 <+3>:     push   ebx                                                                     
   0x08048889 <+4>:     sub    esp,0x4

   0x0804888c <+7>:     call   0x8048900 <__x86.get_pc_thunk.ax>
   0x08048891 <+12>:    add    eax,0x902cb
   0x08048896 <+17>:    sub    esp,0x8
   0x08048899 <+20>:    push   DWORD PTR [ebp+0x8]
   0x0804889c <+23>:    lea    edx,[eax-0x2cd94]
   0x080488a2 <+29>:    push   edx
   0x080488a3 <+30>:    mov    ebx,eax
   0x080488a5 <+32>:    call   0x804f8e0 <printf>
   0x080488aa <+37>:    add    esp,0x10
   0x080488ad <+40>:    nop
   0x080488ae <+41>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x080488b1 <+44>:    leave  
   0x080488b2 <+45>:    ret    
End of assembler dump.

You can see that at 0x080488e8 the stack pushes [ebp-0xc] where it pushes the argument for the binary.

and then creates a stack frame from 0x08048885 then prints out the name then leave which it moves ebp in to esp and pops the saved EBP. Then ret pops the saved instruction pointer into eip which causes the program to move back to the main function.

The posts of ‘basics of pwn’ is a summary of mine of the posts on https://ctf101.org/binary-exploitation/