IntermedioWindowsPEformato binarioanálisis estáticomalware

Anatomía de un PE: Entendiendo los Ejecutables de Windows para Análisis de Malware

Guía técnica completa del formato PE (Portable Executable) de Windows desde la perspectiva del análisis de malware. Headers, secciones, imports, exports, relocations, resources y cómo el malware manipula cada componente.

MalwareIntel Research··13 min lectura
Serie: Malware en Windows — Parte 1

El formato que todo analista de malware debe dominar

Todo análisis de malware en Windows comienza con la misma pregunta: ¿qué es este archivo? Y la respuesta está en el formato PE (Portable Executable). El PE es la estructura que Windows usa para ejecutables (.exe), bibliotecas (.dll), drivers (.sys) y otros binarios. Entender este formato es el requisito previo para cualquier forma de análisis de malware, estático o dinámico.

Este artículo descompone el formato PE desde la perspectiva del analista de malware: no como una referencia de documentación (para eso está la especificación de Microsoft), sino como una guía de qué buscar, qué es normal, qué es sospechoso y qué revela el comportamiento probable del malware.

Estructura general del PE

Un archivo PE tiene la siguiente estructura, de principio a fin:

Offset 0x0000  ┌─────────────────────────┐
               │     DOS Header          │  64 bytes, incluye magic "MZ"
               ├─────────────────────────┤
               │     DOS Stub            │  Programa DOS legacy ("This program...")
               ├─────────────────────────┤
               │     PE Signature        │  4 bytes: "PE\0\0"
               ├─────────────────────────┤
               │     COFF File Header    │  20 bytes: machine, sections, timestamp
               ├─────────────────────────┤
               │     Optional Header     │  Variable: entry point, image base, subsystem
               │     (PE32 o PE32+)      │  Data Directories (imports, exports, resources...)
               ├─────────────────────────┤
               │     Section Headers     │  Array de secciones (.text, .data, .rsrc, etc.)
               ├─────────────────────────┤
               │     Section Data        │  Contenido real de cada sección
               │     .text (código)      │
               │     .data (datos)       │
               │     .rdata (read-only)  │
               │     .rsrc (recursos)    │
               │     .reloc (relocations)│
               └─────────────────────────┘

DOS Header y DOS Stub

DOS Header (IMAGE_DOS_HEADER)

Los primeros 64 bytes del archivo. Los campos relevantes para análisis de malware:

CampoOffsetTamañoSignificado para el analista
e_magic0x002 bytesMagic number: 0x5A4D ("MZ"). Si no empieza con MZ, no es un PE válido
e_lfanew0x3C4 bytesOffset al PE signature. Valor típico: 0x80-0x100. Valores muy grandes pueden indicar contenido oculto entre el DOS stub y el PE header

DOS Stub

Programa DOS legacy que imprime "This program cannot be run in DOS mode." cuando se ejecuta en DOS. En la práctica, este espacio se ignora en Windows moderno.

Indicador de malware: algunos malware almacenan datos cifrados o shellcode en el espacio entre el DOS stub y el PE header (la zona entre e_lfanew y el DOS stub). Si e_lfanew apunta muy lejos (0x1000+), puede haber contenido oculto.

PE Signature y COFF File Header

PE Signature

4 bytes en el offset indicado por e_lfanew: "PE\0\0" (0x50450000). Confirma que es un PE válido.

COFF File Header (IMAGE_FILE_HEADER)

20 bytes inmediatamente después de la PE signature:

CampoTamañoSignificado
Machine2 bytesArquitectura: 0x14C (x86), 0x8664 (x64), 0xAA64 (ARM64)
NumberOfSections2 bytesCuántas secciones tiene el PE
TimeDateStamp4 bytesTimestamp de compilación (Unix epoch)
PointerToSymbolTable4 bytesNormalmente 0 en PEs modernos
NumberOfSymbols4 bytesNormalmente 0
SizeOfOptionalHeader2 bytesTamaño del Optional Header
Characteristics2 bytesFlags: IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_DLL, etc.

TimeDateStamp: qué revela

