Dancing with Functions: Unraveling the Assembler Function Convention in x32
4 min read
February 10, 2023
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.
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.
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.
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.
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.
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.
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
Chapters
Previous chapter
Next chapter