Iniciación al reversing

Desmonta, explora y comprende tu propio firmware para recuperar el control sobre los dispositivos que usas cada día.

Iniciación al reversing

Created by potrace 1.15, written by Peter Selinger 2001-2017 Este artículo está hecho con "IA"

Gracias a IA entré en el mundillo del reversing. IA de Increíble Arrizen. Gracias por haberme guiado de la mano en mis primeros pasos en este campo, amigo. Por haberme quitado el miedo a equivocarme, a probar cosas y a machacar el código hasta la extenuación.

¿Por qué una entrada sobre reversing?

En el mundo actual, estamos constantemente interactuando con objetos que contienen en su interior pequeños ordenadores, es decir, microcontroladores que ejecutan instrucciones de forma autónoma.

Algunos de estos dispositivos han sido bautizados con el apelativo de inteligentes o smart, mientras que otros no presumen de ello (igual tampoco lo son tanto). Sin embargo, todos comparten una misma realidad tecnológica: funcionan gracias a fragmentos de código —el llamado firmware— que opera de forma invisible para el usuario final.

Created by potrace 1.15, written by Peter Selinger 2001-2017 Hay dispositivos que solo fallan después de X usos

Algunos firmwares incluyen contadores internos para limitar la vida útil de impresoras, luces o filtros. Esto se llama obsolescencia programada por software, y el reversing ha sido clave para detectarla.

Ese firmware, inaccesible para la mayoría, es el encargado de tomar decisiones cotidianas: determina si tu cepillo de dientes eléctrico tiene dos o tres modos de funcionamiento, decide cuándo debe apagarse automáticamente una lámpara conectada por WiFi, o controla el comportamiento de sensores, cerraduras electrónicas, juguetes que emiten sonidos, alarmas, termostatos, básculas y un sinfín de dispositivos más.

A pesar de haber pagado por esos aparatos y tenerlos físicamente en casa, el software que los hace funcionar está completamente cerrado, protegido, y fuera del alcance de quienes los utilizamos.

¿Por qué mirar "dentro" es necesario?

Para obtener nuestra respuesta, basta con repasar algunos casos reales, documentados por la comunidad de personas que practican ingeniería inversa:

  • Un sensor de temperatura, adquirido en una plataforma popular como Amazon, continuaba enviando datos a los servidores del fabricante incluso después de haber sido desvinculado de la aplicación oficial.
  • Un juguete aparentemente inofensivo grababa fragmentos de voz de niños sin informar a los padres y almacenaba estas grabaciones incluso cuando el dispositivo se encontraba “apagado”.
  • Una cerradura WiFi respondía a comandos sin haber sido correctamente emparejada, lo que generaba un grave riesgo de seguridad.
  • Un router doméstico contenía una función interna denominada debug_backdoor() que sólo podía ser descubierta mediante el análisis directo de su firmware.

Estos casos no son excepcionales. No se trata de errores aislados, sino de ejemplos de una realidad mucho más común de lo que imaginamos. Y la única forma fiable de descubrirlos no pasa por leer titulares o confiar en manuales; pasa por mirar directamente dentro del código binario

Created by potrace 1.15, written by Peter Selinger 2001-2017 Jugando gracias a la BIOS

Varios ingenieros escondieron huevos de pascua en firmwares de BIOS. Un ejemplo famoso: Award BIOS tenía un Space Invaders oculto, visible solo con una combinación específica.


Entonces, ¿qué es el reversing?

La ingeniería inversa de firmware —también conocida como reversing— es la disciplina que permite analizar sistemas embebidos cuando no se dispone del código fuente original. Es un proceso meticuloso que parte de lo visible (un comportamiento, un binario, un chip) y avanza hacia lo oculto (la lógica, las funciones, la intención del fabricante).

El reversing consiste en abrir un archivo binario, examinarlo, identificar funciones, seguir rutas de ejecución, descubrir accesos a memoria y, si es posible, modificar comportamientos. Lejos de ser un proceso que parece ser un conocimiento esotérico reservado a expertos, se trata de una práctica accesible que cualquier persona con interés, paciencia y un mínimo de conocimientos técnicos puede comenzar a explorar.

Este aprendizaje tiene muchas aplicaciones reales:

  • Permite reparar dispositivos que han dejado de funcionar y que el fabricante ya no da soporte.
  • Facilita la extensión de funcionalidades, sin depender de actualizaciones oficiales.
  • Abre una ventana a comprender cómo operan realmente los sistemas electrónicos que usamos a diario.
  • Y sobre todo, devuelve al usuario el control sobre los objetos que lo rodean.

