Grunt's personal blog

this is my personal blog for my hacking stuff, my degree stuff, etc

View on GitHub

Convenciones de llamada

En este punto, hemos visto cómo funcionan las funciones, pero hemos omitido algunos detalles para simplificar. En este nivel, cubriremos la convención de llamadas utilizada en sistemas Linux x86-64. Esto completará los conceptos aprendidos hasta ahora y te ayudará a construir una intuición sobre cómo el código en C de alto nivel se traduce a ensamblador.

Objetivos de Aprendizaje


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void funcion_cadenas(char * a1, char * a2, char * a3, char * a4) 
{
    printf(" Arg 1: %s\n", a1);
    printf(" Arg 2: %s\n", a2);
    printf(" Arg 3: %s\n", a3);
    printf(" Arg 4: %s\n", a4);
}

void funcion_mixta(char * a1, int a2, short a3, char a4) 
{
    printf(" Arg 1: %s\n",  a1);
    printf(" Arg 2: %d\n",  a2);
    printf(" Arg 3: %hd\n", a3);
    printf(" Arg 4: %c\n",  a4);
}

void funcion_enteros(int a1, unsigned int a2) 
{
    printf(" Arg 1: %d\n", a1);
    printf(" Arg 2: %u\n", a2);
}

void funcion_muchos_args(char * a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) 
{
    printf(" Arg 1: %s\n", a1);
    printf(" Arg 2: %d\n", a2);
    printf(" Arg 3: %d\n", a3);
    printf(" Arg 4: %d\n", a4);
    printf(" Arg 5: %d\n", a5);
    printf(" Arg 6: %d\n", a6);
    printf(" Arg 7: %d\n", a7);
    printf(" Arg 8: %d\n", a8);
}

int main()
{
    unsigned int opcion = 0;

    while (1)
    {
        printf("-- Menú de Convenciones de Llamadas --\n"
               " 1. Argumentos de Cadenas            \n"
               " 2. Argumentos Mixtos                \n"
               " 3. Argumentos Enteros               \n"
               " 4. Muchos Argumentos                \n"
               " 5. Salir                            \n"
               "-------------------------------------\n"
               "Ingrese una opción: ");
        if (scanf("%u", &opcion) == EOF)
            break;

        if (opcion == 1)
            funcion_cadenas("uno", "dos", "tres", "cuatro");
        else if (opcion == 2)
            funcion_mixta("uno", 1337, 123, 'A');
        else if (opcion == 3)
            funcion_enteros(0x12345678, 0x87654321);
        else if (opcion == 4)
            funcion_muchos_args("uno", 1, 2, 3, 4, 5, 6, 7);
        else if (opcion == 5)
            break;
        else
            printf("¡Opción inválida!\n");

        opcion = 0;
    }

    return 0;
}

Argumentos de Función

Hemos visto detalles sobre cómo se realizan las llamadas a funciones en x86 y cómo se manejan los argumentos. Ahora exploraremos cómo se gestionan los argumentos en sistemas Linux x86-64.

Para comenzar, establece un punto de interrupción en funcion_cadenas(...) y ejecuta el programa.

wdb> break funcion_cadenas
wdb> run

Convenciones de Llamadas

El esquema que utiliza un sistema operativo para pasar argumentos a funciones y recibir resultados se llama convención de llamadas. Estas convenciones varían según el sistema operativo y la arquitectura.

En Linux x86-64, la convención de llamadas es la siguiente:

1er argumento: rdi
2do argumento: rsi
3er argumento: rdx
4to argumento: rcx
5to argumento: r8
6to argumento: r9

Resultado: rax

Los argumentos adicionales se colocan en la pila.

Prueba imprimir algunos de los argumentos y continúa la ejecución.

wdb> x/s $rdi
wdb> x/s $rsi
...

Deberías ver la salida impresa coincidiendo con tu inspección manual de registros. Si no, vuelve al primer punto de interrupción e inténtalo de nuevo.


Argumentos en la Pila

Cuando se agotan los registros disponibles, los argumentos adicionales se colocan en la pila. Intenta inspeccionar la memoria de la pila para encontrar estos argumentos:

wdb> x/10gd $rsp

Referencia de Argumentos en la Pila

Los argumentos en la pila suelen referenciarse con un índice positivo desde $rbp. Por ejemplo:

mov     eax, dword [rbp+0x18]

Esto indica que el argumento está basado en la pila. El marco de pila típico se ve así:

<argumentos en la pila> | ($rbp + 8) + ?
<dirección de retorno>  | $rbp + 8
<puntero base guardado> | $rbp apunta aquí
<variables locales>     | ($rbp - 8) - ?

Con esto, hemos cubierto los conceptos esenciales de la convención de llamadas en Linux x86-64.