En esta publicación, analizaremos un binario de firmware STM32 en Ghidra. En particular, el firmware es para la placa de desarrollo STM32F103C de STMicroelectronics.
El archivo se puede descargar desde este enlace.
El análisis de archivos binarios de firmware suele ser diferente del análisis de un archivo PE o ELF. Un PE (ejecutable portátil) es el formato de archivo ejecutable estándar en Windows. Un .exe el archivo es un PE debajo. El formato de archivo PE está diseñado para sistemas Windows de 32 bits. Existe el formato de archivo PE64 que es similar a PE pero está diseñado para sistemas de 64 bits.
En consecuencia, en Linux tenemos el archivo ELF (formato ejecutable y enlazable) que tiene el mismo propósito. Ambos formatos de archivo tienen una estructura definida. Tienen un encabezado que describe cómo se colocará el archivo en la memoria cuando se ejecute. Las direcciones de las secciones de código y datos se proporcionan en el encabezado del archivo. Los desensambladores como Ghidra usan esta información para diferenciar automáticamente entre código y datos y cargar el archivo en la dirección correcta.
Un archivo de firmware plano, por otro lado, es solo un blob binario, un montón de bytes sin encabezado o metadatos que describen el diseño del archivo. Al examinar un archivo de este tipo, el propio analista debe proporcionar la información a Ghidra.
Analisis preliminar
Avancemos y carguemos el firmware en Ghidra. Dado que es un archivo binario sin procesar, Ghidra no sabe cómo procesarlo.

STM32F103 son una serie de microcontroladores alimentados por el procesador ARM Cortex-M3. Cortex-M3 es un procesador de 32 bits. Hagamos clic en el botón Opciones de idioma y establezcamos «ARM-Cortex-32-little» como idioma.

Dejando las otras opciones como están, ahora podemos continuar cargando el archivo y hacer doble clic para abrirlo en el desensamblador.

Ghidra le pedirá que analice el archivo y hacemos clic en sí manteniendo las opciones de análisis predeterminadas. Echemos un vistazo al código desensamblado después de que finalice el análisis.

Podemos notar que varias direcciones están marcadas con texto de color rojo. Las direcciones son de la forma 08000XXX
. Ghidra marca una dirección en rojo cuando la dirección especificada no existe en el archivo. Hacer doble clic en la dirección no conduce a ninguna parte.
Del mismo modo, analicemos la lista de desmontaje de cualquier función (digamos FUN_000003e4
) haciendo clic en él en el árbol de símbolos.

La lista de desmontaje tiene varias otras direcciones marcadas en rojo.

Hay referencias a direcciones de la forma e000xxxx
y 20000xxxx
que Ghidra no pudo resolver. Además, si buscamos cadenas, podemos ver que las cadenas no tienen ninguna referencia que las apunte.

Todo esto indica que no hemos cargado el archivo en la dirección correcta. Si hubiéramos especificado la dirección correcta al cargar el archivo en Ghidra, al menos algunas de las cadenas tendrían una referencia que las señalaría, si no todas.
Por lo tanto, nuestro próximo paso es encontrar la dirección de carga correcta en la memoria para un firmware STM32. Esta información a menudo se puede encontrar en la hoja de datos del dispositivo y en los archivos de encabezado del compilador.
Pasando por la hoja de datos
En el archivo de encabezado stm32f103x6.h podemos ver que la dirección base de flash es 0x08000000
. Esto explica la presencia de 0x08000xxx
dirección en el listado de desmontaje. Por lo tanto, la dirección de carga del firmware es 0x08000000
.
SRAM comienza en 0x20000000
lo que explica el 20000xxxx
direcciones.
#define FLASH_BASE 0x08000000UL /*!< FLASH base address in the alias region */
#define FLASH_BANK1_END 0x08007FFFUL /*!< FLASH END address of bank1 */
#define SRAM_BASE 0x20000000UL /*!< SRAM base address in the alias region */
#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */
#define SRAM_BB_BASE 0x22000000UL /*!< SRAM base address in the bit-band region */
#define PERIPH_BB_BASE 0x42000000UL /*!< Peripheral base address in the bit-band region */
/*!< Peripheral memory map */
#define APB1PERIPH_BASE PERIPH_BASE
[...]
#define FLASH_R_BASE (AHBPERIPH_BASE + 0x00002000UL) /*!< Flash registers base address */
#define FLASHSIZE_BASE 0x1FFFF7E0UL /*!< FLASH Size register base address */
#define UID_BASE 0x1FFFF7E8UL /*!< Unique device ID register base address */
#define OB_BASE 0x1FFFF800UL /*!< Flash Option Bytes base address */
#define DBGMCU_BASE 0xE0042000UL /*!< Debug MCU registers base address */
La misma información también se puede encontrar en el mapa de memoria del procesador de la hoja de datos del dispositivo.
Recreando el mapa de memoria en Ghidra
Volvamos a importar el archivo una vez más en Ghidra pero especificando la dirección base correcta esta vez. La dirección base se puede especificar haciendo clic en el botón de opciones en el cuadro de diálogo de importación.

Tenga en cuenta que la dirección base se ha establecido en 0x08000000
. Siguiendo adelante con el análisis podemos ver el 0x08000xxx
las direcciones ya no están marcadas en rojo. Esto se debe a que Ghidra puede ubicar la dirección dentro del archivo.