¿Por qué usar un chip Cortex-M0?

Porque el Cortex-M0 es uno de los microcontroladores más utilizados a nivel global. Su bajo coste, simplicidad estructural y alta disponibilidad lo han convertido en el corazón de muchos de dispositivos: sensores, juguetes, cerraduras electrónicas, placas educativas, dispositivos domóticos y herramientas de aprendizaje.

A mi parecer, es una puerta de entrada al mundo del reversing porque:

  • Puedes adquirir uno fácilmente, por precios realmente bajos.
  • Es posible obtener el firmware de muchas de estas placas, ya sea desde el chip o desde actualizaciones.
  • Su arquitectura es simple, está documentada y es compatible con herramientas de software libre como Ghidra o Hiew.
  • Su funcionamiento es directo: no hay sistema operativo que oculte el flujo de ejecución, no hay capas intermedias.

¿Qué significa hacer reversing, y por qué es relevante incluso para quien no se dedica a la electrónica?

Imagina que tienes en casa un pequeño dispositivo —por ejemplo un medidor de CO₂ con conectividad WiFi— que, por algún motivo, parece estar transmitiendo datos incluso cuando ya no está emparejado con ninguna aplicación oficial.

Tal vez revisas la documentación y no encuentras ninguna mención a ese comportamiento. Visitas la web del fabricante y descubres que ha desaparecido, o que ya no ofrece soporte técnico.

En ese momento, te enfrentas a un dilema: puedes aceptar el funcionamiento del aparato como una caja negra, resignarte a no entenderlo y seguir utilizándolo… o puedes decidir abrirlo, examinar qué tiene en su interior y tratar de comprender cómo y por qué se comporta así.

Ese es, en esencia, el punto de partida de la ingeniería inversa de firmware: partir de lo que tienes —un archivo binario, un circuito, un comportamiento observable— para tratar de alcanzar lo que no tienes —la lógica del programa, su intención original, las decisiones ocultas del código.


¿Cómo se hace reversing?

El proceso de reversing es una práctica racional, estructurada y basada en la observación, el análisis progresivo y el uso de herramientas que, en muchos casos, son de acceso libre y gratuito.

La mayoría de dispositivos embebidos, esos que no tienen sistema operativo tradicional, ejecutan firmware: un bloque compacto de instrucciones grabadas directamente en la memoria del chip, generalmente en Flash. Este bloque no es legible como un texto común, pero sí puede ser abierto como archivo binario y examinado byte a byte.

El procedimiento suele seguir la siguiente lógica:

  1. Obtener el binario: Puedes hacerlo extrayéndolo físicamente del chip mediante un programador; o bien localizando una copia en alguna actualización pública distribuida por el fabricante. En algunos casos, es posible interceptarlo durante una transferencia OTA (over-the-air) o acceder a él desmontando el dispositivo y leyendo su memoria directamente.
  2. Explorar su contenido: Abrir el archivo resultante en un visor hexadecimal —como Hiew— te permite observar su estructura en crudo. Aunque lo primero que veas sean números aparentemente sin sentido, pronto empezarás a reconocer patrones. Verás la tabla de interrupciones al comienzo, bloques de código ensamblador, cadenas de texto embebidas y regiones con datos fijos o modificables.
  3. Desensamblar y decompilar: Usando herramientas como Ghidra, podrás transformar ese conjunto de instrucciones binarias en algo más cercano al lenguaje C. No es una traducción exacta, pero sí una aproximación que permite identificar funciones, condicionales, bucles, accesos a memoria y llamadas a rutinas internas.
  4. Identificar comportamientos significativos: Aquí es donde empieza la interpretación. ¿El código accede a puertos de entrada/salida? ¿Implementa un bucle de espera o un retardo? ¿Se comporta de forma diferente según ciertas condiciones? Todo esto puede deducirse a partir del análisis estructurado del flujo del programa.
  5. Modificar (opcional): Si llegas a comprender con suficiente profundidad qué hace el firmware, puedes incluso modificarlo. Puedes, por ejemplo, eliminar retardos innecesarios, cambiar cadenas de texto, activar modos ocultos o invertir lógicas internas. Si lo haces bien, el dispositivo comenzará a responder de una manera distinta porque lo has decidido así.

Ejemplo concreto: cuando el conocimiento permite recuperar un dispositivo olvidado