El timestamp de compilación es uno de los primeros indicadores que examina un analista:

ValorInterpretación
Fecha reciente y razonableCompilación legítima o malware reciente
Fecha futuraTimestamp manipulado (el atacante lo falsificó)
Fecha muy antigua (ej. 1970, 1990)Timestamp eliminado/corrupto o herramienta de compilación vieja
0x00000000Timestamp borrado deliberadamente
Delphi epoch (1992-06-19)Compilado con Delphi/Borland (timestamp base diferente)

Muchas familias de malware falsifican el timestamp para dificultar la atribución temporal. Otros lo dejan en 0 o usan fechas futuras. Un timestamp legítimo que coincide con la fecha de aparición de la muestra añade confianza al análisis.

Optional Header

A pesar de su nombre, el Optional Header es obligatorio en ejecutables PE. Contiene información crítica para la carga del binario:

Campos clave para análisis de malware

CampoSignificadoIndicadores sospechosos
Magic0x10B (PE32, 32-bit) o 0x20B (PE32+, 64-bit)
AddressOfEntryPointRVA donde comienza la ejecuciónEntry point en una sección inusual (no .text) indica packing
ImageBaseDirección de carga preferida0x00400000 típico para .exe, 0x10000000 para .dll
SectionAlignmentAlineación de secciones en memoriaTípicamente 0x1000 (4 KB)
FileAlignmentAlineación de secciones en discoTípicamente 0x200 (512 bytes)
SizeOfImageTamaño total en memoriaValor inusualmente grande puede indicar secciones ocultas
SubsystemTipo de aplicación2 = GUI, 3 = Console. Malware a veces usa GUI sin ventana
DllCharacteristicsFlags de seguridadASLR (0x40), DEP (0x100), CFG (0x4000). Ausencia indica compilación antigua o deliberada
CheckSumChecksum del archivoDrivers (.sys) requieren checksum válido. Ejecutables normales lo ignoran

AddressOfEntryPoint y detección de packers

El entry point es donde Windows transfiere el control de ejecución al programa. En un PE legítimo, el entry point está al inicio de la sección .text. En un PE empaquetado:

  • Entry point en la última sección (donde está el unpacking stub)
  • Entry point en una sección con nombre inusual (.UPX0, .aspack, .themida)
  • Entry point fuera de cualquier sección definida

Data Directories

El Optional Header termina con un array de Data Directories: punteros a estructuras especiales del PE:

IndexNombreUso en análisis de malware
0Export TableFunciones que el PE exporta (DLLs principalmente)
1Import TableFunciones importadas: la fuente de info más valiosa
2Resource TableRecursos embebidos (iconos, strings, datos arbitrarios)
4Certificate TableFirma digital (Authenticode)
5Base Relocation TableDatos de reubicación para ASLR
6Debug DirectoryInformación de depuración (PDB path)
11Bound ImportImports pre-resueltas (optimización legacy)
12IAT (Import Address Table)Tabla de direcciones de funciones importadas
14CLR Runtime HeaderHeader .NET (indica que es un assembly .NET)

Import Table: la mina de oro del análisis estático

Por qué las imports son tan importantes

Cada función que un PE llama de una DLL externa aparece (normalmente) en la Import Table. Las imports revelan qué capacidades tiene el malware sin necesidad de ejecutarlo.

APIs de Windows que indican comportamiento malicioso

APIDLLComportamiento indicado
CreateRemoteThreadkernel32.dllInyección de código en otro proceso
VirtualAllocExkernel32.dllAsignación de memoria en otro proceso (inyección)
WriteProcessMemorykernel32.dllEscritura en memoria de otro proceso (inyección)
NtUnmapViewOfSectionntdll.dllProcess hollowing
SetWindowsHookExuser32.dllHooking de teclado/mouse (keylogger)
InternetOpenUrlwininet.dllDescarga de archivos de Internet
URLDownloadToFileurlmon.dllDescarga directa a disco
CryptEncryptadvapi32.dllCifrado (posible ransomware)
RegSetValueExadvapi32.dllEscritura en registro (persistencia)
CreateServiceadvapi32.dllCreación de servicio Windows (persistencia, escalada)
AdjustTokenPrivilegesadvapi32.dllManipulación de privilegios (escalada)
IsDebuggerPresentkernel32.dllAnti-debugging
GetTickCountkernel32.dllTiming checks (anti-sandbox)