De manera similar podemos crear el segmento SRAM en 0x20000000
. Según la hoja de datos que conocemos, los dispositivos STM32F103C8 cuentan con un tamaño de memoria flash de 64 KiB y SRAM de 20 KiB. Nuestro archivo de firmware ya tiene un tamaño de 64 KiB. 20 KiB es 0x5000
en hexadecimal.

A continuación, vaya a Ventana -> Mapa de memoria y haga clic en «+» para agregar un nuevo bloque de memoria. Especificar 0x20000000
como dirección base y 0x5000
como longitud.

Los periféricos se encuentran en la dirección 0xE0000000
por una longitud de 0x100000
bytes

En consecuencia, podemos crear el bloque de memoria en Ghidra.

Volviendo a FUN_080003e4
podemos notar que las direcciones ya no están marcadas en rojo.

De la misma manera, las cadenas tienen referencias que aparecen junto a ellas.

Esto implica que hemos cargado el archivo en la dirección correcta con la asignación de memoria adecuada.
Analizando el binario del firmware
La presencia de las cadenas «Ingrese la contraseña», «Autenticación exitosa» indica que el firmware tiene algún tipo de lógica de verificación de contraseña. La cadena «Ingrese la contraseña:» tiene una referencia de la función FUN_080007ee
. El código descompilado parece

Al revisar el código descompilado, podemos inferir que el firmware lee una cadena byte por byte hasta que encuentra el r
personaje. La contraseña se almacena en la variable. local_20
. Luego se hace una llamada a la función FUN_080002e0
pasando el búfer de contraseña como argumento.
El código descompilado de FUN_080002e0
es simple.

El búfer de contraseña junto con un puntero a la cadena «attify» se pasa a la función FUN_08003910
. Si la función devuelve cero, FUN_08000290
se llama. De lo contrario llama FUN_0800024c
para un valor de retorno distinto de cero.
FUN_08000290
parece

La presencia de la cadena «Autenticación exitosa» indica que se llamará a esta función si la contraseña era correcta.
En la otra función, FUN_0800024c
podemos ver la cadena «Autenticación fallida», lo que implica que se llamará si la contraseña es incorrecta.

Podemos deducir que la función FUN_08003910
es strcmp
haciendo una comparación de cadenas de las dos cadenas pasadas. strcmp
devuelve 0 cuando las dos cadenas coinciden (iguales). La contraseña correcta es, por lo tanto, la cadena «attify», pasada como segundo parámetro a strcmp
.
Uso del cargador SVD
SVD-Loader de Leveldown Security es un complemento de Ghidra para automatizar la creación de segmentos de memoria y periféricos para firmware ARM bare metal. El complemento analiza los archivos SVD y crea automáticamente los segmentos de memoria. El campo SVD para varias plataformas ARM se puede obtener del repositorio cmsis-svd GitHub.
Instalar SVD-Loader en Ghidra es simple. Después de clonar el repositorio, la ruta al directorio se puede agregar a la lista de directorios de Script en el Administrador de Script. Se puede acceder al Administrador de secuencias de comandos desde Ventana -> Administrador de secuencias de comandos.
Antes de ejecutar SVD-Loader, debemos asegurarnos de que el archivo se cargue en la dirección base correcta 0x08000000
de la misma manera que lo hicimos antes. Una vez cargado el firmware en Ghidra, podemos ejecutar SVD-Loader.py script desde el administrador de scripts.

El complemento solicitaría especificar la ruta a un archivo SVD para analizar. Podemos seleccionar el archivo STM32F103xx.svd descargado del repositorio cmsis-svd y pulsar en “Cargar archivo SVD”.

Después de que SVD-Loader haya terminado de importar, podemos ir a la ventana del mapa de memoria.

SVD-Loader ha creado automáticamente los bloques de memoria y los periféricos al analizar el SVD. Sin embargo, vale la pena señalar que el complemento no creó el bloque de memoria SRAM en 0x20000000
. Podemos crear el bloque SRAM manualmente. En este punto, podemos ejecutar el análisis automático de Ghidra con las opciones predeterminadas de Análisis -> Análisis automático.
A continuación, podemos ir a 080007ee para verificar el código descompilado de la lógica de verificación de contraseña.

Desafortunadamente, Ghidra no pudo identificar la función por sí solo. En tales casos, podemos definir manualmente una función haciendo clic derecho en esa dirección -> Desmontar. Al hacerlo, Ghidra crea una función en esa dirección con el mismo código descompilado que antes.

Ultimas palabras
En este post hemos visto cómo analizar un firmware bare metal en Ghidra. Un firmware bare metal es solo una mancha binaria. Para analizar correctamente dicho archivo, necesitamos especificar la dirección de carga y crear los segmentos de memoria. La información sobre la dirección de carga y los segmentos de memoria se puede encontrar en la hoja de datos y en los archivos de encabezado del compilador. El mapa de memoria también se puede crear automáticamente usando SVD-Loader analizando archivos SVD. Sin embargo, es posible que SVD-Loader no cree todos los segmentos. SVD-Loader solo creará segmentos que están definidos en el archivo SVD. Por lo tanto, es importante verificar siempre con la hoja de datos para garantizar la exactitud.