Para ilustrar el valor real de esta práctica, basta con revisar el caso de una persona que descubrió que su báscula de baño “inteligente” contenía, en su interior, un pin oculto que activaba un menú de diagnóstico. Este menú no estaba documentado por el fabricante, ni accesible mediante la aplicación oficial. Sin embargo, tras analizar el firmware, descubrió que al escribir un valor concreto en la dirección de memoria 0x40021010, la báscula desbloqueaba una funcionalidad no prevista para el usuario común.

Gracias a este hallazgo, pudo no solo resetear la báscula —sin necesidad de una app que ya había dejado de funcionar— sino también reutilizarla de forma independiente, sin conexión a la nube.

¿Hace falta ser un experto para hacer esto?

Aunque tener conocimientos de programación en C o nociones básicas de ensamblador puede ser de ayuda, lo más importante, como en todo, es tener una actitud curiosa y observar con paciencia.

Las herramientas actuales están diseñadas para facilitar el trabajo pesado: decompilan, destacan funciones, interpretan patrones y permiten navegar visualmente por el flujo del programa.

Aprender ingeniería inversa es, en muchos sentidos, como aprender un idioma nuevo. No necesitas dominar toda su gramática para empezar a comprenderlo. 

¿El reversing es legal?

La legalidad del reversing depende del país, del propósito y del uso final.

En la mayoría de contextos legales, analizar un firmware con fines educativos, de investigación o de reparación no constituye una infracción. No estás rompiendo sistemas de seguridad, no estás distribuyendo exploits, no estás publicando material protegido. Estás ejerciendo tu derecho a entender cómo funciona un dispositivo que tú has comprado.


Entonces...¡vamos a por ello!

Cuando uno se inicia en el mundo de la ingeniería inversa, es tentador lanzarse a analizar routers, teléfonos antiguos, consolas o dispositivos complejos (al final, los amantes de la tecnología somos un poco flipados). Pero la realidad es que estos sistemas tienen múltiples capas de seguridad, protocolos criptográficos, gestores de archivos y muchas más barreras de entrada.

Por eso, como comenté previamente en este post, es infinitamente más sensato comenzar por un entorno donde el código se ejecuta de forma directa, donde no hay sistema operativo que lo complique todo, y donde las direcciones de memoria están claramente documentadas. Y ese entorno amigable es el del Cortex-M0.

Cómo crear tu propio firmware desde cero para Cortex-M0

Uno de los mejores puntos de partida para comprender cómo funciona un firmware, cómo se estructura y cómo se analiza es escribir uno propio (básico).

No se trata de un ejercicio trivial, ni tampoco de una pérdida de tiempo si tu objetivo es aprender a hacer ingeniería inversa. Al contrario: es precisamente al compilar tú mismo el código, observar qué genera, y luego leer ese binario con herramientas como Ghidra o Hiew, cuando comienzas a entender cómo funciona un chip.

Un firmware para Cortex-M0 es, simplificando la definición un poquito, un programa mínimo que se ejecuta directamente desde la memoria Flash del chip, sin sistema operativo, sin drivers externos y sin gestores de recursos.

¿Qué vamos a construir?

Un firmware mínimo, que simplemente:

  • Activa un pin escribiendo en una dirección de memoria (simulando un LED encendido).
  • Espera un tiempo mediante un bucle de retardo.
  • Apaga el pin escribiendo otro valor.
  • Vuelve a esperar.
  • Repite este ciclo indefinidamente.

Esto es lo que se conoce como el “blinky loop”, el “Hola, mundo” del desarrollo embebido.

Primero, vamos a desarrollar los scripts necesarios:

main.c – El comportamiento básico: encender, esperar, apagar

// main.c
#define LED_ADDR (*(volatile unsigned int*)0x50000000)

void delay() {
    for (volatile int i = 0; i < 100000; i++);  // Bucle vacío como retardo
}

int main() {
    while (1) {
        LED_ADDR = 1;  // Encender el LED (o activar un pin)
        delay();       // Esperar
        LED_ADDR = 0;  // Apagar el LED (o desactivar el pin)
        delay();       // Esperar
    }
}

Este archivo muestra lo esencial: acceso directo a hardware (simulado aquí con 0x50000000) y control explícito del flujo con bucles simples. Sin llamadas externas, sin bibliotecas, sin intermediarios.

startup.c – Definimos cómo arranca el microcontrolador

// startup.c
#include 

/* Declaración de funciones */
extern int main(void);
void Reset_Handler(void);
void Default_Handler(void);