Imports resueltas dinámicamente

El malware sofisticado evita poner APIs sospechosas en la Import Table. En vez de eso, resuelve las funciones en runtime:

// En vez de importar directamente:
// #include <windows.h>
// CreateRemoteThread(...)

// El malware resuelve la funcion en runtime:
typedef HANDLE (WINAPI *pCreateRemoteThread)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);

HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
// O mas sigiloso: usando hash en vez de string
pCreateRemoteThread myCreateRemoteThread = (pCreateRemoteThread)GetProcAddress(hKernel32, "CreateRemoteThread");
myCreateRemoteThread(hProcess, NULL, 0, pRemoteCode, NULL, 0, NULL);

Esto se llama dynamic API resolution. El resultado: la Import Table solo muestra GetModuleHandle y GetProcAddress (o LoadLibrary + GetProcAddress), ocultando las APIs realmente usadas.

Detección: herramientas como CAPA (Mandiant) y FLOSS detectan dynamic API resolution buscando patrones de hash de API names y llamadas a GetProcAddress.

Import hashing

Muchas familias de malware usan hashes en vez de strings para resolver APIs:

// Ejemplo: hash de "CreateRemoteThread" = 0x72A68B3C
FARPROC resolve_api(DWORD hash) {
    // Recorrer PEB -> PEB_LDR_DATA -> InLoadOrderModuleList
    // Para cada modulo, recorrer Export Table
    // Calcular hash de cada nombre de funcion
    // Si coincide con el hash buscado, devolver la direccion
}

Esto elimina completamente los strings de nombres de API del binario. Las herramientas de análisis estático necesitan tablas de hashes conocidos para resolver estas referencias.

Secciones del PE

Secciones estándar

SecciónContenidoFlags típicos
.textCódigo ejecutableIMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ
.dataVariables globales inicializadasIMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE
.rdataDatos de solo lectura (strings, imports, exports)IMAGE_SCN_MEM_READ
.bssVariables globales no inicializadasIMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE
.rsrcRecursos (iconos, diálogos, manifests, datos arbitrarios)IMAGE_SCN_MEM_READ
.relocTabla de relocaciones (ASLR)IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_DISCARDABLE
.edataExport tableIMAGE_SCN_MEM_READ
.idataImport tableIMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE

Indicadores de malware en secciones

IndicadorSignificado
Sección .text con flag WRITECódigo auto-modificable (self-modifying code, unpacking)
Nombres de sección inusuales (.UPX0, .aspack, .vmp0)Packer conocido
Entropía de sección mayor a 7.0 (de 8.0 max)Datos cifrados o comprimidos (packing)
Sección con tamaño en disco = 0 pero tamaño en memoria mayor a 0Sección que se llena en runtime (unpacking)
Muy pocas secciones (1-2)Posible packing agresivo
Muchas secciones (más de 10)Posible ofuscación o malware complejo
Entry point en sección no-.textPacking o inyección

Entropía como indicador

La entropía mide la aleatoriedad de los datos. En el contexto de PE analysis:

EntropíaSignificado
0 - 3.0Datos con patrones repetitivos (strings ASCII, código simple)
3.0 - 6.0Código compilado normal, datos mixtos
6.0 - 7.0Posible compresión o datos binarios
7.0 - 7.9Altamente comprimido o cifrado (fuerte indicador de packing)
7.9 - 8.0Datos aleatorios o cifrado fuerte

Un PE legítimo tiene la sección .text con entropía ~6.0-6.5. Un PE empaquetado tiene secciones con entropía mayor a 7.0.

Resources: datos embebidos

