Grunt's personal blog

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

View on GitHub

Apuntes sobre Memoria en C

Introducción a la Gestión de Memoria

C, como lenguaje de bajo nivel, permite un control preciso sobre la memoria en tiempo de ejecución. Un programador experto en C debe conocer cuántos bytes ocupa una variable, dónde se almacenan los datos en memoria y cómo se formatean o acceden durante la ejecución. Esto es crucial en contextos como la explotación de binarios.


Buffers

Un buffer es un bloque de memoria reservado para almacenar datos arbitrarios, como entradas de usuario. En C, los buffers suelen declararse como arreglos.

Ejemplo

// Reserva un buffer de 16 bytes en el stack
char buffer[16] = {};

En este caso, se asigna un buffer de 16 bytes en el stack, inicializando todos sus bytes a 0 mediante las llaves {}.

Visualización del Buffer

------------------------------------------------------------
-- Visualización de Buffer #1
------------------------------------------------------------

                . . . . . . . . . . . . . . . . . . . . . .

                +------------------ 0x7fffffffed80 -----------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +---------------------------------------+
                | 04 | 30 | 40 | 00 | 00 | 00 | 00 | 00 |
                +------------------ 0x7fffffffed90 -----------------+
 buffer ----->  | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
                +---------------------------------------+
                | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
                +---------------------------------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +------------------ 0x7fffffffeda8 -----------------+

                . . . . . . . . . . . . . . . . . . . . . .

Llenado de Buffers

Un buffer puede llenarse con datos, como una cadena estática, utilizando funciones como strcpy.

Ejemplo

// Copia una cadena estática en el buffer
strcpy(buffer, "Hello World!");

Visualización del Buffer Llenado

------------------------------------------------------------
-- Visualización de Buffer #2
------------------------------------------------------------

                . . . . . . . . . . . . . . . . . . . . . .

                +------------------ 0x7fffffffed80 -----------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +---------------------------------------+
                | 36 | 30 | 40 | 00 | 00 | 00 | 00 | 00 |
                +------------------ 0x7fffffffed90 -----------------+
 buffer ----->  | 48 | 65 | 6C | 6C | 6F | 20 | 57 | 6F |  <- "Hello Wo"
                +---------------------------------------+
                | 72 | 6C | 64 | 21 | 00 | 00 | 00 | 00 |  <- "rld!\0"
                +---------------------------------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +------------------ 0x7fffffffeda8 -----------------+

                . . . . . . . . . . . . . . . . . . . . . .

Datos Arbitrarios

Los buffers pueden almacenar entradas de usuario mediante funciones como fgets.

Ejemplo

// Llena el buffer con entrada del usuario
printf("Enter data: ");
fgets(buffer, sizeof(buffer), stdin);

Visualización con Entrada de Usuario (Ejemplo: 0x00001)

------------------------------------------------------------
-- Visualización de Buffer #3
------------------------------------------------------------

                . . . . . . . . . . . . . . . . . . . . . .

                +------------------ 0x7fffffffed80 -----------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +---------------------------------------+
                | 89 | 30 | 40 | 00 | 00 | 00 | 00 | 00 |
                +------------------ 0x7fffffffed90 -----------------+
 buffer ----->  | 30 | 78 | 30 | 30 | 30 | 30 | 31 | 0A |  <- "0x00001\n"
                +---------------------------------------+
                | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
                +---------------------------------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +------------------ 0x7fffffffeda8 -----------------+

                . . . . . . . . . . . . . . . . . . . . . .

Punteros

Los punteros son direcciones de memoria que hacen referencia a variables o funciones. El operador unario & se usa para obtener la dirección de memoria de una variable.

Ejemplo

printf("Misc Pointers:\n");
printf(" -     int foobar  @ %p\n", &foobar);
printf(" - demo_pointers() @ %p\n", &demo_pointers);

Los punteros son esenciales para explorar el funcionamiento interno de una aplicación en tiempo de ejecución.


Paso por Referencia

Un parámetro declarado como puntero (usando *) almacena una dirección de memoria. Los datos grandes, como buffers, se pasan por referencia para evitar copias costosas.

Ejemplo

void pass_buffers(char *buffer_small, ...);

Visualización de Punteros