/* Vector de interrupciones: la tabla que el microcontrolador lee al encenderse */
__attribute__ ((section(".isr_vector")))
uint32_t *isr_vector[] = {
    (uint32_t*) 0x20002000,  // Valor inicial del stack pointer (tope de RAM)
    (uint32_t*) Reset_Handler  // Dirección a la que saltar tras reset
};

/* Manejador por defecto para interrupciones no definidas */
void Default_Handler(void) {
    while (1);
}

/* Reset_Handler: inicializa memoria y llama a main() */
extern uint32_t __data_start__, __data_end__, __bss_start__, __bss_end__, _etext;

void Reset_Handler(void) {
    // Copiar .data desde Flash a RAM
    uint32_t *src = &_etext;
    uint32_t *dst = &__data_start__;
    while (dst < &__data_end__) {
        *dst++ = *src++;
    }

    // Inicializar .bss a cero
    dst = &__bss_start__;
    while (dst < &__bss_end__) {
        *dst++ = 0;
    }

    main();  // Saltar al programa principal

    // Si main termina, quedarse detenido
    while (1);
}

Este archivo es la clave para que el firmware funcione: configura el vector de interrupciones, inicializa memoria y define qué ocurre cuando el sistema se reinicia. Es lo mínimo que necesita cualquier Cortex-M0 para funcionar.

linker.ld – Guía para colocar todo en memoria

/* linker.ld */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
  RAM (rwx)  : ORIGIN = 0x20000000, LENGTH = 8K
}

SECTIONS
{
  .isr_vector :
  {
    KEEP(*(.isr_vector))  /* Asegura que la tabla de interrupciones no sea eliminada */
  } > FLASH

  .text :
  {
    *(.text*)       /* Código ejecutable */
    *(.rodata*)     /* Datos de solo lectura */
  } > FLASH

  .data : AT (ADDR(.text) + SIZEOF(.text))
  {
    __data_start__ = .;
    *(.data*)       /* Variables inicializadas */
    __data_end__ = .;
  } > RAM

  .bss :
  {
    __bss_start__ = .;
    *(.bss*)        /* Variables no inicializadas */
    *(COMMON)
    __bss_end__ = .;
  } > RAM
}

El linker script es el plano arquitectónico del firmware: indica dónde se debe colocar cada sección del programa en la memoria del microcontrolador. Este archivo es fundamental para que el binario funcione correctamente.

Compilarlo todo: del código al binario

Para convertir este conjunto de archivos en un firmware real que puedas flashear o analizar, necesitas usar el compilador cruzado de ARM. Aquí te dejo los comandos básicos:

arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -nostdlib -T linker.ld startup.c main.c -o firmware.elf
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin

Esto produce dos archivos:

  • firmware.elf: contiene símbolos y metadatos, útil para depuración.
  • firmware.bin: un binario plano, listo para flashear o analizar con herramientas de ingeniería inversa.

¿Y ahora qué hacemos con todo esto?

Este binario, aunque simple, es una joya (está feo que yo lo diga) para aprender. Porque ahora puedes:

  • Abrirlo en Ghidra, cargarlo como RAW binario con base 0x08000000, y ver cómo reconstruye funciones como main o delay.
  • Explorarlo con Hiew, viendo las direcciones reales, los opcodes como MOVS, STR, BNE, y empezar a reconocer patrones.
  • Compararlo con binarios reales, ya extraídos de dispositivos, y notar las similitudes estructurales.
  • Ejecutarlo en una placa real, si tienes una STM32 o similar, y observar cómo ese pin virtual cambia su estado.

Lo más importante es que este pequeño ejercicio te cambia la forma de ver cualquier otro binario. Te da referencias, te enseña a mirar, y te demuestra que lo binario no es opaco por naturaleza: solo necesita que estemos dispuestos a leerlo.

Created by potrace 1.15, written by Peter Selinger 2001-2017 El firmware del primer Furby ocupaba solo 32 KB

¡Menos que una foto actual! Pero era capaz de aprender palabras, emitir sonidos y reaccionar al tacto. Todo con una memoria miaunúscula.

Vamos a explorar un firmware compilado: cómo mirar por dentro con Hiew y Ghidra

Una vez que has compilado tu propio firmware —aunque sea el más simple posible— ya tienes algo que puedes diseccionar como si fuera el de un dispositivo comercial. La ventaja en este caso es que tú sabes exactamente lo que hace. 

Vamos a abrir firmware.bin con dos enfoques diferentes pero complementarios:

  • Uno bajo nivel, directo y sin abstracciones: Hiew32 (o cualquier visor hexadecimal).
  • Otro con interpretación automática y ayuda visual: Ghidra, el decompilador gráfico de la NSA.