La sección .rsrc (resources) contiene datos embebidos en el PE. En binarios legítimos: iconos, diálogos, manifests, version info. En malware:

  • Payload cifrado: el dropper almacena el payload real como un recurso cifrado. Al ejecutarse, lee el recurso, lo descifra en memoria y lo ejecuta
  • Configuración: datos de configuración del malware (C2 URLs, claves de cifrado)
  • Segundo stage: otro PE embebido como recurso
  • Documentos señuelo: PDFs o documentos que se muestran al usuario mientras el malware se instala en background

Detección: examinar los recursos con herramientas como Resource Hacker. Recursos con nombres genéricos (RT_RCDATA con IDs numéricos), alta entropía o tamaño grande son sospechosos.

Debug Directory y PDB Path

El Debug Directory puede contener la ruta al archivo PDB (Program Database) de símbolos de depuración:

C:\Users\admin\Desktop\malware_project\Release\payload.pdb
D:\work\rat_builder\x64\Release\client.pdb

El PDB path es valioso porque:

  • Revela el nombre del proyecto y el directorio de desarrollo
  • Puede contener el nombre de usuario del desarrollador
  • Permite vincular muestras diferentes del mismo desarrollador
  • A veces contiene rutas en cirílico (indicando desarrollador ruso)

Nota: muchas familias de malware eliminan o falsifican el PDB path. Su ausencia no indica nada; su presencia es un bonus de inteligencia.

Firma digital (Authenticode)

Los PE pueden tener firma digital (Authenticode) que certifica la identidad del publisher:

EstadoSignificado
Firma válida de publisher conocidoBinario legítimo (alta confianza)
Firma válida de publisher desconocidoPosible certificado robado o comprado
Firma inválida (certificado expirado/revocado)Certificado robado, o malware firmado antes de la revocación
Sin firmaLa mayoría del malware. También muchos binarios legítimos

Certificados robados: grupos como Lazarus, APT41 y operadores de ransomware han usado certificados de firma de código robados para firmar malware. Esto permite bypass de controles que confían en binarios firmados.

Herramientas de análisis de PE

HerramientaTipoUso principal
PE-bearGUIVisualización completa de estructura PE
pestudioGUITriage rápido: imports, strings, entropy, indicators
CFF ExplorerGUIEditor de headers PE, visualización de estructuras
Detect It Easy (DIE)GUIDetección de compilador, packer, linker
pefile (Python)LibreríaScripting y análisis automatizado de PEs
CAPA (Mandiant)CLIDetección automática de capacidades (APIs, comportamiento)
FLOSS (Mandiant)CLIExtracción de strings ofuscadas y stack strings
strings / strings -aCLIExtracción de strings ASCII y Unicode
Exeinfo PEGUIDetección de packers y protectors
HashMyFilesCLI/GUICálculo de hashes (MD5, SHA1, SHA256)

Workflow de análisis estático de un PE

  1. Hash: calcular MD5, SHA1, SHA256. Buscar en VirusTotal
  2. Strings: extraer strings con strings y FLOSS. Buscar URLs, IPs, rutas, mensajes
  3. PE headers: examinar timestamp, entry point, secciones, imports con pestudio
  4. Entropía: calcular entropía por sección. Entropía mayor a 7.0 indica packing
  5. Packer detection: ejecutar DIE para identificar el packer
  6. Imports: analizar la Import Table. Categorizar las APIs por comportamiento
  7. CAPA: ejecutar para detección automática de capacidades
  8. Resources: examinar recursos embebidos con Resource Hacker
  9. PDB path: buscar en el Debug Directory
  10. Firma: verificar Authenticode con sigcheck (Sysinternals)

Lo que viene después

Este artículo ha cubierto la estructura del formato PE y cómo analizarlo. El siguiente artículo profundiza en una de las técnicas más comunes de malware en Windows: la inyección de DLLs. Cómo funciona, qué variantes existen (classic DLL injection, reflective loading, DLL side-loading) y cómo detectar cada una.


Fuentes y referencias

Preguntas frecuentes

Artículos relacionados

Este contenido tiene fines exclusivamente educativos y de investigación en ciberseguridad defensiva. No se proporcionan binarios maliciosos ni payloads ejecutables. El uso indebido de esta información es responsabilidad exclusiva del usuario. Leer disclaimer completo.