Pruebas un pastel de elote en una fonda, te encanta y la dueña no te da la receta. En casa lo intentas: lo pruebas despacio, separas la costra de la miga, comparas la textura, horneas tres versiones y apuntas qué cambia. Al final tienes un pastel que sabe igual, una receta tuya y una idea bastante buena de cómo trabaja esa cocina, aunque nunca viste su libreta. Eso es la ingeniería inversa: averiguar cómo funciona algo a partir del producto terminado, sin sus planos ni su código fuente. Se hace con aparatos, con firmware, con programas y con protocolos de red, y está detrás de buena parte de la seguridad informática, de la compatibilidad entre programas y de la reparación de aparatos que su fabricante abandonó.
El nombre lo dice: la ingeniería normal va del diseño al producto, y esta va del producto al diseño. En software el punto de partida casi siempre es un binary (binario), el archivo ya compilado que corre la computadora o el aparato. Al compilar se pierden los nombres de las variables, los comentarios y buena parte de la estructura, así que lo que se recupera nunca es el código original, sino una explicación: qué hace, cómo lo decide y con qué formato habla con los demás. Andrew Tridgell, que empezó Samba, el programa que permite a Linux compartir archivos con redes de Windows, lo describió con otra analogía en 2003: su «técnica del café francés» consistía en aprender un idioma sin libros, sentado en un café de París, apuntando lo que los clientes le piden al mesero y qué llega a la mesa. Con un programa que escucha la red, el equipo de Samba hacía lo mismo con las conversaciones entre computadoras con Windows. Y para aprender las groserías, decía, hay que pisarle el pie al mesero: provocar errores a propósito y anotar qué responde el servidor.
Claves de la ingeniería inversa
- Qué es: estudiar un producto terminado para entender cómo funciona, sin acceso a sus planos ni a su código fuente.
- Qué no es: copiar. El resultado útil es conocimiento (un formato, un protocolo, un fallo de seguridad); reproducir el código o el producto ajeno es otra cosa, y casi siempre ilegal.
- Dos formas de mirar: el análisis estático lee el archivo sin ejecutarlo; el dinámico lo ejecuta y observa qué hace. Se combinan: uno da el mapa y el otro confirma por dónde se camina.
- Varios niveles: el aparato físico, su firmware, los binarios y el tráfico de red. No siempre se pasa por todos.
- El resultado es aproximado: un decompiler entrega un código parecido al original, sin nombres ni comentarios, que alguien tiene que interpretar y comprobar.
- Es legal en muchos casos: las leyes de Estados Unidos, la Unión Europea y México la permiten para fines como la interoperabilidad o la seguridad, con condiciones. Los contratos y términos de uso también cuentan.
Para qué se usa
- Seguridad y análisis de malware. Nadie le entrega el código fuente a quien analiza un virus, ni a quien audita un aparato sospechoso. En el caso de la botnet XCPE, que Google desmanteló este mes, la pista empezó cuando la empresa de investigación Synthient compró una caja Android TV MECOOL KM9PRO Max y encontró una puerta trasera preinstalada en la partición del sistema de su firmware; Google atribuyó después la operación al fabricante. Lo mismo vale para el lado de la defensa: buscar fallos en un programa compilado, comprobar qué datos manda una app o verificar que un parche corrige lo que dice.
- Compatibilidad. Wine permite correr programas de Windows en Linux y macOS porque implementa, una por una, las funciones de Windows que esos programas llaman. Samba, como se vio arriba, nació de escuchar la red. Lo mismo pasa con formatos de archivo cerrados: para que un programa abra los documentos de otro, alguien tuvo que averiguar cómo están organizados los bytes.
- Reparación y aparatos abandonados. Cuando un fabricante apaga sus servidores, el aparato puede quedarse a medias. Weber apagó en septiembre los de sus hornos inteligentes June y no liberó su app; un grupo de voluntarios, Project July, les instala ahora un servidor local, según el artículo de juristas de Cornell que reseñamos.
- Preservación de juegos. La mayoría de los juegos nunca publicó su código. Proyectos como la descompilación de Super Mario 64, terminada en 2019, lo reconstruyeron función por función a partir del juego, y gracias a eso hoy se juegan en computadoras modernas e incluso en el navegador, siempre con la copia del juego de cada quien.
- Aprender. Ver qué hace el compilador con tu código, por qué un programa es lento o cómo resolvió otro equipo un problema enseña más que muchos libros. Es, además, la forma de entender a fondo un formato o un protocolo que no tiene documentación.
Desensamblar no es descompilar
Con los binarios hay dos herramientas que se confunden. Un disassembler (desensamblador) traduce los bytes a instrucciones del procesador, una por una: «compara», «llama a esta función», «salta si es igual». Es exacto, porque cada grupo de bytes corresponde a una instrucción, pero leerlo cuesta. Un decompiler (descompilador) va un paso más allá: agrupa esas instrucciones en condiciones, bucles y funciones, y escribe un código parecido a C. Se lee mucho mejor, pero es una reconstrucción: inventa nombres como param_1 y a veces se equivoca. Así se ve un pedazo del programa de nuestro laboratorio, que verás completo más abajo, desensamblado con objdump:
objdump -d, la parte que decide 401072: call 401050 <strcmp@plt> 401077: test %eax,%eax 401079: je 401091 <strcmp@plt+0x41> 40107b: mov $0x402028,%edi 401080: call 401030 <puts@plt>
Se lee así: llama a strcmp, la función de C que compara dos textos; revisa si el resultado es cero, y si lo es («je», jump if equal) salta a la dirección 401091; si no, sigue y llama a puts para imprimir un mensaje. Un decompiler convertiría esas líneas en algo como un if sobre el resultado de strcmp. Si quieres ver la diferencia con un mismo programa en siete lenguajes, la explicamos en nuestra guía de qué es descompilar, con la salida real de Ghidra.
Las herramientas, nivel por nivel
- El aparato. Se abre, se identifican los chips y se buscan los puertos de prueba que dejan los fabricantes: UART, una consola de texto en serie que a veces muestra todo lo que el sistema hace al arrancar; JTAG y SWD, conexiones de depuración que permiten detener el procesador y leer su memoria. Un multímetro ayuda a encontrar cuál pin es tierra y cuál lleva datos.
- El firmware. Es el software grabado en el aparato. Se lee («se vuelca») con un programador conectado a esos puertos o directamente al chip de memoria, y luego se separan sus partes: el arranque, el sistema y las aplicaciones. Así empezó la reconstrucción del firmware del multímetro EEVblog 121GW: con un ST-LINK V2 conectado por SWD.
- Los binarios, sin ejecutarlos.
filedice qué tipo de archivo es y para qué procesador;stringssaca los textos legibles;objdumpdesensambla. Para un análisis serio están Ghidra, el disassembler y decompiler que la NSA liberó como código abierto en 2019; IDA Pro, el comercial de Hex-Rays, con una versión gratuita, IDA Free; radare2, de línea de órdenes, y Cutter, una interfaz gráfica basada en Rizin, un derivado de radare2. Para apps de Android, jadx convierte el código de una APK de vuelta a Java. - El comportamiento, ejecutándolo. Un debugger como
gdbdetiene el programa donde quieras y deja leer su memoria.ltracemuestra las llamadas a bibliotecas ystracelas llamadas al sistema: qué archivos abre, qué escribe, a dónde se conecta. Frida va más lejos: mete tu propio código dentro de un programa en ejecución para interceptar cualquier función, incluso en celulares. - La red. Wireshark captura el tráfico y lo descompone protocolo por protocolo. Es la versión moderna del café francés de Samba: ver qué se dicen un aparato y su servidor.
Laboratorio: le sacamos la clave a nuestro propio programa
Para ver estas herramientas en acción escribimos un programa en C que pide una clave para activar un «modo completo», como hacen muchos programas de prueba. La clave está escrita tal cual en el código, que es justo lo que hace mucha gente. Todo corrió en un contenedor desechable, sin red y con un usuario normal, y lo único que se analiza es código nuestro:
/* clave.c: un programa nuestro que pide una clave para activar el «modo completo».
La clave está escrita tal cual en el código, como la deja mucha gente. */
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Uso: %s CLAVE\n", argv[0]);
return 2;
}
if (strcmp(argv[1], "PASTEL-DE-ELOTE-1987") == 0) {
puts("Clave correcta: modo completo activado");
return 0;
}
puts("Clave incorrecta");
return 1;
}Lo compilamos con gcc 15.3 y optimización -O2, como cualquier programa que se publica, y le quitamos los símbolos con strip, para que no queden ni los nombres de las funciones. Ahora hagamos de cuenta que solo tenemos el archivo clave. Lo primero es preguntar qué es:
file$ file clave clave: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, stripped
Un ejecutable de Linux para procesadores x86 de 64 bits, que usa bibliotecas del sistema y sin símbolos («stripped»). Lo segundo es buscar textos legibles dentro del archivo. strings imprime toda secuencia de caracteres de al menos cierta longitud, aquí 8:
strings: salida recortada$ strings -n 8 clave /lib64/ld-linux-x86-64.so.2 … Uso: %s CLAVE PASTEL-DE-ELOTE-1987 Clave incorrecta Clave correcta: modo completo activado GCC: (GNU) 15.3.0 …
Ahí está, entre los mensajes del programa. Recortamos las líneas con nombres internos del formato del archivo, pero no hizo falta más: cualquier texto escrito tal cual en el código viaja tal cual en el binario. Aunque no lo supiéramos, ltrace lo confirma: ejecuta el programa y anota cada llamada a una biblioteca con sus argumentos.
ltrace$ ltrace ./clave hola
strcmp("hola", "PASTEL-DE-ELOTE-1987") = 24
puts("Clave incorrecta") = 17
Clave incorrecta
+++ exited (status 1) +++El programa comparó lo que escribimos («hola») con lo que esperaba, y ltrace nos enseñó las dos cosas. strace, que vigila las llamadas al sistema, sirve para otra pregunta: qué hace el programa con la máquina. Aquí solo abrió las bibliotecas que necesita para arrancar e imprimió su mensaje; no leyó ningún archivo propio ni intentó conectarse a la red. Con un programa sospechoso, esa misma orden es la que delata si manda datos a algún lado.
Y con objdump encontramos el salto que vimos arriba, el je de la dirección 401079, que decide entre «Clave correcta» y «Clave incorrecta». En el archivo ese salto ocupa dos bytes, 74 16. Si el 74 («salta si es igual») se cambia por EB («salta siempre»), el programa acepta cualquier cosa. Lo hicimos con un pequeño script de Python que cambia ese único byte en una copia:
un byte cambiado$ python3 parche.py clave clave_parchada 0x401079 dirección 0x401079 = posición 0x1079 del archivo: 74 -> eb $ ./clave_parchada cualquier-cosa Clave correcta: modo completo activado
Un detalle curioso: el programa parchado terminó con código de salida 19 en lugar de 0. Al optimizar, gcc no escribió un «devuelve 0» en el camino del éxito, sino que reutilizó el resultado de strcmp, porque sabía que ahí siempre vale cero. Con el salto forzado ya no es cero. Es un buen ejemplo de por qué la ingeniería inversa pide entender lo que hizo el compilador, no solo lo que escribió el programador.
La reacción obvia es esconder la clave. Guardamos cada letra combinada con el valor 0x5A mediante XOR, una operación que se deshace aplicándola otra vez, y el programa la arma en memoria justo antes de comparar. El primer intento falló de una forma inesperada: con -O2, gcc hizo la cuenta al compilar y volvió a dejar la clave escrita en el archivo; strings mostró «PASTEL-DE-ELOTE-» y el «1987» apareció como un número dentro de una instrucción. Para que el disfraz llegara al binario tuvimos que marcar la llave como volatile, que obliga al programa a leerla al ejecutarse:
/* clave_oculta.c: la misma clave, pero guardada «disfrazada»: cada letra pasó por XOR con 0x5A,
así que en el archivo no aparece escrita. El programa la arma en memoria justo antes de comparar. */
#include <stdio.h>
#include <string.h>
static const unsigned char disfrazada[] = {
0x0a, 0x1b, 0x09, 0x0e, 0x1f, 0x16, 0x77, 0x1e, 0x1f, 0x77,
0x1f, 0x16, 0x15, 0x0e, 0x1f, 0x77, 0x6b, 0x63, 0x62, 0x6d
};
/* volatile: obliga a leer la llave al ejecutar, para que el compilador no deshaga el disfraz */
static volatile unsigned char llave = 0x5A;
int main(int argc, char *argv[])
{
char clave[sizeof disfrazada + 1];
if (argc != 2) {
printf("Uso: %s CLAVE\n", argv[0]);
return 2;
}
for (size_t i = 0; i < sizeof disfrazada; i++)
clave[i] = disfrazada[i] ^ llave;
clave[sizeof disfrazada] = '\0';
if (strcmp(argv[1], clave) == 0) {
puts("Clave correcta: modo completo activado");
return 0;
}
puts("Clave incorrecta");
return 1;
}Esta vez strings ya no encuentra la clave (lo único que asoma es una «Z» pegada a la versión de gcc: es la llave 0x5A, que en ASCII es la letra Z). Pero el disfraz solo dura mientras el programa no corre, porque para comparar tiene que armar la clave real en algún lugar de la memoria:
ltrace, con la clave disfrazada$ ltrace ./clave_oculta hola
strcmp("hola", "PASTEL-DE-ELOTE-1987") = 24
puts("Clave incorrecta") = 17
Clave incorrecta
+++ exited (status 1) +++Exactamente la misma línea. Y si el programa no usara strcmp de la biblioteca, sino una comparación propia que ltrace no ve, queda el debugger. Le pedimos a gdb que se detuviera en la dirección de strcmp que nos dio objdump y que imprimiera los dos textos que recibe:
gdb: salida recortada$ gdb -q -batch -x gdb.txt ./clave_oculta Breakpoint 1 at 0x401050 … Breakpoint 1, 0x0000000000401050 in strcmp@plt () 1.er argumento (lo que escribiste): hola 2.o argumento (lo que espera): PASTEL-DE-ELOTE-1987
La lección no es que haya que disfrazar mejor. Hay ofuscadores profesionales mucho más complicados que un XOR, y también se estudian; solo hacen el trabajo más largo. La lección es que todo lo que un programa comprueba en la máquina del usuario, el usuario lo puede ver y cambiar. Por eso las comprobaciones que importan se hacen en un servidor, o con firmas criptográficas cuya clave privada nunca sale de las manos del fabricante. Y es también por eso que desarmar un programa es tan útil para la seguridad: los defensores encuentran así las mismas claves olvidadas que buscaría un atacante.
¿Es legal? Lo que dicen las leyes de Estados Unidos, Europa y México
Depende del país, de lo que se estudia y para qué, y de lo que se aceptó al comprar o instalar. Este resumen se basa en el texto de cada ley, pero no es asesoría legal: para un caso concreto hay que consultar a un abogado.
Estados Unidos. El precedente clásico es Sega contra Accolade (1992). Accolade desensambló juegos de Sega para averiguar cómo hacer que los suyos funcionaran en sus consolas, y el tribunal de apelaciones del noveno circuito resolvió que desensamblar un programa por un motivo legítimo, cuando no hay otra forma de llegar a sus partes no protegidas, es uso justo (fair use). Después llegó la ley DMCA, cuya sección 1201 prohíbe saltarse un «candado digital» que controle el acceso a una obra, aunque el fin sea legal. Su apartado 1201(f) hace una excepción: quien usa legalmente un programa puede saltar esa protección «con el único propósito» de identificar y analizar lo necesario para lograr la interoperabilidad de un programa creado de forma independiente, que la ley define como la capacidad de los programas de intercambiar información y usarla. Además, cada tres años la Biblioteca del Congreso aprueba exenciones; las vigentes incluyen la reparación de aparatos de consumo, la investigación de seguridad de buena fe y los juegos cuyo servidor de autenticación se apagó, y ya está en marcha la revisión del ciclo 2027. El punto débil son los contratos: según el artículo de Cornell, en Estados Unidos las licencias que prohíben la ingeniería inversa suelen ser válidas.
Unión Europea. La directiva sobre programas de computadora, la 2009/24/CE, permite a quien usa legalmente un programa observarlo, estudiarlo y probarlo para entender sus ideas y principios (artículo 5.3). Su artículo 6 autoriza a descompilar sin permiso del titular cuando sea indispensable para lograr la interoperabilidad con un programa creado de forma independiente, con tres condiciones: que lo haga alguien con derecho a usar el programa, que esa información no esté ya disponible de forma fácil y rápida, y que se limite a las partes necesarias. Lo obtenido no puede usarse para hacer un programa sustancialmente similar. Y el artículo 8 dice algo que no tiene Estados Unidos: una cláusula de contrato contraria a esas excepciones es nula. Fuera del software, la directiva de secretos comerciales, la 2016/943, considera lícito obtener información mediante «la observación, el estudio, el desmontaje o el ensayo» de un producto obtenido legalmente, siempre que no haya una obligación válida de no hacerlo.
México. La reforma a la Ley Federal del Derecho de Autor publicada el 1 de julio de 2020 añadió un capítulo sobre medidas tecnológicas de protección, el nombre legal de los candados digitales. Saltarse una que controle el acceso a una obra se castiga con una multa de mil a diez mil veces la Unidad de Medida y Actualización (artículo 232 Ter), pero el artículo 114 Quáter enumera excepciones. La primera son «los procesos de ingeniería inversa no infractores realizados de buena fe» sobre una copia obtenida legalmente de un programa, «con el único propósito de lograr la interoperabilidad» con otro programa creado de forma independiente. Otras cubren probar, investigar o corregir la seguridad de una computadora, sistema o red con la autorización de su propietario, y analizar fallas en tecnologías de cifrado; además, el instituto del derecho de autor puede aprobar nuevas excepciones con evidencia. Y la Ley Federal de Protección a la Propiedad Industrial, del mismo día, dice en su artículo 164 que no es apropiación indebida de un secreto industrial «la observación, el estudio, el desmontaje o el ensayo» de un producto que esté a disposición del público o en posesión legal de quien lo estudia, si no tiene una obligación de confidencialidad.
Las tres leyes se parecen en lo esencial: protegen estudiar un producto que obtuviste legalmente, sobre todo para que otros programas puedan conectarse con él o para revisar su seguridad, y no protegen copiar su código ni hacer uno igual. Falta una pieza que no es ley pero pesa: los términos de uso. Microsoft, por ejemplo, prohíbe en sus términos la ingeniería inversa de todos sus servicios en línea. Un servicio en la nube corre en servidores ajenos, y estudiarlo a fondo casi siempre exige tocar esos servidores, algo que ninguna de estas excepciones ampara. Lo prudente es practicar con programas propios, con software libre o con retos hechos para eso.
Lo que cambió con la IA, y lo que está en juego
Durante décadas, la ingeniería inversa fue lenta y cara: leer miles de instrucciones, adivinar qué hace cada función y documentarlo a mano. Eso cambió en 2026 con los agentes de programación. REA, una herramienta de código abierto que conecta agentes como Claude Code, Codex o Cursor con Ghidra, IDA o Hopper por medio de MCP, pasó de las 100,000 estrellas en GitHub en seis meses; repasamos qué programas se han analizado con ella. En noviembre, Dave Jones, de EEVblog, le pidió a Claude Code en un solo prompt que reconstruyera en C el firmware de su multímetro: en menos de 24 horas, el agente volcó la memoria con un ST-LINK, la analizó con Ghidra, reescribió 691 funciones, armó un emulador que las comparó con las originales (591 de 591 pruebas pasaron) y el resultado ya funciona en el aparato.
Los juristas de Cornell que publicaron «Cheap Reverse Engineering and What It Will Do» sostienen que eso rompe el equilibrio de la ley, que contaba con que desarmar un producto fuera difícil. Entre sus casos hay uno que se parece mucho a nuestro laboratorio: en una laptop HP, Claude cambió un solo byte de la verificación de firma del BIOS para que siempre la diera por buena. Otro: un ingeniero tardó cerca de un día en encontrar con Ghidra la rutina que valida las opciones de pago de un generador de señales, y Codex le hizo un generador de claves en una hora. Para los defensores es una buena noticia, porque auditar firmware, revisar malware o rescatar un aparato abandonado cuesta mucho menos. Para los fabricantes que confían en que nadie mirará dentro, ya no es una apuesta segura. Y para todos, la pregunta legal de los próximos años es cuánto de lo que hoy es fácil seguirá siendo permitido.
Para empezar
- Con tus propios programas. Escribe uno pequeño en C, compílalo y repite lo que hicimos arriba:
file,strings,ltrace,objdumpygdbvienen en cualquier distribución de Linux. - Sin instalar nada. Nuestro visor hexadecimal y analizador de binarios abre en el navegador archivos ELF, PE y Mach-O y muestra sus cadenas y su entropía.
- Con Ghidra. La guía práctica de Ghidra va de la instalación a descompilar tu primer programa, y la de qué es descompilar compara lo que se recupera en siete lenguajes.
- Con apps de Android. jadx-headless-mcp explica qué archivo descargar y cómo usar jadx con un agente de IA.
- Con un agente de IA. La guía de REA muestra cómo poner a tu agente a analizar un programa, de la instalación al primer resultado.
La ingeniería inversa es tan vieja como la curiosidad de desarmar un reloj, y en software es la forma de entender lo que nadie documentó: el malware que llega en una caja de TV, el formato de un archivo, el aparato que su fabricante abandonó. Nuestro laboratorio lo resume: una clave guardada en un programa duró lo que tardamos en ejecutar strings, y disfrazada, lo que tardamos en ejecutar ltrace. Las leyes de Estados Unidos, la Unión Europea y México permiten estudiar lo que es tuyo, sobre todo para conectar programas y revisar su seguridad, y ninguna permite copiar. Con la IA, desarmar ya no cuesta meses, y eso obliga a los fabricantes a diseñar sin secretos en el aparato y a los legisladores a decidir dónde queda el límite.
Fuente: 17 U.S.C. § 1201 (DMCA); Oficina de Copyright de EE. UU., Sega contra Accolade (1992); 37 CFR 201.40, exenciones de la Biblioteca del Congreso; Directiva 2009/24/CE; Directiva (UE) 2016/943; Ley Federal del Derecho de Autor; Ley Federal de Protección a la Propiedad Industrial; Andrew Tridgell, «How Samba was written». Pruebas propias con gcc 15.3, ltrace, strace y gdb.