Paso 1: abrir el binario con Hiew

Hiew32 es una herramienta clásica. Su interfaz es sobria, pero rápida y precisa: te muestra exactamente lo que hay en el binario, sin más pretensiones.

¿Qué deberías ver al principio del archivo?

Los primeros bytes de firmware.bin son cruciales. Corresponden al vector de interrupciones, que en tu caso incluye dos entradas:

  • La primera es el stack pointer inicial → 0x20002000 (tope de RAM).
  • La segunda es la dirección del Reset Handler → 0x08000005 (la dirección real con el bit de modo Thumb activado).

En hexadecimal, verás algo parecido a:

00 20 00 20   05 00 00 08

Esto confirma que tu firmware fue compilado para un Cortex-M0 (modo Thumb, 32 bits), y que se inicia como esperabas.

Navega hacia la sección de código

Un poco más abajo verás bloques de instrucciones como:

20 49        LDR     R1, [PC, #0x4]
01 22        MOVS    R2, #1
08 60        STR     R0, [R1]

Estos valores pueden variar dependiendo del compilador, pero si has seguido los pasos anteriores, verás claramente los accesos a memoria y los bucles de retardo. Incluso sin interpretar a fondo, ya puedes reconocer que ahí ocurre la escritura a 0x50000000 y que hay un bucle que decrementa un contador.

Paso 2: abrir el binario en Ghidra

Ghidra es una herramienta más avanzada, visual, y muy poderosa. Te permite ver funciones reconstruidas, flujos de ejecución, accesos a memoria e incluso decompilación aproximada al estilo C.

¿Cómo importar el binario?

  1. Abre Ghidra y crea un nuevo proyecto (tipo Non-Shared Project).
  2. Importa firmware.bin como tipo RAW.
  3. Cuando te pregunte por la dirección base, escribe: 0x08000000.
  4. Elige arquitectura: ARM:LE:32:Cortex y luego ajusta a Thumb mode si lo pide.
  5. Ejecuta el análisis automático.

¿Qué verás en Ghidra?

  • Una función llamada entry en 0x08000004, que corresponde al Reset_Handler.
  • Dentro de ella, deberías ver llamadas a otras funciones: copia de .data, limpieza de .bss, y finalmente un salto a main().
  • Ghidra te mostrará una función main() con un bucle while(1) donde hay escritura a memoria (*(0x50000000) = 1), llamada a delay(), luego escritura con 0, y otro delay.
  • También podrás navegar por la tabla de interrupciones en .isr_vector, donde reconocerás las dos primeras entradas que viste en Hiew.

¿Qué patrones deberías aprender a reconocer en reversing?

  1. Vector de interrupciones: Siempre está al principio. Define dónde arranca el código.
  2. Escrituras a direcciones tipo 0x50000000: Acceso a periféricos simulados (GPIO).
  3. Bucles de retardo: Instrucciones como MOVS, SUBS, BNE que forman un delay en ensamblador.
  4. Funciones pequeñas sin nombre: Ghidra las reconstruye, pero puedes renombrarlas (delay, led_on, etc.).
  5. Condicionales y saltos: Las instrucciones CMP, BNE, BEQ, BL, etc. revelan lógica de decisión.

¿Por qué usar Hiew y Ghidra a la vez?

HerramientaVentaja principal
Hiew32Te muestra el binario crudo: hex puro, directo
GhidraTe ayuda a interpretar: funciones, gráficos, C

Usar Hiew te entrena para pensar como la máquina. Usar Ghidra te entrena para pensar como ingeniera. Combinarlas es lo que te permite realmente leer un firmware.

Viendo tu propio firmware like a pro

Al abrir tu propio binario ya no estás simplemente usando herramientas para “desmontar cosas”, sino que estás entendiendo cómo una idea en C se transforma en una secuencia de bytes, y cómo esa secuencia da lugar a decisiones más o menos cuestionables dentro de un chip.

Aprendes que:

  • MOVS R0, #1 no es solo código máquina; es una intención.
  • STR R0, [R1] no es solo un acceso; es encender un LED.
  • Un BNE no es un salto cualquiera; es parte de un retardo pensado por una persona como tú (no te estoy llamando nada ofensivo, ¿eh?).

Y cuando luego abras firmwares reales —de sensores, juguetes, routers, básculas— sabrás reconocer estos patrones.

Así que ahora... vamos a profundizar en esos patrones.


Reconociendo patrones clave en firmwares de Cortex-M0

Una de las habilidades más importantes cuando haces ingeniería inversa es desarrollar la capacidad de detectar patrones típicos en el flujo binario. Al analizar un firmware —especialmente si es de un dispositivo comercial— no tendrás nombres de funciones, ni estructuras de alto nivel, ni comentarios (esto duele). Solo una sucesión de instrucciones.

Sin embargo, si aprendes a reconocer patrones, verás bucles que reconoces, funciones que identificas por su forma, escrituras a direcciones que sabes que son periféricos, y comparaciones que revelan condicionales.

Vamos a ver algunos de los patrones más frecuentes y cómo reconocerlos en Ghidra o Hiew.

1. Bucles de retardo ("delay loops")

En muchos sistemas embebidos no se usan temporizadores de hardware para las pausas, sino bucles vacíos. Es la forma más primitiva —pero efectiva— de hacer que el programa espere.

En C:

for (volatile int i = 0; i < 100000; i++);

En ensamblador (Ghidra o Hiew):

MOVS R3, #0x7A120     ; carga un valor grande en un registro
SUBS R3, R3, #1        ; resta 1 en cada iteración
BNE  loop_start        ; salta si no es cero

¿Cómo se reconocen?

  • Un MOVS que carga un valor numérico en un registro (usualmente #50000–200000).
  • Un SUBS que decrementa ese registro.
  • Un BNE que salta hacia atrás: crea el bucle.

Puedes reducir el valor de inicio (MOVS) para acortar el retardo, o eliminar el bucle para que el comportamiento sea inmediato. Muy útil para quitar esperas artificiales.

2. Escritura a GPIO o periféricos

El microcontrolador controla el mundo físico escribiendo valores en direcciones especiales de memoria. Estas direcciones corresponden a puertos (GPIO), timers, UART, etc.

En C:

*(volatile uint32_t*)0x50000000 = 1;

En ensamblador:

LDR R0, =0x50000000
MOVS R1, #1
STR R1, [R0]

¿Cómo se reconocen?

  • Una dirección constante entre 0x40000000 y 0x5FFFFFFF.
  • Un STR o STRB que escribe en esa dirección.
  • A menudo, se combina con un retardo inmediatamente después.

Puedes cambiar el valor que se escribe, redirigir a otro pin o, si lo combinas con el análisis del hardware, detectar qué está controlando exactamente.

3. Funciones pequeñas reutilizadas (por ejemplo, delay())

Aunque el compilador pierde los nombres, las funciones siguen existiendo. El código es idéntico cada vez que se llama, y puedes identificarlo si lo ves más de una vez.

En Ghidra:

void FUN_080000ac(void) {
  int counter = 100000;
  while (counter != 0) {
    counter--;
  }
}

¿Cómo se reconocen?

  • Bloques de código de 5 a 10 instrucciones.
  • Se repiten en distintos lugares del binario.
  • Están precedidas por BL o BLX (Branch with Link), que indica llamada a función.

Renombrar estas funciones dentro de Ghidra (por ejemplo, delay(), led_toggle(), check_button_state()) te ayuda a mantener una navegación clara del binario.

4. Condicionales y lógica de decisión

Cuando el firmware necesita tomar decisiones (“si se pulsa un botón, hacer X”), se traducen en instrucciones de comparación y salto condicional.

En ensamblador:

CMP R0, #0
BEQ somewhere   ; si es igual, salta

¿Cómo lo reconoces?

  • Combinaciones como CMP + BEQ, BNE, BLT, BGT.
  • A menudo, seguidas de un B (branch incondicional) para cubrir el “else”.

Puedes invertir la lógica cambiando BEQ por BNE, o modificar el valor de comparación. Esto permite alterar comportamientos sin añadir código nuevo.

5.  Inicialización del sistema ("SystemInit")

Al arrancar, el microcontrolador configura el sistema: relojes, periféricos, GPIO, y más. Esto suele estar antes de main().

¿Cómo se reconocen?

  • Bloques de escritura a direcciones 0x4002xxxx, 0x40021000, etc.
  • Múltiples STR, LDR, y a veces funciones llamadas antes de main.

Si entiendes cómo se inicializan los periféricos, puedes identificar qué partes del hardware están activas, incluso si no se usan en el resto del código.

Resumen visual de patrones comunes

PatrónInstrucciones claveQué indica
Retardo (delay loop)MOVS, SUBS, BNEEspera artificial
Escritura a GPIOLDR, MOVS, STRControl físico (LEDs, relés…)
Funciones pequeñasBL, BX, bloques cortosComportamientos reutilizados
CondicionalesCMP, BEQ, BNEToma de decisiones
Inicialización del sistemaSTR a 0x4002xxxxConfiguración de hardware

Aplicaciones reales del reversing

Este mismo tipo de análisis que aplicamos aquí tiene un enorme potencial en la vida real y ya ha sido utilizado con éxito para:

  • Activar funciones ocultas o deshabilitadas en dispositivos electrónicos, como los conocidos "modos debug".
  • Eliminar retardos artificiales introducidos por los fabricantes en impresoras, juguetes electrónicos o sistemas domóticos.
  • Documentar firmwares que han sido abandonados por el fabricante y que ya no cuentan con soporte oficial.
  • Verificar si los dispositivos médicos que utilizamos funcionan realmente como aseguran sus fabricantes.
  • Reparar o revitalizar dispositivos que han quedado obsoletos por falta de actualizaciones.

Reversing como acto de soberanía tecnológica

El firmware como caja negra

A diferencia de un objeto mecánico que puedes desmontar con un destornillador, el firmware, como he comentado al principio del post, es opaco. Puede estar cifrado, protegido mediante técnicas de ofuscación o simplemente no documentado, lo que impide saber cómo funciona o intervenir en su lógica.

Esto implica que, incluso si hemos pagado por ese dispositivo, muchas veces no tenemos ni la posibilidad legal ni técnica de repararlo, modificarlo o ampliarlo en función de nuestras necesidades.

“Una caja negra solo existe mientras nadie la abra.”Alexandre Oliva, activista de GNU.

El derecho a reparar y a comprender

El movimiento global por el derecho a reparar ha puesto sobre la mesa una cuestión muy clara: si un objeto es de tu propiedad, deberías tener la capacidad de arreglarlo y comprender cómo funciona.

Esto va más allá de cambiar una batería o soldar un cable suelto. También incluye poder:

  • Actualizar el software de un aparato que el fabricante ha dejado obsoleto intencionadamente.
  • Añadir funciones nuevas que tú necesitas.
  • Entender qué hace el dispositivo cuando se conecta a tu red o recopila tus datos.

Aquí es donde entra en juego el reversing, que se convierte en una herramienta para recuperar control. Porque si no hay código fuente, ni esquemas, ni documentación, el conocimiento se convierte en un privilegio restringido a quienes lo ocultan.

El conocimiento como forma de defensa

La mayoría de personas que aprenden técnicas de reversing no lo hacen con fines maliciosos. Lo hacen porque quieren aprender, entender y protegerse. Quieren saber cómo funcionan las cosas que usan.

“Cuando todo el software es invisible y opaco, el poder se concentra donde no hay luz.” Lorena Jaume-Palasí

Comprender el comportamiento interno de un firmware permite, por ejemplo:

  • Detectar si un sensor está recopilando más información de la que debería.
  • Verificar si tu router tiene funciones ocultas que podrían vulnerar tu privacidad.
  • Desactivar funciones no deseadas o invasivas.
  • Restaurar el funcionamiento de un aparato que ha sido desactivado de forma arbitraria.

Cultura maker, software libre y alfabetización técnica

El reversing no es solo una técnica de ingeniería: es también una práctica cultural.

Se relaciona estrechamente con la filosofía del software libre, con la cultura maker y la educación sin barreras para lograr la alfabetización digital.

Implica defender la idea de que lo digital también debe ser abierto, comprensible y modificable, como cualquier otra herramienta en una sociedad libre.

No todo el mundo tiene por qué interesarse en estas prácticas. Pero toda persona que quiera aprender debería tener los medios para hacerlo.

¿Por qué lo hacen tan difícil entonces?

La respuesta es tan incómoda como clara: porque el conocimiento da poder. Y a quienes construyen sistemas cerrados, les interesa que la mayoría de usuarios permanezca en la ignorancia.

Cifrar el firmware, ocultar protocolos, eliminar la documentación técnica o poner trabas legales no es una casualidad: es una forma de control. Es asegurarse de que nadie pueda cambiar el funcionamiento de un producto sin permiso de la empresa.

Reversing como gesto de reapropiación

El reversing es una forma de reapropiarnos del conocimiento que otros han decidido ocultar. De poder enseñar sin necesitar un acuerdo de confidencialidad. De reconstruir saberes desde lo que ya existe.

Esto tiene que ver con cómo se distribuye el conocimiento, con cómo respondemos ante un “esto no se toca”: con curiosidad y compartiendo información.

Este post nace de la idea de que creo firmemente que el conocimiento no debe ser un privilegio.

“El software es una ley. Y si no puedes leerla, estás en manos de quien la escribió.”Eben Moglen

Quizá también sea una forma de cuidar a los demás:

  • Cuidar los dispositivos que usamos, para que duren más.
  • Cuidar a quienes quieren aprender cómo funcionan las cosas, ofreciéndoles una entrada amable.
  • Cuidar el conocimiento técnico como si fuera una lengua común, y no una torre de marfil para unos pocos privilegiados.

Ejemplos reales de reversing con impacto social

  • Algunos proyectos de software libre nacieron del análisis de firmwares cerrados.
  • Personas han reactivado aparatos médicos para ser utilizados en zonas rurales sin acceso a tecnología moderna.
  • Estudiantes han aprendido ensamblador desmontando juguetes.
  • Colectivos han documentado el funcionamiento de routers antiguos para que no dependan de actualizaciones oficiales.
  • Investigadores han verificado si sensores de CO₂ miden correctamente, o solo muestran números decorativos.

Glosario técnico del reversing de firmware

Término / SiglaDefinición clara
FirmwareCódigo precompilado que controla un dispositivo embebido. Vive en la memoria Flash del microcontrolador.
Cortex-M0Microcontrolador ARM de 32 bits, muy simple y común en dispositivos baratos, educativos o domésticos.
FlashMemoria no volátil donde se guarda el firmware. No se borra al apagar el dispositivo.
SRAMMemoria de acceso aleatorio volátil donde se almacenan variables y la pila (stack).
Linker ScriptArchivo (.ld) que indica dónde se coloca cada parte del programa en la memoria del microcontrolador.
Vector de interrupcionesTabla al comienzo del firmware que define el punto de arranque (Reset_Handler) y otras rutinas críticas.
Reset_HandlerFunción que se ejecuta al encender el dispositivo. Suele inicializar la memoria y llamar a main().
GhidraDecompilador visual de código binario, desarrollado por la NSA. Permite analizar firmware en ensamblador o C.
HiewEditor y visor hexadecimal clásico para ver binarios a bajo nivel, en modo texto.
BL / BLXInstrucción de ensamblador ARM que llama a una función (Branch with Link).
B / BNE / BEQInstrucciones de salto condicional: B (salto), BNE (si no es igual), BEQ (si es igual).
MOVS / SUBSInstrucciones que mueven (MOVS) o restan (SUBS) valores en registros. Usadas en bucles de retardo.
STR / LDREscribir (STR) o leer (LDR) desde una dirección de memoria. Sirven para manejar GPIO y periféricos.
0x40000000 – 0x5FFFFFFFRango de direcciones de memoria mapeada a periféricos (GPIO, UART, timers…).
GPIO“General-Purpose Input/Output”. Pines digitales para encender LEDS, leer botones, etc.
OTA“Over The Air”: actualización de firmware mediante red o conexión inalámbrica.
Delay loopBucle simple que consume tiempo (ej. for (i = 0; i < X; i++)) para crear esperas sin temporizadores.
ThumbConjunto reducido de instrucciones de ARM, usado en Cortex-M0. Usa instrucciones de 16 bits.
DesensambladoProceso de convertir binario crudo en instrucciones de ensamblador.
DecompilaciónProceso de convertir instrucciones de máquina a una representación aproximada en lenguaje C.
RE / ReversingIngeniería inversa (Reverse Engineering): analizar sistemas sin tener acceso al código fuente.
OpenOCDHerramienta que permite depurar y leer firmware directamente desde microcontroladores reales.
arm-none-eabi-gccCompilador cruzado que genera binarios ejecutables para microcontroladores ARM sin sistema operativo.
firmware.elf / firmware.binArchivos generados tras la compilación: .elf incluye símbolos, .bin es el binario plano ejecutable.
.bss / .data / .textSecciones de memoria del firmware: variables no inicializadas, inicializadas, y código ejecutable.
Created by potrace 1.15, written by Peter Selinger 2001-2017

Otros artículos

Gaza: el genocidio asistido por IA

Gaza: el genocidio asistido por IA

Miaunipulación digital

Miaunipulación digital

El patonejo: entre picos y orejas

El patonejo: entre picos y orejas

Herramientas de optimiauzación en Python

Herramientas de optimiauzación en Python

Tipos de datos en Python 

Tipos de datos en Python