------------------------------------------------------------
-- Visualización de Puntero
------------------------------------------------------------

  +---------------------+----------------+
  | char *buffer_small  | 0x7fffffffeb20 |---+
  +---------------------+----------------+   |
                                            |
           +--------------------------------+
           |      . . . . . . . . . . . . . . . . . . . . . .
           |      +------------------ 0x7fffffffeb10 -----------------+
           |      | 01 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
           |      +---------------------------------------+
           |      | 68 | 71 | 22 | 00 | D2 | 04 | 00 | 00 |
           +----> +------------------ 0x7fffffffeb20 -----------------+
                  | 41 | 41 | 41 | 41 | 41 | 41 | 41 | 41 |  <- "AAAAAAAA"
                  +---------------------------------------+
                  | 41 | 41 | 41 | 41 | 41 | 41 | 41 | 41 |
                  +---------------------------------------+
                  | 41 | 41 | 41 | 41 | 41 | 41 | 41 | 41 |
                  +------------------ 0x7fffffffeb40 -----------------+

                  . . . . . . . . . . . . . . . . . . . . . .

Estructuras

Una estructura (struct) es un tipo de dato complejo que agrupa varias variables (miembros) bajo un solo nombre. Es lo más cercano en C a un objeto de clase.

Ejemplo

typedef struct person {
    char first_name[16];
    char last_name[16];
    uint32_t age;
    uint32_t weight;
} person;

// Inicializa una estructura 'person' vacía
person student = {};

// Llena los campos de la estructura
strcpy(student.first_name, "Jane");
strcpy(student.last_name, "Doe");
student.age = 21;
student.weight = 112;

Visualización de Estructura

------------------------------------------------------------
-- Visualización de Estructura
------------------------------------------------------------

                . . . . . . . . . . . . . . . . . . . . . .

                +------------------ 0x7fffffffed70 -----------------+
                | B0 | ED | FF | FF | FF | 7F | 00 | 00 |
                +---------------------------------------+
 person ------->| F7 | 32 | 40 | 00 | 00 | 00 | 00 | 00 |
                +------------------ 0x7fffffffed80 -----------------+
 first_name --> | 4A | 61 | 6E | 65 | 00 | 00 | 00 | 00 |  <- "Jane\0"
                +---------------------------------------+
                | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
                +---------------------------------------+
 last_name ---> | 44 | 6F | 65 | 00 | 00 | 00 | 00 | 00 |  <- "Doe\0"
                +---------------------------------------+
                | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
                +---------------------------------------+
 age/weight --> | 15 | 00 | 00 | 00 | 70 | 00 | 00 | 00 |  <- 21, 112
                +---------------------------------------+
                | 00 | 52 | CE | FC | 95 | 09 | 2B | 73 |
                +------------------ 0x7fffffffedb8 -----------------+

                . . . . . . . . . . . . . . . . . . . . . .

Punteros a Estructuras

Las estructuras suelen pasarse a funciones como punteros para mayor eficiencia. Para acceder o modificar los miembros de una estructura a través de un puntero, se usa el operador ->.

Ejemplo

void pass_structure(person *student) {
    // Modifica la estructura mediante puntero
    strcpy(student->first_name, "John");
    strcpy(student->last_name, "Smith");
    student->age = 18;
    student->weight = 173;
}

Datos Globales

Las variables globales son accesibles desde cualquier función en el programa y suelen definirse con el prefijo g_ para indicar su naturaleza global.

Ejemplo

const uint8_t g_encrypted[16] = {
    0xA1, 0xAD, 0xBF, 0xB8, 0xA9, 0xBE, 0x93, 0xBC,
    0xAD, 0xBF, 0xBF, 0xBB, 0xA3, 0xBE, 0xA8, 0xCC
};

Estas variables se almacenan en la sección de datos del binario, un tema que se explorará en lecciones futuras. En este caso, la función validate_password() usa g_encrypted para autenticar contraseñas.

Ejemplo de Decodificación (Python)

g_encrypted = [0xA1, 0xAD, 0xBF, 0xB8, 0xA9, 0xBE, 0x93, 0xBC,
               0xAD, 0xBF, 0xBF, 0xBB, 0xA3, 0xBE, 0xA8, 0xCC]
password = ''.join(chr(b ^ 0xCC) for b in g_encrypted)
print(password)