Ghidra 101: Creación de estructuras en Ghidra

Ghidra 101: Creación de estructuras en Ghidra

En esta serie de blogs, me centraré en los útiles ghidra características que puede haber pasado por alto. Cada publicación analizará una característica diferente y mostrará cómo lo ayuda a ahorrar tiempo y ser más efectivo en sus flujos de trabajo de ingeniería inversa. Ghidra es una herramienta increíblemente poderosa, pero gran parte de este poder proviene de saber cómo usarla de manera efectiva.

Los programadores suelen definir tipos de datos compuestos para agrupar datos relacionados para acceder con un solo puntero. Al programar en C (o varios derivados), esto se puede implementar con el tipo de datos struct. Como programador, se puede acceder a los elementos de miembro a través de entradas con nombre que corresponden a compensaciones fijas desde el puntero de estructura. Como ingeniería inversa, es necesario identificar estructuras y correlacionar las compensaciones dadas con tipos de datos específicos y variables miembro. Afortunadamente, Ghidra hace que esto sea relativamente sencillo con la creación automática de estructuras y un editor visual para crear o modificar diseños. En esta publicación de blog, mostraré brevemente cómo se ve una estructura en desensamblaje y descompilación antes de explicar cómo representar estas estructuras de datos en su proyecto Ghidra.

Acceso a la estructura desmontada

Al mirar en el desensamblado, las referencias de estructura se pueden detectar donde un puntero (por ejemplo, un registro) se está desreferenciando en varios desplazamientos. En el siguiente ejemplo, de un binario ls de Linux, RDI es un parámetro de función basado en registros que contiene la dirección de una estructura de ARCHIVO:

estructura de ARCHIVO

Explicación: En 0x413470, los datos en el desplazamiento 0x8 de la estructura puntiaguda se mueven a RAX para compararlos con el valor en un desplazamiento de 0x10 bytes desde la base de la estructura. Si los valores son iguales, el salto se llevará a LAB_00413480 donde el valor en el desplazamiento 0x20 se compara con el valor en el desplazamiento 0x28 antes de una verificación NULL final del valor en el desplazamiento 0x48.

Lo que hay que reconocer en el ejemplo anterior es que se accede a RDI en 5 desplazamientos diferentes 0x8, 0x10, 0x20, 0x28 y 0x48, que no están espaciados uniformemente. Si bien esto podría ser una operación de matriz, este no es un patrón de uso típico de una matriz. Esta es una indicación bastante fuerte de que RDI es la dirección base de una estructura. El compilador simplemente calculó el desplazamiento de las variables miembro en la estructura y las codificó para que sean más eficientes en el tiempo de ejecución.

Acceso a estructuras descompiladas

La descompilación de este código en particular muestra que Ghidra ya pudo inferir no solo que se trata de una estructura, sino que también identificó la estructura específica y resolvió los nombres de las variables miembro:

Nombres de variables

La opción del menú contextual/clic derecho ‘Editar tipo de datos’ está disponible en param_1 para revisar la estructura de datos en el Editor de estructuras Ghidra:

Editor de estructuras Ghidra

Una mirada cercana muestra que los valores de compensación en esta definición de estructura coinciden con los valores observados en el desensamblado (8, 16, 32, 40 y 72) y también coinciden con los nombres descompilados.

En este caso, no es necesario modelar la estructura de datos ya que Ghidra ya ha enriquecido la ingeniería inversa con datos de bibliotecas estándar. (param_1 se usa en la misma función que el argumento de una función importada, lo que le permite a Ghidra inferir el tipo de datos). La vista del descompilador se verá bastante diferente.

Cuando tenga suerte, Ghidra descompilará las referencias de estructura en una serie bastante obvia de operaciones matemáticas de puntero como:

  if (((*(long *)((long)param_1 + 0x10) == *(long *)((long)param_1 + 8)) &&
      (*(long *)((long)param_1 + 0x28) == *(long *)((long)param_1 + 0x20))) &&
     (*(long *)((long)param_1 + 0x48) == 0))

El patrón anterior debe reconocerse fácilmente como una estructura, pero también trabajé en proyectos en los que una estructura se modeló como una matriz, similar a la siguiente:

if (((param_1[1] == *param_1) && (param_1[4] == param_1[3])) && (param_1[8] == 0)) 

Este código representa una condición muy similar al código anterior, con la única diferencia de que todos los elementos de la estructura tienen la misma longitud. En el ejemplo principal, un int al comienzo de la estructura de datos tiene una longitud diferente a la de los otros miembros de la estructura, por lo que Ghidra puede inferir con confianza que no se trata de una matriz sino de una estructura.

Creación manual de una estructura de datos

Siempre se pueden definir nuevos tipos de datos en la ventana Administrador de tipos de datos de Ghidra, que se encuentra en la parte inferior izquierda de un diseño predeterminado de CodeBrowser. Se crea una nueva definición de estructura haciendo clic con el botón derecho en el nombre del programa en el Administrador de tipos de datos y seleccionando NewàStructure… para cargar una instancia de editor de estructura en blanco. El ícono más en la barra de herramientas se usa para definir variables miembro. La longitud de la estructura se debe ajustar para proporcionar suficiente espacio y luego se puede especificar el tipo de datos. Alternativamente, establecer el tamaño de la estructura inicialmente permite al usuario completar tipos de datos para reasignar espacio automáticamente.

editor de estructura

Creación automática de estructuras

El proceso manual funciona lo suficientemente bien, pero a veces puede ser un poco frustrante ingresar los datos, por lo que es excelente que Ghidra proporcione la función ‘Crear estructura automáticamente’ para comenzar una definición de estructura utilizando sugerencias inferidas del análisis de Ghidra. Esta función útil está disponible en Decompiler haciendo clic con el botón derecho en la variable, que puede ser una estructura:

Estructura de creación automática

La activación de esta función transformará la salida del Decompiler anterior en un código mucho más legible:

  if (((param_1->field_0x10 == param_1->field_0x8) && (param_1->field_0x28 == param_1->field_0x20)) && (param_1->field_0x48 == 0))

Editar el tipo de datos en param_1 ahora mostrará una estructura parcialmente definida en la ventana del Editor de estructuras:

Ventana del editor de estructuras

La estructura se creó con un nombre genérico astruct y, aunque se infirieron algunos tipos de datos, muchos de los tipos no reflejan el código fuente original. A partir de aquí, depende de la ingeniería inversa aplicar información contextual para actualizar nombres de variables y tipos de datos.

Refinar un tipo de datos de estructura

A menudo he tenido la suerte de obtener suficiente contexto de los mensajes de registro y depuración incrustados en el código para dar algunos nombres más significativos. Es inmensamente útil ver estos nombres cuando se hace referencia al mismo desplazamiento en otras partes del programa. En la mayoría de los casos, es posible realizar ediciones útiles en la estructura automática directamente desde el menú contextual param_1:

menú contextual param_1

Renombrar campo y Reescribir campo hacen posible cambiar rápidamente el nombre de una variable o asignar un tipo de datos sobre la marcha a partir de la salida descompilada. Sin embargo, es importante señalar que para que se asigne un nuevo tipo de datos, la estructura debe tener una asignación de espacio adecuada. Si no hay suficiente espacio disponible, Ghidra generará un error de que el tipo de datos no encaja. Esto puede suceder particularmente cuando se trata de cosas como estructuras anidadas donde Ghidra ya puede haber inferido los tipos de datos de variables de miembros anidados.

Observaciones finales

El descompilador de Ghidra generalmente es bastante bueno para reconocer cuándo se usa una estructura, pero hay situaciones en las que puede confundir una estructura con una matriz. Al encontrar una estructura desconocida en un programa analizado, la función de creación automática de estructuras de Ghidra es un excelente recurso para crear una representación básica de la estructura. Se limita a lo que se puede inferir al analizar las compensaciones y las pistas de contexto del desensamblaje, pero servirá como punto de partida a medida que avanza en un esfuerzo de ingeniería inversa.

También dirijo una sesión de capacitación en Black Hat USA llamada ‘Una guía para principiantes para dar marcha atrás con Ghidra’. Si estás interesado en aprender más sobre el tema, haz clic aquí: https://www.blackhat.com/us-21/training/schedule/index.html#a-beginners-guide-to-reversing-with-ghidra-21992

Publicaciones Similares