banner



Which Register Stores The Current 64-bit Memory Address Of The Stack?

Stack frames

A actually quick explanation of stack frames and frame pointers

February 16, 2018

Agreement Frame Pointers

Each office has local memory associated with it to agree incoming parameters, local variables, and (in some cases) temporary variables. This region of memory is called a stack frame and is allocated on the procedure' stack. A frame pointer (the ebp annals on intel x86 architectures, rbp on 64-flake architectures) contains the base address of the function'south frame. The code to access local variables within a function is generated in terms of offsets to the frame pointer. The stack pointer (the esp register on intel x86 architectures or rsp on 64-bit architectures) may modify during the execution of a function as values are pushed or popped off the stack (such as pushing parameters in preparation to calling another office). The frame pointer doesn't change throughout the part.

Hither's what happens during function (there might be slight differences amongst languages/architectures)

  1. Push the electric current value of the frame pointer (ebp/rbp). This saves it so nosotros can restore it afterward.

  2. Move the current stack pointer to the frame pointer. This defines the start of the frame.

  3. Subtract the space needed for the function's data from the stack pointer. Remember that stacks grow from high memory to low memory. This puts the stack pointer past the infinite that will be used past the function so that anything pushed onto the stack now will not overwrite useful values.

  4. At present execute the code for the office. References to local variables will be negative offsets to the frame pointer (e.yard., "movl $123, –eight(%rbp)").

  5. On exit from the role, re-create the value from the frame arrow to the stack pointer (this clears upwards the space allocated to the stack frame for the function) and pop the onetime frame pointer. This is achieved by the "get out" instruction.

  6. Return from the procedure via a "ret" educational activity. This pops the return value from the stack and transfers execution to that accost.

Bones example

Let's consider the following set of functions in a file chosen attempt.c

          void bar(int a, int b) {     int x, y;      x = 555;     y = a+b; }  void foo(void) {     bar(111,222); }                  

We'll compile it via

          gcc -S  -m32 try.c                  

The -S option tells the compiler to create an assembler file. The -m32 pick tells the compiler to generate lawmaking for a 32-bit compages. In this case, information technology keeps the numbers smaller and nosotros don't take to worry nigh specifying -no-red-zone (meet more details, below).

gcc chooses to use the mov teaching (movl) instead of button considering the Intel x86 instruction gear up doesn't have an education to button constant values onto the stack. Adjusting the stack and so moving the required parameters into the proper places as negative offsets accomplishes the same matter.

The generated code is (removing lines that contain directives to the linker):

          bar:     pushl   %ebp     movl    %esp, %ebp     subl    $sixteen, %esp     movl    $555, -four(%ebp)     movl    12(%ebp), %eax     movl    8(%ebp), %edx     addl    %edx, %eax     movl    %eax, -8(%ebp)     leave     ret foo:     pushl   %ebp     movl    %esp, %ebp     subl    $8, %esp     movl    $222, 4(%esp)     movl    $111, (%esp)     call    bar     leave     ret                  

We can annotate the lawmaking and trace it by starting at foo():

          bar:        # --------- start of the part bar()     pushl   %ebp        # salve the incoming frame pointer     movl    %esp, %ebp  # prepare the frame pointer to the current top of stack     subl    $xvi, %esp   # increase the stack by xvi bytes (stacks grow down)     movl    $555, -4(%ebp)  # x=555 a is located at [ebp-4]     movl    12(%ebp), %eax  # 12(%ebp) is [ebp+12], which is the second parameter     movl    8(%ebp), %edx   # 8(%ebp) is [ebo+8], which is the first parameter     addl    %edx, %eax  # add them     movl    %eax, -viii(%ebp)  # store the event in y     leave           #     ret         # foo:        # --------- showtime of the function foo()     pushl   %ebp        # save the current frame arrow     movl    %esp, %ebp  # set the frame pointer to the electric current superlative of the stack     subl    $viii, %esp    # increase the stack past 8 bytes (stacks grow down)     movl    $222, four(%esp)   # this is effectively pushing 222 on the stack     movl    $111, (%esp)    # this is effectively pushing 111 on the stack     call    bar     # call = push the instruction pointer on the stack and branch to foo     go out           # done     ret         #                  

