
Agradecemos a Ezequiel Salinas Bydlowski que comparte su experiencia y conocimientos a través de este post.
Ezequiel es especialista en ciberseguridad ofensiva, pentesting y Red Team, está enfocado en detección de vulnerabilidades y fortalecimiento defensivo.

Este posteo habla sobre los tipos de binarios que existen dentro de los distintos SO (Sistemas Operativos), la intención es explicar cómo se conforman estos a bajo nivel, lo cual es un punto de partida para entender cómo se analiza malware. Mismo en este posteo veremos algunas de las herramientas para análisis estático de binarios. Se deja para una futura publicación lo que sería análisis de APK e IPSW.
Los formatos de los distintos binarios se dividen según el sistema en que están preparados para ser ejecutados, los binarios PE están preparados para Windows, los ELF están preparados para Linux y Mach-O formato preparado para MacOS. Cada uno de estos tres tienen diferencias entre sí, como la arquitectura que soportan, la forma de identificar sus formatos, como deben organizarse las cabeceras, pero, tienen en común algo que es el tipo de archivo, esto quiere decir que los tres funcionan como un contenedor con instrucciones y herramientas para que el sistema a través de lenguaje ASM comprenda las acciones que debe realizar.
Lo mejor para poder entender cómo se componen los binarios es analizarlos de manera estática, para poder ver cada header y entender para que sirve, con esto podemos ver que nos encontraremos dentro de cualquier binario a la hora de analizar.
Es un estándar de binarios para Windows de 32 y 64 bits, se enfoca en ejecutables (.exe), archivos de tipo .dll y controladores (.sys).
Las cabeceras diferenciales de este tipo de archivo son:
La mayoría de las DOS Headers son cabeceras de herencia que estan ahi solo por el mero hecho de hacer que un binario nuevo tenga compatibilidad con un sistema antiguo.

El campo que vemos como e_lfanew sería el parámetro de inicio del PE Header que importa para analizar, este es un puntero indica donde comienzan esas cabeceras importantes para analizar.
Entre los DOS Header y los PE Header normalmente hay un DOS Stub: es una parte del binario que imprime el mensaje “This program cannot be run in DOS mode” si se intenta ejecutar un .exe en un OS MS-DOS.
El PE Header se divide en tres partes:

Hay que tener en cuenta que a pesar de que su nombre lo diga no es opcional, ya que es esencial para el correcto funcionamiento de binarios
.exe y .dll, esto ya que contiene cabeceras como las siguientes:




A continuación, una lista de las secciones que podrían encontrarse si analizaran un binario PE
Secciones del compilador:
| Sección | Contenido | Permisos |
| .text | Código ejecutable | R + X |
| .data | Variables globales inicializadas | R + W |
| .rdata | Datos de solo lectura, strings, imports | R |
| .bss | Variables no inicializadas | R + W |
| .reloc | Tabla de relocalizaciones para ASLR | R |
| .pdata | Tabla de excepciones (solo x64) | R |
| .xdata | Datos de unwinding para excepciones (x64) | R |
| .edata | Export Table (funciones exportadas) | R |
| .idata | Import Table (DLLs y funciones importadas) | R |
| .tls | Thread Local Storage | R + W |
| .rsrc | Recursos: iconos, strings, manifests, versión | R |
| .crt | Datos de inicialización del CRT de C++ | R |
Secciones de debugging*:
| Sección | Contenido | Permisos |
| .debug | Información de debugging genérica | R |
| .debug$S | Símbolos de debugging (MSVC) | R |
| .debug$T | Tipos de debugging (MSVC) | R |
| .symtab | Tabla de símbolos | R |
| .strtab | Tabla de strings de símbolos | R |
Secciones de seguridad y protección:
| Sección | Contenido | Permisos |
| .sxdata | Datos de Safe Exception Handlers (SEH) | R |
| .gfids | Guard CF — tabla de funciones válidas para CFG | R |
| .gehcont | Guard EH Continuation (mitigación de excepciones) | R |
| .voltbl | Volatile metadata table | R |
Secciones específicas de .NET / CLR**:
| Sección | Contenido | Permisos |
| .text (CLR) | IL bytecode + metadata | R + X |
| .rsrc (CLR) | Recursos del ensamblado | R |
| .reloc (CLR) | Relocalizaciones del CLR | R |
*Estas secciones generalmente se eliminan en builds de Release. Si las ves en un binario sospechoso, puede ser que el autor compiló en modo Debug por descuido, lo cual filtra rutas del sistema, nombres de variables y estructura del proyecto.
**Los binarios .NET tienen una estructura interna completamente diferente dentro de estas secciones. El entry point real lleva al CLR runtime, no directamente al código del programador.
Las letras en permisos significan lo siguiente: R (lectura), W (escritura) y X (ejecución). Es importante aclarar que estos no son los permisos que tienes como usuario sobre el archivo en disco, sino los atributos que el loader de Windows asigna a cada región de memoria cuando carga el binario. Es decir, determinan qué operaciones puede realizar el procesador sobre esa región una vez el ejecutable está en memoria.
Los binarios de tipo Executable and Linkable Format son los que le envían instrucciones a maquinas con Kernel de Linux para poder ejecutar instrucciones comprensibles para la máquina. A diferencia del PE que tiene el legado MZ, el ELF fue diseñado desde cero en 1999 como parte del estándar System V ABI, por lo que es más limpio conceptualmente.
Se compone de las siguientes cabeceras (Headers):
\x7FELF
Este Header tiene la siguiente estructura:


Valores de e_type :
| Valor | Constante | Significado |
| 0x0000 | ET_NONE | Tipo desconocido |
| 0x0001 | ET_REL | Archivo relocatable (.o) |
| 0x0002 | ET_EXEC | Ejecutable estático |
| 0x0003 | ET_DYN | Shared object / PIE executable |
| 0x0004 | ET_CORE | Core dump |
Nota: Los ejecutables modernos compilados con PIE (Position Independent Executable) aparecen como ET_DYN, no como ET_EXEC.
Valores de e_machine
| Valor | Arquitectura |
| 0x03 | x86 32-bit |
| 0x3E | x86-64 |
| 0x28 | ARM 32-bit |
| 0xB7 | AArch64 (ARM 64-bit) |
| 0xF3 | RISC-V |
Cada entrada describe un segmento (segment): muestra una sección del binario que el kernel mapea en la memoria.

Tipos de segmentos más importantes:
| Tipo | Valor | Significado |
| PT_LOAD | 0x1 | Segmento que se carga en memoria |
| PT_DYNAMIC | 0x2 | Info del dynamic linker (símbolos, libs) |
| PT_INTERP | 0x3 | Path al dynamic linker (/lib64/ld–linux–x86-64.so.2) |
| PT_NOTE | 0x4 | Metadata del compilador / SO |
| PT_PHDR | 0x6 | Ubicación del propio Program Header Table |
| PT_TLS | 0x7 | Thread Local Storage |
| PT_GNU_STACK | 0x6474e551 | Permisos del stack |
| PT_GNU_RELRO | 0x6474e552 | Región de solo lectura post-init |
Cada entrada describe una sección con su contenido especifico:

Secciones más comunes:
| Sección | Tipo | Contenido |
| .text | SHT_PROGBITS | Código ejecutable |
| .data | SHT_PROGBITS | Variables globales inicializadas |
| .bss | SHT_NOBITS | Variables no inicializadas (no ocupa espacio en disco) |
| .rodata | SHT_PROGBITS | Datos de solo lectura, strings |
| .symtab | SHT_SYMTAB | Tabla de símbolos (static) |
| .dynsym | SHT_DYNSYM | Tabla de símbolos dinámicos |
| .strtab | SHT_STRTAB | Strings de .symtab |
| .dynstr | SHT_STRTAB | Strings de .dynsym |
| .shstrtab | SHT_STRTAB | Nombres de todas las secciones |
| .plt | SHT_PROGBITS | Procedure Linkage Table |
| .got | SHT_PROGBITS | Global Offset Table |
| .got.plt | SHT_PROGBITS | GOT entries para PLT |
| .dynamic | SHT_DYNAMIC | Info del dynamic linker |
| Sección | Tipo | Contenido |
| .rel.text | SHT_REL | Relocalizaciones para .text |
| .rela.text | SHT_RELA | Relocalizaciones con addend |
| .init | SHT_PROGBITS | Código de inicialización pre-main |
| .fini | SHT_PROGBITS | Código de finalización post-main |
| .init_array | SHT_INIT_ARRAY | Punteros a constructores |
| .fini_array | SHT_FINI_ARRAY | Punteros a destructores |
| .note.gnu.build–id | SHT_NOTE | Hash único del build |
| .eh_frame | SHT_PROGBITS | Info de unwinding para excepciones |
MacOS: Mach–O
Es el formato de binario de Apple. Fue creado originalmente para el microkernel Mach en la Universidad Carnegie Mellon y Apple lo adoptó cuando compró NeXT en 1997. A diferencia del PE y el ELF, Mach-O tiene una característica única: los Fat Binaries / Universal Binaries.
Fat Binaries / Universal Binaries
Antes de ver la estructura dejo una lista de los distintos tipos de paquetes que entran dentro de Fat Binaries:

Un Fat Binary es un paquete que ejecuta varios Mach-O el sistema se encarga de elegir el slice correcto cuando ejecuta el paquete, su estructura es algo así:

Además de esto podemos ver sus distintos Headers para saber que encontraríamos a la hora de analizar uno

Valores de cputype :
| Valor | Arquitectura |
| 0x07 | x86 32-bit |
| Valor | Arquitectura |
| 0x01000007 | x86_64 |
| 0x0C | ARM 32-bit |
| 0x0100000C | ARM64 (Apple Silicon) |
Valores de filetype :
| Valor | Constante | Significado |
| 0x1 | MH_OBJECT | Archivo objeto (.o) |
| 0x2 | MH_EXECUTE | Ejecutable estándar |
| 0x6 | MH_DYLIB | Librería dinámica (.dylib) |
| 0x7 | MH_DYLINKER | El dynamic linker (dyld) |
| 0x8 | MH_BUNDLE | Bundle / plugin |
| 0xA | MH_DYSYM | Stub de librería dinámica |
| 0xB | MH_DSYM | Archivo de símbolos de debug |
Flags importantes:
| Flag | Significado |
| MH_PIE | PIE habilitado → ASLR activo |
| MH_TWOLEVEL | Namespaces de dos niveles (lib + símbolo) |
| MH_NO_HEAP_EXECUTION | Heap no ejecutable |
| MH_HAS_TLV_DESCRIPTORS | Tiene Thread Local Variables |
| MH_ALLOW_STACK_EXECUTION | Stack ejecutable |
Todos los Loads Commands comparten esta cabecera base:

El loader de macOS (dyld) lee cada Load Command en orden y ejecuta las instrucciones que contiene.
Los Load Commands más importantes
| Comando | Significado |
| LC_SEGMENT_64 | Define un segmento a cargar en memoria |
| LC_DYLD_INFO_ONLY | Info de binding y rebase para dyld |
| LC_SYMTAB | Tabla de símbolos estáticos |
| LC_DYSYMTAB | Tabla de símbolos dinámicos |
| LC_LOAD_DYLIB | Librería dinámica a importar |
| LC_LOAD_WEAK_DYLIB | Librería opcional (no falla si no existe) |
| LC_RPATH | Rutas de búsqueda de librerías |
| LC_MAIN | Entry point del binario |
| LC_UNIXTHREAD | Entry point estilo Unix (binarios viejos) |
| LC_CODE_SIGNATURE | Firma de código (obligatoria en iOS) |
| LC_ENCRYPTION_INFO_64 | Info de cifrado (apps de App Store) |
| LC_VERSION_MIN_MACOSX | Versión mínima de macOS requerida |
| LC_UUID | Identificador único del binario |
| LC_FUNCTION_STARTS | Tabla de offsets a funciones |
| LC_DATA_IN_CODE | Datos embebidos en secciones de código |
| LC_DYLD_CHAINED_FIXUPS | Sistema moderno de fixups de dyld |
LC_LOAD_DYLIB en detalle:

En Mach-O hay una jerarquía de dos niveles Segments que contienen

Segmentos estándar:
| Segmento | Permisos | Contenido |
| __PAGEZERO | Ninguno | Página nula, atrapa null pointer dereferences |
| __TEXT | R + X | Código ejecutable y datos de solo lectura |
| __DATA | R + W | Datos mutables |
| Segmento | Permisos | Contenido |
| __DATA_CONST | R (post-init) | Datos constantes después de inicialización |
| __LINKEDIT | R | Símbolos, strings, firmas, info de dyld |
| __OBJC | R + X | Metadata de Objective-C (binarios legacy) |
Secciones dentro de __TEXT:
| Sección | Contenido |
| __TEXT.__text | Código compilado |
| __TEXT.__stubs | Stubs para llamadas a librerías (equiv. PLT) |
| __TEXT.__stub_helper | Helper para lazy binding |
| __TEXT.__objc_methnames | Nombres de métodos Objective-C |
| __TEXT.__cstring | Strings literales de C |
| __TEXT.__const | Constantes |
| __TEXT.__unwind_info | Info de unwinding de excepciones |
Secciones dentro de __DATA:
| Sección | Contenido |
| __DATA.__got | Global Offset Table (non-lazy) |
| __DATA.__la_symbol_ptr | Lazy symbol pointers (equiv. GOT.PLT) |
| __DATA.__nl_symbol_ptr | Non-lazy symbol pointers |
| __DATA.__cfstring | Strings de CoreFoundation |
| __DATA.__objc_classlist | Lista de clases Objective-C |
| __DATA.__objc_selrefs | Referencias a selectores ObjC |
| __DATA.__bss | Variables no inicializadas |
Dynamic Linker de Apple (dyld)
El equivalente a ld–linux.so en Linux, pero con diferencias importantes:
Kernel carga un binario → lee LC_LOAD_DYLINKER (/usr/lib/dyld) → dyld procesa LC_DYLD_INFO_ONLY → LC_MAIN (entry point del binario)
dyld procesa LC_DYLD_INFO_ONLY (estructura):
Code Signing – Obligatorio en iOS, importante en macOS

| Componente | Función |
| CodeDirectory | Hash de cada página del binario |
| Entitlements | Permisos declarados (XML) |
| CMS Signature | Firma criptográfica de Apple |
| Requirements | Restricciones de ejecución |
| Concepto | PE (Windows) | ELF (Linux) | Mach-O (macOS/iOS) |
| Magic bytes | MZ + PE\0\0 | \x7FELF | 0xFEEDFACF |
| Multi-arch | NO | NO | Fat Binary |
| Entry point | AddressOfEntryPoint | e_entry | LC_MAIN |
| Imports | Import Table / IAT | PLT + GOT | LC_LOAD_DYLIB + stubs |
| Loader | ntdll.dll | ld–linux.so | dyld |
| Segmentos | Sections | Segments +Sections | Segments → Sections |
| Firma decódigo | Opcional | Opcional | Obligatoria en iOS |
| Herramientas | PE-bear, CFFExplorer | readelf,objdump | otool, nm, jtool2 |
Créditos: Ezequiel Salinas Bydlowski