00004zaXvRJBo5qo.es.srt

Este video es una sesión de dynamic analysis (tracing) del ejercicio 5 usando el debugger local de Ghidra, complementando el análisis estático que se había hecho previamente.


Setup inicial:

Se cargó el ejercicio 5 en el debugger local de Ghidra. Se verificó que la base address coincidiera entre el binario estático y el proceso en memoria (rebase). Se mapeó el módulo manualmente con Map Module porque al principio el cursor no sincronizaba con el código.


Estructuras en memoria — visualización dinámica:

El punto clave del video fue ver en tiempo real cómo las estructuras se van llenando en memoria a medida que se ejecuta el programa. Las estructuras que se observaron fueron:

  • GlobalStruct — ubicada en .data, se observó cómo sus campos (string, byte, word, etc.) se fueron escribiendo durante la ejecución.
  • Derived (heap) — creado con new (0x20 bytes). Se usó Choose Data Type en la ventana de memoria para mostrar la estructura con sus campos. Se observó el proceso completo: primero __AutoClassInit la zeroea, luego el constructor de base escribe la vtable de base, y finalmente el constructor de derived sobreescribe con la vtable correcta de Derived.
  • Nest — también creado con new. Se aplicó el mismo proceso: zeroeo → constructor → observación de campos llenados, incluyendo su propia vtable.
  • Derived (stack) — mismo flujo que el del heap, pero ubicado en el stack. Se copiaron las direcciones manualmente desde los registros para observarlas en la ventana de memoria.
  • Derived (global) — ubicado en la sección de datos. Ya tenía su vtable de una ejecución previa; se verificó que los campos se actualizaran correctamente.

Vtable dinámica:

Se verificó en memoria que las vtables contienen los punteros correctos a las funciones virtuales. Usando Choose Data Type con los tipos de vtable previamente creados, se pudieron ver los nombres de las funciones (random, get_valor_calculado, imprimir_case, destructor) directamente en la vista de memoria.


Flujo de ejecución general:

El programa sigue esta secuencia para cada objeto (type_pointer, type_stack, type_global):

  1. Crear/inicializar el objeto.
  2. Imprimir el tipo ("pointer", "stack", "global").
  3. Llamar a get_random → obtener valor random.
  4. Llamar a get_valor_calculado → calcular valor derivado.
  5. Imprimir ambos valores.
  6. Llamar a métodos virtuales (imprimir_hello, imprimir_test, imprimir la string caracter por caracter).

Observaciones del debugger de Ghidra:

  • El step over / step into se confundía frecuentemente; los botones no son intuitivos.
  • Hubo un string basura ("Hello Fressez" / "1337") que se imprimía en medio de la salida correcta, probablemente un bug en el ejercicio o memoria no inicializada.
  • Las ventanas de memoria, registros y código se sincronizan en tiempo real, pero requieren configuración manual del mapeo del módulo.