Let'southward see what happens. In foo(), we need to prepare the stack for ii parameters that will exist sent to bar(). The compiler would like to do

          button $222 button $111                  

but those instructions don't exist on the IA–32 compages and so instead, the compiler generates code to subtract 8 from the stack pointer, making the stack abound by eight bytes (enough to concord 2 32-bit values). It and then uses stack offset addressing to place the values 111 and 222 on the stack (run into effigy i).

Figure 1. Before call to bar
Figure one. Before telephone call to bar

And then foo calls bar. This pushes the return address onto the stack so it looks like this when execution starts at bar (effigy 2):

Figure 2. At entry to bar
Figure ii. At entry to bar

Figure 3. In bar
Figure 3. In bar

On entry to bar(), nosotros save the previous value of ebp, and set the frame arrow to the top of the stack (the electric current position of the stack arrow). Then nosotros abound the stack by subtracting 16 from the stack arrow. Stacks on intel architectures grow from high retentiveness to low retentivity, and so the pinnacle of the stack (the latest contents) are in low memory. The stack now looks like the one shown in figure 3. Nosotros have a stack frame for the function bar that holds local data for this instance of the function. Negative offsets of the frame pointer %ebp (toward the top of the stack, into lower memory) volition refer to local information in bar. Positive offsets of %ebp will allow us to read incoming parameters.

Now we're ready to execute the piddling logic of the function. We set local variable ten to 555. This variable is the very next prepare of four bytes after the saved ebp. The side by side statement adds the two parameters and stores the result into the local int y. The code for this is to read the value of b (which is [ebp+12]) and store it into register %eax. The value of a (which is [ebp+eight]) is read into register %edx. The two values are added and the result is stored in y, which is [ebp–eight]. Effigy four shows the position of the parameters and local variables.

Figure 4. In bar
Figure 4. In bar

When nosotros're done, nosotros call "exit", which sets the stack arrow to the value of the frame pointer (%ebp) and pops the saved value of the frame pointer (the one the function foo was using). Now the stack arrow is pointing to the return address inside foo that was saved when the call instruction was executed and our frame is effectively deallocated. The ret instruction pops the stack and transfers control dorsum to foo right after the call bar instruction.

Y'all might be wondering why the stack was adjusted by 16 bytes instead of the viii that was needed to concur x and y. I don't know. That seems to exist a multiple that gcc uses. If you lot allocate ii more local ints, the frame remains the same size. If you classify some other int, the compiler grows the stack by 32 bytes.

More details about how frames are used

  • gcc (and other compilers) uses registers for the commencement few (six) parameters and these are copied into areas inside the office's frame]

  • Every bit an optimization, the intel x86–64 compages allows functions to utilize space on the stack without adjusting the stack pointer if that space is <= 128 bytes. Interrupt handlers are guaranteed to not modify this region. You lot tin can search for "red zone" to read about this if you're interested. The gcc compiler can exist told to ignore this via a -mno-red-zone option.

  • Since the compiler can keep track of what's going on with the stack at any point in time, the frame pointer isn't strictly necessary. You can compile lawmaking to use the stack pointer exclusively with the -fomit-frame-pointer selection to gcc.

Exploiting buffer overflow

By exploiting a buffer overflow, yous can write arbitrary data onto the stack. This means that you tin can change the return address of a function and also alter the data past that return address - the local variables of previous functions. In a basic code injection assault, you can change the return address to the accost of the buffer that you overwrote with code of your choosing. You now injected code into the program. In a unproblematic return-oriented-programming attack, you change the return address to the address of a library office such as organisation() and insert data on the stack to make requite system() the parameters you want (e.k., a command to execute). Note that the code illustrated above is not vulnerable to buffer overflow since we're using scalars (merely ints) instead of arrays.

References

  • Call stack
  • Stack frame layout on x86–64
  • Buffer overflow: the function stack
  • x86 Disassembly/Functions and Stack Frame
  • Ruby-red zone
  • x86 get out instruction

Source: https://www.cs.rutgers.edu/~pxk/419/notes/frames.html

Posted by: beattiehouseenjut.blogspot.com

0 Response to "Which Register Stores The Current 64-bit Memory Address Of The Stack?"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel