Packers y unpacking:
por qué un binario ‘pesa poco y hace mucho’
Malware y Reversing | unfantasmaenelsistema.com
Abres una muestra en el desensamblador y te encuentras un puñado de instrucciones, casi ningún import y una sección de código con aspecto de ruido. No es que el malware sea simple: es que lo que estás viendo es un stub de descompresión, y el programa de verdad está comprimido o cifrado dentro del fichero, esperando a que ese stub lo reconstruya en memoria cuando el proceso arranca. Eso es un packer.
Entender el empaquetado es un paso obligado antes de reversar en serio: sin desempaquetar, el análisis estático no llega a ninguna parte y las firmas basadas en contenido fallan. Este artículo explica, a alto nivel, qué hace un packer, cómo se reconoce un binario empaquetado, cómo se deshace un empaquetado estándar como UPX y cuál es la idea del desempaquetado dinámico, sin entrar en técnicas de evasión. Todos los ejemplos son de lectura y comprobación sobre muestras en un laboratorio aislado.
Qué aprenderás en este artículo
Qué hace exactamente un packer: compresión o cifrado más un stub que restaura en memoria · La diferencia entre packers legítimos, protectores comerciales y crypters a medida · Las señales que delatan un binario empaquetado: entropía, imports, secciones y entry point · Desempaquetado estático de UPX con upx -d y por qué solo funciona con el formato original · La idea del desempaquetado dinámico: dejar que se descomprima y volcar desde memoria · Qué es el anti-unpacking y cuándo conviene parar y pasar a una sandbox
1. Qué hace un packer
Un packer toma un ejecutable ya compilado y produce otro ejecutable funcionalmente equivalente pero con el código y los datos originales transformados: comprimidos, cifrados o ambas cosas. Ese contenido transformado se guarda dentro del nuevo fichero, normalmente en una o varias secciones nuevas. El entry point del binario ya no apunta al programa original, sino a un pequeño trozo de código añadido —el stub o unpacking stub— cuya tarea es revertir la transformación.
Cuando el sistema lanza el binario empaquetado, se ejecuta primero el stub: reserva memoria, descomprime o descifra el código original en ella, reconstruye la tabla de importaciones (que el cargador de Windows no ha podido resolver porque estaba empaquetada), aplica las relocations que hagan falta y, por último, salta al Original Entry Point (OEP), la primera instrucción del programa real. A partir de ahí el proceso se comporta como si nunca hubiera estado empaquetado. Ese salto al OEP es el momento clave para quien analiza: si consigues capturar la memoria del proceso justo después, tienes el binario desempaquetado.
El empaquetado nació por motivos legítimos: reducir el tamaño de distribución cuando el ancho de banda era caro, y dificultar la copia trivial de recursos o cadenas. Hoy sus dos usos dominantes son proteger propiedad intelectual (licencias, antitrampas de videojuegos) y, en el lado ofensivo, romper las firmas estáticas de los antivirus: el mismo malware reempaquetado con una clave distinta produce un fichero con hash nuevo y contenido irreconocible.
2. Familias: de UPX a los crypters a medida
No todos los empaquetados cuestan lo mismo de deshacer. Conviene situar la muestra en una de estas categorías antes de invertir tiempo:
| Categoría | Ejemplos | Objetivo | Dificultad de desempaquetado |
|---|---|---|---|
| Packer de compresión abierto | UPX, UPack, FSG | Reducir tamaño; formato documentado y reversible | Baja: a menudo hay un desempaquetador oficial |
| Instalador autoextraíble | NSIS, Inno Setup, 7-Zip SFX | Distribuir software; no oculta, empaqueta | Baja: herramientas dedicadas extraen el contenido |
| Protector comercial | Themida, VMProtect, Enigma, ASProtect | Antipiratería; virtualización de código y anti-debug fuerte | Alta: pensado para resistir el análisis |
| Crypter / packer a medida | Código propio de un actor o de un builder de mercado negro | Evadir AV/EDR; suele añadir anti-análisis | Variable: de trivial a muy costoso |
| .NET packer / protector | ConfuserEx, .NET Reactor, packers de IL | Ofuscar ensamblados gestionados | Media: existen des-ofuscadores específicos (de4dot, etc.) |
UPX (Ultimate Packer for eXecutables) es el caso de referencia porque es libre, muy usado y simétrico: el mismo binario `upx` que comprime sabe descomprimir, siempre que la cabecera UPX del fichero esté intacta. Muchos actores lo usan tal cual por comodidad; otros lo aplican y luego manipulan la cabecera o los nombres de sección para que `upx -d` falle, aunque el algoritmo siga siendo el de UPX. Distinguir «UPX de verdad» de «UPX modificado» es una de las primeras preguntas del triaje.
3. Cómo se reconoce un binario empaquetado
Ninguna señal aislada es concluyente, pero la combinación de varias lo es. Estas son las que se miran, todas sobre el fichero en disco y sin ejecutarlo:
- Entropía alta y homogénea: una o varias secciones con entropía de Shannon por encima de ~7,2 (sobre 8) indican contenido comprimido o cifrado. Si la sección de código está en 7,9, ahí no hay instrucciones legibles.
- Tabla de importaciones mínima: dos o tres DLL y un puñado de funciones, típicamente LoadLibrary, GetProcAddress, VirtualAlloc y VirtualProtect. El programa real resolverá sus imports en tiempo de ejecución; la tabla estática está vacía a propósito.
- Nombres de sección atípicos: `UPX0`, `UPX1`, `.themida`, `.vmp0`, `.enigma1`, secciones sin nombre o con bytes no imprimibles, o nombres duplicados.
- Descuadre de tamaños: una sección con VirtualSize grande y SizeOfRawData cero o muy pequeño (el stub la llenará al arrancar); o `SizeOfImage` muy por encima del tamaño del fichero.
- Entry point fuera de lo normal: la primera instrucción cae en la última sección, o en una sección de datos, en lugar de en `.text`.
- Pocas cadenas legibles: apenas texto ASCII o UTF-16 útil; ni rutas, ni claves de registro, ni URLs, porque están dentro del blob comprimido.
- Identificación por firma: herramientas como Detect It Easy (DiE), PEiD (histórico) o el módulo de detección de packers de PEStudio comparan patrones de bytes del entry point con una base de firmas conocidas.
La entropía alta no es una acusación
Instaladores legítimos (NSIS, Inno Setup), muchos binarios .NET, ejecutables con grandes recursos multimedia y binarios firmados con secciones comprimidas dan entropías altas sin ser maliciosos. La entropía sirve para priorizar y para sospechar de empaquetado, no para emitir un veredicto: se combina siempre con la tabla de imports, los nombres de sección y el entry point.
# Comprobacion rapida de empaquetado. Solo lee el fichero, no lo ejecuta.
import pefile, math
def entropia(datos):
if not datos:
return 0.0
frec = [0] * 256
for b in datos:
frec[b] += 1
e = 0.0
for c in frec:
if c:
p = c / len(datos)
e -= p * math.log2(p)
return e
pe = pefile.PE('muestra.bin')
for s in pe.sections:
nombre = s.Name.rstrip(b'\x00').decode(errors='replace')
print(f'{nombre:9s} vsize={s.Misc_VirtualSize:#8x} raw={s.SizeOfRawData:#8x} entropia={entropia(s.get_data()):.2f}')
n_imports = sum(len(d.imports) for d in getattr(pe, 'DIRECTORY_ENTRY_IMPORT', []))
print('funciones importadas:', n_imports)4. Desempaquetado estático: el caso UPX
El desempaquetado estático consiste en revertir la transformación sin ejecutar el binario, aplicando el algoritmo inverso al blob comprimido. Solo es viable cuando el formato del packer está documentado o es simétrico. UPX es el ejemplo canónico: el propio proyecto distribuye la opción de descompresión.
# 1. Confirmar que el fichero esta empaquetado con UPX y que la cabecera esta intacta upx -t muestra.exe # 'testing' -> [OK] si el formato UPX es valido upx -l muestra.exe # 'list' -> muestra ratio y tamanos comprimido/original # 2. Descomprimir a un fichero NUEVO, sin tocar el original upx -d muestra.exe -o muestra_unpacked.exe # 3. Verificar el resultado: ahora deberia tener imports completos y menos entropia upx -t muestra_unpacked.exe # ya NO deberia reconocerlo como UPX
Si `upx -d` responde con un error del tipo «not packed by UPX» o «header corrupted» pese a que DiE identifica el binario como UPX, casi siempre es porque el actor ha alterado la cabecera UPX o los nombres de sección después de empaquetar. El algoritmo sigue siendo UPX, pero la herramienta oficial se niega. Opciones a alto nivel: reconstruir a mano los campos de la cabecera UPX comparándolos con los de un binario UPX sano de la misma versión, o —más habitual— pasar directamente al desempaquetado dinámico, que no depende de esos metadatos.
| Situación | Vía recomendada |
|---|---|
| UPX estándar, upx -t da [OK] | upx -d: segundos, sin ejecutar nada |
| UPX con cabecera manipulada | Dinámico, o reparación manual de la cabecera si el volumen lo justifica |
| Instalador SFX (NSIS, Inno, 7z) | Extractor dedicado (7-Zip, innoextract) o volcado del directorio temporal |
| Protector comercial (Themida, VMProtect) | Dinámico con herramientas específicas; a veces solo análisis en sandbox |
| Packer a medida desconocido | Dinámico: identificar el salto al OEP y volcar memoria |
5. La idea del desempaquetado dinámico
Cuando no hay desempaquetador conocido, la estrategia es dejar que el propio stub haga el trabajo y capturar el resultado. El binario se ejecuta de forma controlada —en una máquina virtual aislada, sin red, con un depurador o un motor de instrumentación— y se le permite descomprimir el programa original en memoria. El objetivo es identificar el instante en que el stub termina y salta al OEP, y en ese punto volcar (dump) la imagen del proceso desde memoria a un fichero.
A alto nivel, las técnicas para localizar ese instante son de dos tipos. Las manuales ponen un punto de interrupción en el momento en que el flujo pasa de la sección del stub a la sección donde aparece el código descomprimido (un cambio de sección con permisos de ejecución, o una escritura seguida de un salto a esa misma zona). Las automáticas usan herramientas genéricas de unpacking que monitorizan la ejecución y detectan el patrón «escribir memoria ejecutable y luego saltar a ella», hacen el volcado y reconstruyen la tabla de importaciones.
| Pieza del proceso | Qué resuelve | Herramientas habituales (análisis) |
|---|---|---|
| Ejecución controlada | Dejar que el stub descomprima sin dañar el host | VM aislada sin red, snapshots |
| Detección del OEP | Saber cuándo el código real ya está en memoria | Depurador (x64dbg) con breakpoints de sección; unpackers automáticos |
| Volcado de memoria | Extraer la imagen ya descomprimida | Plugins de dump del depurador, volcado de secciones del proceso |
| Reconstrucción de imports (IAT) | Que el binario volcado vuelva a enlazar con las DLL | Scylla, ImpREC (histórico) |
| Arreglo del PE | Cabeceras y alineaciones coherentes para que cargue | Editores de PE, LIEF |
El resultado —un dump con la IAT reconstruida— no siempre vuelve a ejecutarse, pero eso da igual: lo que se busca es un fichero que el desensamblador pueda leer entero, con sus cadenas, sus llamadas a API y su lógica a la vista. A partir de ahí el análisis continúa como con cualquier binario no empaquetado.
Ejecutar para desempaquetar es ejecutar malware
El desempaquetado dinámico implica correr la muestra. Hazlo solo en una máquina virtual dedicada, con instantánea previa, sin acceso a la red o con red simulada, sin carpetas compartidas con el host y sin credenciales reales dentro. Restaura la instantánea al terminar.
6. Anti-unpacking y cuándo parar
Los protectores serios y muchos crypters a medida incorporan anti-unpacking: mecanismos para que el binario se comporte de forma distinta —o no se desempaquete— cuando detecta que lo están analizando. A grandes rasgos:
- Anti-debug: comprobaciones de si hay un depurador presente (flags del proceso, tiempos de ejecución anómalos, trampas con excepciones) que abortan o desvían la descompresión.
- Anti-VM y anti-sandbox: el stub busca artefactos de virtualización o de entornos de análisis y, si los encuentra, no despliega el payload real.
- Descompresión por partes (stolen bytes, code stripping): el código original nunca está entero en memoria a la vez; se reconstruye a trozos y se vuelve a borrar, de modo que un único volcado no basta.
- Virtualización de código: en lugar de descomprimir instrucciones x86, el protector las traduce a un bytecode propio que ejecuta una máquina virtual embebida. No hay un OEP clásico al que saltar; «desempaquetar» aquí significa escribir un desensamblador para esa VM, un trabajo de semanas.
Por eso una decisión importante del triaje es cuándo dejar de desempaquetar. Si el objetivo es clasificar la muestra, extraer indicadores de compromiso (IOC) y entender su comportamiento, casi siempre sale más a cuenta ejecutarla en una sandbox —automatizada, con captura de llamadas al sistema, tráfico de red y ficheros creados— que invertir días en vencer un VMProtect. El desempaquetado completo se reserva para cuando hace falta el código exacto: atribución, firma específica, o comprender un algoritmo concreto (cifrado de un ransomware, protocolo de un C2).
Orden de trabajo razonable ante una muestra empaquetada
Triaje estático: hash, tipo, identificación de packer con DiE, entropía por sección, imports y cadenas · Si es UPX estándar: upx -d y seguir con el binario limpio · Si no: sandbox primero para obtener comportamiento e IOC rápidos · Solo si se necesita el código real: desempaquetado dinámico en VM aislada, volcado y reconstrucción de la IAT · Si hay virtualización de código: valorar si el coste compensa o basta con el análisis dinámico
7. Herramientas de referencia
| Herramienta | Tipo | Para qué |
|---|---|---|
| Detect It Easy (DiE) | GUI/CLI | Identificar compilador, linker y packer; gráfica de entropía por zonas |
| UPX | CLI | Comprimir y descomprimir el formato UPX (upx -t, -l, -d) |
| PEStudio | GUI, Windows | Marca indicadores de empaquetado y cruza imports con listas de APIs de riesgo |
| x64dbg | Depurador, Windows | Desempaquetado dinámico manual: breakpoints de sección, seguimiento hasta el OEP |
| Scylla | GUI, Windows | Reconstrucción de la tabla de importaciones tras el volcado |
| pefile / LIEF | Librerías | Automatizar el triaje estático y reparar cabeceras del PE volcado |
| Sandbox (CAPE, Cuckoo, comercial) | Entorno automatizado | Comportamiento e IOC sin desempaquetar a mano; muchas hacen unpacking automático |
Marco legal y de seguridad
Analiza solo binarios propios, retos públicos (crackmes, CTF) o muestras de malware dentro de un laboratorio aislado y sin salida a la red; nunca en tu equipo habitual · El desempaquetado dinámico ejecuta la muestra: máquina virtual dedicada, instantánea previa y posterior, sin carpetas compartidas ni credenciales reales · La ingeniería inversa de software de terceros está acotada en la UE a fines concretos (interoperabilidad, corrección de errores, seguridad) y con matices según el país y la licencia del producto; eludir la protección técnica de un programa para piratearlo o distribuir cracks es ilícito · En España, usar estas técnicas contra sistemas ajenos encaja en los artículos 197 bis, 197 ter y 264 del Código Penal, además de la normativa de propiedad intelectual · Si al analizar una muestra encuentras datos de una víctima real o indicios de un incidente en curso, documenta y deriva a INCIBE-CERT o a las fuerzas y cuerpos de seguridad; no lo difundas por tu cuenta
Referencias
«Practical Malware Analysis» (Sikorski y Honig, No Starch Press) — capítulo dedicado a packers y unpacking · Documentación oficial de UPX (upx.github.io) — formato y opciones de línea de comandos · Detect It Easy (github.com/horsicq/DIE-engine) — firmas de packers y protectores · OALabs y el canal de MalwareAnalysisForHedgehogs — vídeos de desempaquetado manual con x64dbg y Scylla · Documentación de CAPE Sandbox — extracción de configuración y unpacking automático
Conclusión
Un binario empaquetado no es más complejo: es el mismo programa envuelto en una capa que hay que retirar antes de poder analizarlo. Esa capa combina compresión o cifrado con un stub que reconstruye el código original en memoria y salta al OEP, y su huella —entropía alta, imports mínimos, secciones raras, entry point desplazado— se detecta en el triaje estático sin ejecutar nada.
Deshacer el empaquetado va de menos a más esfuerzo: upx -d resuelve el caso estándar en segundos; cuando no hay desempaquetador conocido, se deja que el stub trabaje en una VM aislada y se vuelca la memoria en el salto al OEP, reconstruyendo después la tabla de importaciones. Y ante protectores con virtualización de código, lo sensato suele ser una sandbox que dé comportamiento e indicadores sin pelear semanas contra la máquina virtual del protector.
unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)

