Reverse Engineering Puzzles #1 - stack frame and local variables

Hello everyone

While doing some experiments with C language I thought that it will be fine to share the interesting results with you. And this is what this new series is all about - when I find something curious I am going to write about it on my blog. Today's topic is the deeper look at the stack frame.

The stack frame is the place for the function to allocate the local variables for example. After the end of the function execution, the stack frame and the local variables are cleared. But is this really true?

NASM 64-bit
There is the code of the standard stack frame above. I decided to understand the stack frame recently so I tried to explain what this component of each function really does. Let's try to think like a processor:

push rbp  -> this is some mystery what is exactly in the rbp register when the program executes this instruction for the first time. So let's assume that 0x1234 value is in this register.

The stack:
--------------------------------------------------
Return address of the main function 
--------------------------------------------------
0x1234                                                  <-- rsp

 
mov rbp, rsp -> and now we know what is in the rbp register when the program pushes it on the stack. Rbp register stores the address of the stack frame.

Look at the stack because this is such an interesting thing:
--------------------------------------------------
Return address of the main function
--------------------------------------------------
The stack frame address of the caller    <-- rsp, rbp

When the new function is started execution, push rbp instruction pushes the address of the caller stack frame on the stack.

sub rsp, 10h -> the decreasing of the rsp register is the memory allocation on the stack because the stack grows downward. By this instruction our function allocates exactly 16 bytes on the stack. This can be the place for the local variables.

The stack:
--------------------------------------------------
Return address of the main function
--------------------------------------------------
The stack frame address of the caller    <-- rbp
--------------------------------------------------

--------------------------------------------------

--------------------------------------------------
                                                             <-- rsp
--------------------------------------------------   

The set of the above instructions is the prologue of the called function. So these instructions are executed before the instructions which are in the body of that function. Now let's take a look at the epilogue so the instructions which are executed after the execution of the instructions in the body.

mov rsp, rbp -> set the rsp pointer to the start of the stack frame.

The stack:
--------------------------------------------------
Return address of the main function
--------------------------------------------------
The stack frame address of the caller    <-- rbp, rsp
--------------------------------------------------

--------------------------------------------------

--------------------------------------------------
                                                             
-------------------------------------------------- 

pop rbp -> the address of the current function stack frame will be stored in the rbp register and the rsp register is now the pointer to the return address of the function.


The stack:
--------------------------------------------------
Return address of the main function     <-- rsp
--------------------------------------------------

--------------------------------------------------

--------------------------------------------------
                                                             
--------------------------------------------------

ret -> jump to the return address of the function.

And this is the end of the function execution. Someone told it sentence once - "The life of the local variables ends with the end of the function execution." Maybe in the most situations, it is true, but let's take a look at this experiment.

Look at the result in the screen after compile and execute of this code in C.


It works! Let's take a look at the assembly code of the C program and see what is going on there.

NASM 64-bit Like I said - rbp register has the address of the caller or of the last executed function if that function was executed in the same caller stack frame. So when we don't declare some local variables or don't accept any arguments to the function we can take the values from the caller stack frame. But this is only the curiosity and it's not a good practice in the programming of course. :)

Comments

Popular posts from this blog

Learning of malware analysis. Solving 9-1 lab from the "OllyDbg" chapter. ("Practical Malware Analysis" book)

PicoCTF 2018 - Reverse Engineering writeups