Dancing with Functions: Unraveling the Assembler Function Convention in x32

4 min read

February 10, 2023

Initiating Linux Binary Exploitation: A Beginner's Expedition into Code Manipulation
Dancing with Functions: Unraveling the Assembler Function Convention in x32

Table of contents

Introduction

Step into the intricate world of assembly language, where each instruction orchestrates a ballet of bytes and registers. In this exploration, we delve into the heart of the assembler function convention, uncovering the silent rules that govern the exchange of information within the stack. Get ready for a journey behind the scenes, from the dazzling entrance of functions to the graceful exit upon completion.

Unlocking the Dance: Assembler Function Convention in x32

Imagine a function's frame as the stack's VIP lounge, reserved for arguments, local variables, and the return address. As functions step into the limelight, new frames join the party and gracefully exit upon completion. The stack's top, ever the socialite, adjusts dynamically, ensuring the current function takes center stage.

To paint a clearer picture, let's dissect a code snippet:

void function_a(param_1, param_2) {
        int var_1 = 10;
        int var_2 = 11;
        funcion_b(arg_3, arg_4)
}
void function_b(param_3, param_4) {
        int var = 12;
        funcion_c(arg_5);
}
void function_c(param_5) {
        int var = 13;
}

int main() {
        funcion_a(arg_1, arg_2);
        printf("Message\n");
}

As the script unfolds, the stack elegantly transforms, each function adding its unique touch to the stack's haute couture.

Display of the stack status once when the c function is executing

Guiding the Dance: The Eloquent Frame Pointer (ebp, rbp)

In the mysterious world of compilation, the exact memory addresses of local variables or arguments remain hidden. Enter the ebp register—the Sherlock Holmes of function frames. This savvy register stakes out a fixed address within the function frame, providing a secret passage to various arguments or local variables. The subtraction of ebp's memory address is akin to finding the hidden door to access local variables, with the subtracted size dancing to the rhythm of the data type.

Sample of how to access arguments and local variables using ebp

Onstage Elegance: Call Instructions and the VIP Lounge

Before a function makes its grand entrance, parameters and the return address are sipping on espresso cups in the stack's VIP lounge. The assembly instructions set the stage:

push   0x2 
push   0x1
call   80483fb <funcion_b>

These instructions send two integers as parameters onto the stack and roll out the red carpet, with the call instruction ushering the execution to the specified address while keeping the return address on standby.

Viewing from top to bottom, it shows how the arguments and return address are placed on the stack.

Prologue Unveiled: Crafting the Frame with Grace

The prologue is the opening act, featuring instructions that tweak register values to create the function's frame. It's like the function's backstage pass:

; prologue

push    ebp                                     
mov     ebp, esp   

Here, the instructions gracefully push the current ebp value into the limelight and set the stage for a new function frame based on the stack top.

Stack sample once the prologue of a function has been performed

Finale Choreography: The Artful Epilogue of a Function

As the final curtain gracefully descends, the epilogue steps into the limelight, guiding our program towards closure with these meticulous instructions:

; epilogue

leave
ret

Now, let's pull back the curtain to reveal the inner workings of the leave instruction—it's akin to a carefully orchestrated set of moves:

mov esp, ebp  
pop ebp       

In this encore performance, the leave instruction takes a bow by gracefully updating the stack top, aligning it with the value of ebp from the function that just concluded its act. Envision the backstage crew tidying up the set, ensuring every register and memory space returns to its initial state. Following this choreography, the pop instruction steps in, updating the value of ebp to the one stored in the prologue—a changing of the guard between functions.

Sample leave instruction

On another note, the ret instruction assumes the role of the final bow in this symphony of instructions. It elegantly unstacks the return value from the stack, placing it in eip like the closing note of a musical piece. As the curtain falls, it also orchestrates the update of esp to reflect the new stack top, ensuring a seamless transition to the program's next act.

A key note to highlight: Once the caller function takes its final bow and exits the stage, esp undergoes yet another transformation, a dance routine dictated by the number of parameters graciously accepted by the function. For a visual, consider this code excerpt:

call 80483fb
push esp,0x8

In this ballet, two integers, each occupying 4 bytes, gracefully ascend from the stack—a farewell gift from the calling function to the one that shared the limelight.

Epilogue and unstacking function parameters

Conclusion

As the curtain falls on our exploration of assembler functions, we reflect on the harmonious interplay of bytes and registers that brings program execution to life. These functions, akin to lead actors, leave behind an elegant and meticulous imprint on the stack. Remember that behind each execution lies a precise choreography defining the rhythm of the code, a dance that transforms the seemingly complex into a symphony of understanding.

References

Introducción · Guía de exploits

Chapters

Botón Anterior
Embarking on the Exploration: Fundamentals of Binary Exploitation on Linux

Previous chapter

Decoding the Compiler: A Deep Dive into the Phases of C Code Compilation

Next chapter