Pentest

ELF, PE, Mach-O : différences et analyse reverse 2026

ELF vs PE vs Mach-O : structure, sections, headers, magic bytes, outils d'analyse (readelf, PE-bear, otool), architectures et signatures code signing.

Naim Aouaichia
18 min de lecture
  • ELF
  • PE
  • Mach-O
  • Formats binaires
  • Reverse engineering
  • Windows
  • Linux
  • macOS
  • iOS

ELF (Executable and Linkable Format), PE (Portable Executable) et Mach-O (Mach Object) sont les trois formats de fichiers exécutables dominants en 2026, correspondant respectivement aux écosystèmes Linux/BSD/Android, Windows et macOS/iOS. Tous les trois répondent au même besoin : décrire comment charger un programme en mémoire, résoudre ses dépendances dynamiques, lier ses symboles et exécuter son code. Mais ils diffèrent dans leur structure, leurs headers, leurs sections/segments, leurs architectures supportées et leurs mécanismes de signature. ELF repose sur une distinction claire entre Program Headers (runtime, segments) et Section Headers (link-time, sections). PE intègre un DOS stub legacy (MZ) suivi d'un NT Header avec Optional Header et table de sections. Mach-O utilise des Load Commands dynamiques plutôt que des tables fixes, avec notamment LC_SEGMENT_64 pour le mapping mémoire et LC_CODE_SIGNATURE pour la signature intégrée. Les magic bytes permettent une identification rapide : 7F 45 4C 46 pour ELF, 4D 5A (MZ) + PE\\0\\0 pour PE, FE ED FA CF pour Mach-O 64-bit. Cet article détaille la structure de chaque format, les sections importantes pour le reverse engineering, les outils d'analyse par plateforme, les architectures supportées et les mécanismes de sécurité (Authenticode, Apple code signing).

Vue d'ensemble des trois formats

Généalogie et adoption

ELF (Executable and Linkable Format)
  Créé 1999 par UNIX System V
  Standard UNIX / Linux / BSD / Solaris
  Adopté par Android pour bibliothèques natives (.so)
  Aussi utilisé dans firmware OS embarqué, OSes hobby
 
PE (Portable Executable)
  Microsoft NT 3.1 en 1993
  Dérivé du format COFF Unix
  Standard Windows (.exe, .dll, .sys, .cpl, .ocx)
  Compatibility layer DOS conservée (MZ header)
  Extension PE32+ en 2003 pour 64-bit
 
Mach-O (Mach Object)
  NeXT / NeXTSTEP (Steve Jobs, 1989+)
  Basé sur Mach microkernel de Carnegie Mellon
  Hérité par Apple en 1996 (rachat NeXT)
  Standard macOS, iOS, tvOS, watchOS, visionOS
  Support fat/universal binaries (multi-arch)

Position des trois formats en 2026

ELF :
  Linux : tous les exécutables et bibliothèques
  Android : .so natives, binaires système AOSP
  BSD (FreeBSD, OpenBSD, NetBSD) : format natif
  Hurd, Solaris, AIX (certaines versions), etc.
 
PE :
  Windows 11/10/Server : .exe, .dll, .sys, .cpl, .ocx, .scr
  ReactOS (clone OSS Windows) : compatibilité PE
  Wine / Proton : chargement PE sur Linux
  UEFI firmware : dérivé PE (PE+ pour EFI)
 
Mach-O :
  macOS (Intel x86_64, Apple Silicon ARM64)
  iOS, iPadOS, tvOS, watchOS
  visionOS (Apple Vision Pro, 2024+)

Magic bytes - identification immédiate

ELF :
  Hex    : 7F 45 4C 46
  ASCII  : .ELF
  EI_CLASS byte à l'offset 4 : 0x01 (32-bit) ou 0x02 (64-bit)
  EI_DATA byte à l'offset 5  : 0x01 (LE) ou 0x02 (BE)
 
PE :
  Début  : 4D 5A "MZ" (DOS header)
  Offset e_lfanew (à 0x3C) pointe vers PE signature
  PE sig : 50 45 00 00 "PE\0\0"
  Optional Header Magic :
    0x010B (0B 01 little-endian) : PE32 (32-bit)
    0x020B (0B 02 LE)            : PE32+ (64-bit)
 
Mach-O :
  32-bit :   FE ED FA CE (magic "feedface")
  64-bit :   FE ED FA CF (magic "feedfacf")
  32-bit BE: CE FA ED FE (reverse endian)
  64-bit BE: CF FA ED FE (reverse endian)
  Fat 32  :  CA FE BA BE
  Fat 64  :  CA FE BA BF

Vérification rapide en CLI

# Auto-détection par file
file /bin/ls
# Retour : ELF 64-bit LSB pie executable, x86-64
 
file /System/Applications/Calculator.app/Contents/MacOS/Calculator
# Retour : Mach-O universal binary with 2 architectures
 
file monApp.exe
# Retour : PE32+ executable (GUI) x86-64, for MS Windows
 
# Hexdump des magic bytes
xxd -l 16 binary | head -1
 
# Avec LIEF Python (multi-format)
python3 -c "import lief; print(lief.parse('binary').format)"
# Retour : ELF_FORMAT, PE_FORMAT, ou MACHO_FORMAT

Format ELF en détail

Structure globale

+-------------------+
|  ELF Header       |  64 octets (ELF64) ou 52 octets (ELF32)
+-------------------+
|  Program Headers  |  N entries décrivant segments runtime
|  (Segment table)  |
+-------------------+
|  Section 1 (.text)|
|  Section 2 (.data)|
|  ...              |
|  Section N        |
+-------------------+
|  Section Headers  |  N entries décrivant sections link-time
|  (Section table)  |
+-------------------+

ELF Header (ehdr)

typedef struct {
    unsigned char e_ident[16];   // magic + class + data + version + OS/ABI
    uint16_t      e_type;         // 1=REL, 2=EXEC, 3=DYN (PIE/lib), 4=CORE
    uint16_t      e_machine;      // 0x3E=x86_64, 0xB7=AArch64, 0x28=ARM, 0x08=MIPS
    uint32_t      e_version;
    uint64_t      e_entry;        // entry point virtual address
    uint64_t      e_phoff;        // program header table offset
    uint64_t      e_shoff;        // section header table offset
    uint32_t      e_flags;
    uint16_t      e_ehsize;       // ELF header size (64 for ELF64)
    uint16_t      e_phentsize;    // size of program header entry
    uint16_t      e_phnum;        // number of program headers
    uint16_t      e_shentsize;    // size of section header entry
    uint16_t      e_shnum;        // number of section headers
    uint16_t      e_shstrndx;     // section name string table index
} Elf64_Ehdr;

Sections typiques

SectionRôlePermissions typiques
.textCode machine exécutableR-X
.dataDonnées initialiséesRW-
.bssDonnées non initialisées (zeroed)RW-
.rodataDonnées en lecture seule (strings, constants)R--
.plt / .plt.gotProcedure Linkage Table pour appels dynamiquesR-X
.got / .got.pltGlobal Offset Table, résolution dynamiqueRW-
.dynsymTable de symboles dynamiquesR--
.dynstrStrings pour dynsymR--
.rela.dyn / .rela.pltRelocationsR--
.init / .finiConstructor / destructor globauxR-X
.debug_*DWARF debug info (si non stripped)--
.note.*Métadonnées (GNU build ID, etc.)--

Program Headers (segments runtime)

Type PT_LOAD           : segment chargé en mémoire (R/W/X selon flags)
Type PT_DYNAMIC        : table de linking dynamique
Type PT_INTERP         : chemin de l'interpréteur (ex. /lib64/ld-linux-x86-64.so.2)
Type PT_NOTE           : notes système
Type PT_GNU_STACK      : flag stack executable ou non
Type PT_GNU_RELRO      : Relocation Read-Only

Format PE en détail

Structure globale

+-------------------+
|  DOS Header (MZ)  |  64 octets, legacy DOS stub
|  DOS Stub program |  Code DOS affichant "This program cannot be run in DOS mode"
+-------------------+
|  PE Signature     |  "PE\0\0" (4 octets)
+-------------------+
|  COFF File Header |  20 octets
+-------------------+
|  Optional Header  |  224 octets (PE32) ou 240 octets (PE32+)
+-------------------+
|  Data Directories |  16 entrées (Import, Export, Resource, TLS, etc.)
+-------------------+
|  Section Headers  |  N entrées de 40 octets
+-------------------+
|  Sections data    |
+-------------------+

DOS Header et stub legacy

Le DOS header est une relique historique : tout PE commence par MZ (pour Mark Zbikowski, designer du format MZ DOS). Le stub DOS est un petit programme DOS qui affiche « This program cannot be run in DOS mode » si le PE est lancé dans DOS.

Optional Header

Malgré son nom, l'Optional Header est obligatoire pour un exécutable. Il contient :

Magic                  : 0x010B (PE32) ou 0x020B (PE32+)
MajorLinkerVersion
SizeOfCode
AddressOfEntryPoint    : RVA du premier instruction exécutée
BaseOfCode
ImageBase              : adresse préférée de chargement
                         0x00400000 pour PE32 classic
                         0x0000000140000000 pour PE32+
SectionAlignment, FileAlignment
MajorOSVersion, MajorSubsystemVersion
SizeOfImage, SizeOfHeaders
CheckSum               : CRC pour drivers
Subsystem              : 1 NATIVE, 2 WINDOWS_GUI, 3 WINDOWS_CUI
DllCharacteristics     : ASLR, DEP, SEH, etc.
SizeOfStackReserve/Commit, SizeOfHeapReserve/Commit
NumberOfRvaAndSizes    : nombre de Data Directories (généralement 16)

Sections PE typiques

SectionRôlePermissions
.textCode exécutableR-X
.dataDonnées initialiséesRW-
.rdataDonnées en lecture seuleR--
.bssDonnées non initialiséesRW-
.rsrcRessources (icônes, dialogs, strings)R--
.relocRelocations (pour ASLR)R--
.pdataException handling tables (x64)R--
.idataImport tableR--
.edataExport table (DLLs)R--
.tlsThread Local StorageRW-
.CRTC Runtime initializersR--

Data Directories (16 entrées principales)

0  Export Table
1  Import Table
2  Resource Table
3  Exception Table
4  Certificate Table        (Authenticode signature)
5  Base Relocation Table
6  Debug
7  Architecture
8  Global Ptr
9  TLS Table
10 Load Config Table         (CFG, Control Flow Guard)
11 Bound Import
12 IAT (Import Address Table)
13 Delay Import Descriptor
14 CLR Runtime Header        (.NET assemblies)
15 Reserved

Format Mach-O en détail

Structure globale

+-------------------+
|  Mach Header      |  32 octets (mach_header_64)
+-------------------+
|  Load Commands    |  sequence dynamique de commandes
|                   |  LC_SEGMENT_64, LC_DYLD_INFO, LC_MAIN,
|                   |  LC_LOAD_DYLIB, LC_CODE_SIGNATURE, etc.
+-------------------+
|  Segments data    |
|  Chaque segment   |
|  contient ses     |
|  sections         |
+-------------------+
|  Link edit data   |  symbols, strings, relocations, signature
+-------------------+

Mach Header 64-bit

struct mach_header_64 {
    uint32_t  magic;           // 0xFEEDFACF
    cpu_type_t     cputype;    // CPU_TYPE_X86_64, CPU_TYPE_ARM64
    cpu_subtype_t  cpusubtype;
    uint32_t  filetype;        // MH_EXECUTE, MH_DYLIB, MH_BUNDLE, MH_KEXT_BUNDLE
    uint32_t  ncmds;           // number of load commands
    uint32_t  sizeofcmds;
    uint32_t  flags;           // MH_NOUNDEFS, MH_PIE, MH_HAS_TLV_DESCRIPTORS
    uint32_t  reserved;
};

Load Commands principaux

LC_SEGMENT_64         : mappe un segment en mémoire (équiv. ELF PT_LOAD)
                        Contient les sections (.text, .data)
LC_MAIN               : entry point (remplace LC_UNIXTHREAD moderne)
LC_LOAD_DYLIB         : charge une bibliothèque dynamique
LC_ID_DYLIB           : pour dylib, son propre install name
LC_DYLD_INFO_ONLY     : info pour dyld (rebase, binding, exports)
LC_SYMTAB             : table de symboles
LC_DYSYMTAB           : dynamic symbol table
LC_UUID               : identifiant unique du binaire (pour crash reports)
LC_VERSION_MIN_MACOSX : version macOS minimale requise
LC_BUILD_VERSION      : plateforme + versions (moderne, 2018+)
LC_CODE_SIGNATURE     : signature Apple (intégrée, clé du sandboxing iOS)
LC_ENCRYPTION_INFO_64 : pour binaires iOS chiffrés (.ipa App Store)
LC_FUNCTION_STARTS    : table des points d'entrée de fonctions
LC_RPATH              : runtime path pour résolution dylib
LC_SOURCE_VERSION     : version du code source

Segments typiques Mach-O

__PAGEZERO     : page zéro non-accessible (protection contre NULL deref)
               : 4 GB sur 64-bit, seulement sur exécutables MH_EXECUTE
 
__TEXT         : code exécutable et données en lecture seule
               : contient .text, .cstring, .const, .eh_frame, .unwind_info
 
__DATA         : données en lecture/écriture
               : contient .data, .bss, .got, .la_symbol_ptr
 
__DATA_CONST   : données constantes modernes (ASLR-friendly)
               : depuis macOS 10.14 / iOS 12
 
__LINKEDIT     : symbols, strings, relocations, signature
               : partagé en mémoire pour dylibs multiples

Fat / Universal binaries

Mach-O supporte nativement les fat binaries qui contiennent plusieurs architectures dans un seul fichier :

fat_header (CAFE BABE BE ou CAFE BABF LE)
  nfat_arch       : nombre d'architectures
  fat_arch[0]     : offset + size + cputype pour x86_64
  fat_arch[1]     : offset + size + cputype pour ARM64
  fat_arch[2]     : etc.
 
Cas d'usage 2026 :
  macOS sur Intel x86_64 + Apple Silicon ARM64 (transition 2020-2024)
  iOS sur plusieurs variants ARM (armv7, arm64, arm64e)

Tableau comparatif détaillé

CritèreELFPEMach-O
ÉcosystèmeLinux, BSD, Android, SolarisWindowsmacOS, iOS, tvOS, watchOS
Magic7F 45 4C 464D 5A + 50 45 00 00FE ED FA CF
Header principalELF Header (52/64 o)DOS + COFF + OptionalMach Header (32 o)
OrganisationSections + segmentsSections + Data DirectoriesLoad Commands + segments
Entry pointe_entry absoluAddressOfEntryPoint RVALC_MAIN offset
Dynamic linkerInterpreter PT_INTERPIAT + Import DirectoryLC_DYLD_INFO
Import/Export.dynsym + .plt/.gotImport Table + IATLC_LOAD_DYLIB
Symbole manglingStandard UNIXMicrosoft specificUnderscored _main
64-bitELF64 (fichier distinct)PE32+ (étend PE32)mach_header_64
Multi-arch uniqueNon (fichiers séparés)Non (32 vs 64 séparés)Oui (fat binaries)
Signature nativeNonAuthenticode (PKCS#7)LC_CODE_SIGNATURE
Extensions typiques(aucune, ELF, .so, .o).exe, .dll, .sys, .ocx(aucune, .dylib, .bundle)
ASLRPIE via PT_DYN flagDllCharacteristics.DYNAMIC_BASEMH_PIE flag
Stripping binairestrip (garde .dynsym)dumpbin /stripstrip (garde __LINKEDIT)
RPATH runtimeDT_RPATH / DT_RUNPATHManifest / AppContainerLC_RPATH + @rpath

Architectures supportées par format

ELF

ELF est le format avec la plus grande variété d'architectures supportées en tant que format :

Supportées par le format ELF :
  x86, x86_64
  ARM (armv7), ARM64 (AArch64)
  MIPS, MIPS64
  PowerPC, PowerPC64
  RISC-V (32, 64, 128 bits)
  Alpha, IA-64, SPARC, SH, S390/s390x
  AVR, Xtensa (ESP32), RISC-V
  Plus de 100 architectures ID dans e_machine

PE

PE est plus restrictif (écosystème Microsoft) :

Supportées par PE :
  x86 (i386)          : PE32 classique
  x86-64 (AMD64)      : PE32+
  ARM (armv7)         : Windows ARM legacy
  ARM64               : Windows 11 ARM (2021+), Surface Pro X
  Itanium (IA-64)    : déprécié Windows Server 2008 R2
  Certains MIPS, Alpha : historiques NT 4.0

Mach-O

Mach-O est lui aussi limité par les plateformes Apple :

Supportées par Mach-O :
  x86 (i386)          : macOS historique, déprécié Catalina (2019)
  x86_64              : macOS Intel (2006-2024 progression)
  ARM64 (arm64)       : iOS depuis 2014, macOS depuis 2020 Apple Silicon
  arm64e              : iOS avec Pointer Authentication (2018+)
  PowerPC (ppc, ppc64): macOS historique (pré-2006)
  armv7, armv7s       : iOS historique (avant 2014)

Outils d'analyse par format

Pour ELF

CLI (binutils, GNU) :
  readelf -a binary        : tout le header + sections + symbols
  objdump -d binary         : désassemblage
  objdump -M intel -d        : syntaxe Intel plutôt AT&T
  nm binary                  : symboles
  strings binary             : chaînes
  strip binary                : retire symbols
  patchelf                    : modifier RPATH, INTERP
  hexdump -C | head
 
Python :
  pyelftools                  : parser ELF Python
  LIEF                        : multi-format + manipulation
 
Frameworks :
  Ghidra                     : support ELF natif
  IDA Pro                    : idem
  radare2                    : idem
  Binary Ninja               : idem

Pour PE

Windows :
  dumpbin.exe (Visual Studio) : équivalent objdump
  sigcheck (Sysinternals)      : signatures, entropie
  CFF Explorer                 : éditeur PE visuel
  PE-bear                     : analyseur moderne OSS
  pestudio                    : analyseur malware focus
  PE Explorer                 : historique mais fonctionnel
 
Cross-platform :
  pefile (Python)              : parser PE
  peframe                      : analyse malware PE
  LIEF                         : cross-platform manipulation
  pebear-cli                   : alternative CLI
 
Frameworks :
  Ghidra, IDA Pro, radare2, Binary Ninja : support PE natif

Pour Mach-O

macOS natifs :
  otool -l                    : load commands
  otool -L binary              : dylibs linkées
  otool -tV                    : désassemblage
  otool -d                     : data segments
  nm                           : symbols
  dyldinfo                    : info dyld
  codesign -dv --entitlements - binary : signature + entitlements
  install_name_tool            : modifier dylib paths
  lipo -info                   : inspect fat binary
  lipo -thin arm64 fat -output thin : extraire une arch
 
Cross-platform :
  LIEF                         : manipulation multi-format
  machoview (GUI Mac)
 
Frameworks :
  Ghidra, IDA Pro (+ Hex-Rays pour ARM64e), Binary Ninja, radare2

Mécanismes de sécurité et signatures

ELF - pas de signature native

ELF n'intègre pas de mécanisme de signature par le format. Les signatures sont gérées à un niveau supérieur :

Distribution packages :
  RPM : signature GPG sur le package entier
  DEB : signature GPG via apt-key / signed repositories
  Arch : signatures pacman via sign repos
 
Kernel modules :
  CONFIG_MODULE_SIG avec signature X.509
  Ajoutée en append au .ko (format ELF inchangé)
 
UEFI Secure Boot :
  Signatures MOK (Machine Owner Keys)
  Format PE+ pour EFI, pas ELF pur
 
Code signing expérimental :
  fs-verity (kernel 5.4+) pour immutabilité fichier
  IMA (Integrity Measurement Architecture)

PE - Authenticode (signature Microsoft)

Authenticode :
  Signature PKCS#7 dans le Certificate Table (Data Directory #4)
  Ajoutée à la fin du binaire
  Hash PE signé avec clé privée Authenticode
 
Vérification :
  signtool verify /pa monApp.exe         (Windows CLI)
  Get-AuthenticodeSignature monApp.exe   (PowerShell)
  osslsigncode (OSS cross-platform)
 
Niveaux de trust :
  Publisher Identity Verification
  Windows Defender SmartScreen
  User Account Control (UAC) elevation dialog mention

Mach-O - Apple Code Signing intégré

Le format Mach-O intègre nativement la signature via LC_CODE_SIGNATURE qui pointe vers la SuperBlob contenant :

CodeDirectory :
  Hashes SHA-256 de chaque page de code (4 KB)
  Identifier (bundle ID)
  Version minimale OS
  Flags (kill, hard, etc.)
 
Requirements :
  Restrictions sur le signataire
 
Entitlements (plist XML) :
  Capabilities accordées (sandbox, keychain, network, camera)
 
CMS Signature :
  PKCS#7 avec certificat Apple Developer ID
 
Vérification :
  codesign -v --verbose=4 binary
  codesign -dv --entitlements - binary
 
Notarization (macOS Catalina 2019+) :
  Binaires distribués hors App Store doivent être notarizés
  Apple scan pour malware avant notarization

Cas pratique : identifier et analyser un binaire

Scénario : fichier suspect reçu par email

# Étape 1 - Identification
file suspicious_file
# Retour possible :
#   "ELF 64-bit LSB executable, x86-64" → analyser avec readelf
#   "PE32+ executable (console) x86-64" → analyser avec PE-bear
#   "Mach-O 64-bit executable x86_64" → analyser avec otool
 
# Étape 2 - Hashes et VirusTotal
md5sum suspicious_file
sha256sum suspicious_file
# Upload sha256 à virustotal.com pour check historique
 
# Étape 3 - Extraction strings
strings suspicious_file | less
 
# Étape 4 - Analyse header selon format
# Pour ELF :
readelf -h suspicious_file    # ELF Header
readelf -l suspicious_file    # Program Headers (segments)
readelf -S suspicious_file    # Section Headers
readelf -d suspicious_file    # Dynamic section
readelf -s suspicious_file    # Symbols
 
# Pour PE (sous Linux avec pefile) :
python3 -c "
import pefile
pe = pefile.PE('suspicious_file.exe')
print(pe.FILE_HEADER)
for section in pe.sections:
    print(section.Name, hex(section.VirtualAddress), section.SizeOfRawData)
for entry in pe.DIRECTORY_ENTRY_IMPORT:
    print(entry.dll.decode())
"
 
# Pour Mach-O (macOS) :
otool -h suspicious_file      # header
otool -l suspicious_file      # load commands
otool -L suspicious_file      # linked dylibs
codesign -dv suspicious_file  # signature
 
# Étape 5 - Analyse avec outil GUI
# Importer dans Ghidra ou IDA Pro pour décompilation

Cas des formats dérivés modernes

PE+ pour UEFI

UEFI firmware utilise PE+ (PE32+) avec subsystem EFI_APPLICATION, EFI_BOOT_SERVICE_DRIVER, etc. Bootloaders comme GRUB en mode EFI sont des PE+.

Mach-O sandbox iOS

Sur iOS, les apps distribuées via App Store sont chiffrées avec LC_ENCRYPTION_INFO_64. Le déchiffrement se fait au chargement par iOS. Pour analyse reverse, il faut extraire le binaire déchiffré depuis device rooté/jailbroken (outils : Clutch, bfinject, Frida).

.NET managed executables

Format PE mais avec Data Directory #14 (CLR Header) pointant vers le CLR metadata. Analysable avec dnSpy, ILSpy, dotPeek (tout en .NET managed bytecode).

Binaires Go

Les binaires Go sont des ELF/PE/Mach-O standard mais contiennent des sections spécifiques Go (.gosymtab, .gopclntab) et runtime embarqué. Outils : go tool objdump, Ghidra avec plugin GolangAnalyzerExtension.

Binaires Rust et C++

ELF/PE/Mach-O standard avec symbol mangling spécifique. c++filt pour démangler C++. rustfilt pour Rust. Ghidra gère automatiquement depuis 11.x.

Points importants pour le reverse 2026

Architectures ARM64 dominantes

ARM64 domine progressivement : Apple Silicon pour Mach-O, Windows 11 ARM pour PE, serveurs Graviton AWS et Ampere pour ELF. Maîtriser ARM64 assembly devient aussi important que x86_64.

Stripping et symbols

Binaires stripped retirent les symbols pour analyse plus dure. Outils modernes reconstruisent partiellement :

ELF : debuginfod (Fedora) pour DWARF symbols
PE  : symbol server Microsoft public
Mach-O : dSYM files séparés en debug, retrouvables via LC_UUID

Obfuscation et packers

Chaque format a ses packers typiques :

ELF : UPX (universel), pour PE aussi
PE  : Themida, VMProtect, Enigma, custom VM
Mach-O : relativement moins de packers (écosystème fermé)

Points clés à retenir

  • Trois formats binaires dominants 2026 : ELF (Linux/BSD/Android), PE (Windows), Mach-O (macOS/iOS). Identification immédiate via magic bytes : 7F 45 4C 46, MZ + PE\\0\\0, FE ED FA CF.
  • ELF : structure sections/segments claire, pas de signature native, le plus polyvalent (100+ architectures dans e_machine).
  • PE : hérite du MZ legacy DOS, Optional Header obligatoire, 16 Data Directories (Imports, Exports, Certificate = Authenticode).
  • Mach-O : Load Commands dynamiques, fat binaries multi-arch, code signing intégré via LC_CODE_SIGNATURE, standard iOS/macOS.
  • Outils principaux : readelf + objdump + nm (ELF), PE-bear + pefile + dumpbin (PE), otool + codesign + install_name_tool (Mach-O). Ghidra, IDA Pro, radare2, Binary Ninja, LIEF supportent les 3.
  • Signatures : ELF sans signature native (GPG via package managers), PE avec Authenticode PKCS#7 optionnel, Mach-O avec code signing obligatoire pour iOS et notarization pour macOS Catalina+.
  • Architectures : ARM64 domine progressivement dans les 3 formats (Apple Silicon, Windows 11 ARM, serveurs cloud Graviton/Ampere). Maîtriser x86_64 et ARM64 est essentiel.
  • Dérivés et variants : PE+ pour UEFI, Mach-O chiffré iOS, .NET managed dans PE, Go/Rust/C++ dans ELF/PE/Mach-O avec sections spécifiques.

Pour les fondamentaux du reverse engineering qui utilise ces formats, voir qu'est-ce que le reverse engineering : définition 2026. Pour un parcours d'apprentissage qui inclut l'analyse de ces formats, roadmap reverse engineering 2026 : parcours complet étape par étape détaille les 4 étapes. Pour un cas pratique avec format ELF (.so natives dans APK), lire analyse d'un APK Android : le guide pratique 2026. Pour l'outillage pentester complet qui inclut le reverse, consulter les outils de base du pentester en 2026.

Questions fréquentes

  • Quelle est la différence principale entre ELF, PE et Mach-O ?
    Trois formats d'exécutables correspondant à trois écosystèmes. ELF (Executable and Linkable Format) est le format natif Linux, BSD, et Android - conçu en 1999 pour UNIX System V. PE (Portable Executable) est le format Windows depuis NT 3.1 (1993), dérivé du format COFF Unix avec compatibility layer DOS. Mach-O (Mach Object) est le format macOS et iOS, hérité de NeXT/NeXTSTEP racheté par Apple en 1996, basé sur la distinction load commands plutôt que sections fixes. Les trois sont différents en structure mais remplissent le même rôle : décrire comment charger un programme en mémoire.
  • Comment identifier rapidement le format d'un binaire ?
    Via les magic bytes au début du fichier (4-8 premiers octets). ELF : `7F 45 4C 46` (ASCII `.ELF`). PE : commence par `4D 5A` (MZ DOS stub) puis cherche signature `PE\0\0` (50 45 00 00) à l'offset indiqué dans e_lfanew. Mach-O : `FE ED FA CE` (32-bit) ou `FE ED FA CF` (64-bit) ou `CE FA ED FE` (32-bit reverse endian) ou `CF FA ED FE` (64-bit reverse). Fat Mach-O universel : `CA FE BA BE` (32-bit) ou `CA FE BA BF` (64-bit). En pratique : `file binary` détecte automatiquement, `xxd -l 16 binary | head -1` montre les magic.
  • Quels outils pour analyser chaque format ?
    ELF sur Linux : readelf, objdump (binutils), nm, strip, patchelf, pyelftools, LIEF. PE sur Windows : PE-bear (GUI moderne), pestudio, CFF Explorer, PE Explorer, PEview, dumpbin.exe (inclus MSVC), pefile (Python). Mach-O sur macOS : otool (natif macOS), nm, codesign, pagestuff, install_name_tool, lipo (pour fat binaries), LIEF (cross-platform Python). Universels multi-format : Ghidra, IDA Pro, radare2, Binary Ninja acceptent les trois formats nativement. Pour analyse rapide multi-format en CLI : `file`, `xxd`, `strings`, LIEF Python.
  • Quelle différence entre sections et segments ?
    Les sections sont des unités logiques à l'intérieur du binaire pour l'assembleur et le linker (ex. .text contient le code, .data les données initialisées). Les segments sont des unités physiques pour le chargeur runtime (loader), décrivant comment charger des portions du binaire en mémoire avec permissions read/write/execute. Un segment contient typiquement plusieurs sections. En ELF, Program Headers décrivent les segments (runtime), Section Headers décrivent les sections (link-time). En PE, les sections sont les unités principales. En Mach-O, les Load Commands LC_SEGMENT contiennent des sections.
  • Les exécutables Android sont-ils ELF ou quelque chose de spécifique ?
    Android utilise ELF pour les bibliothèques natives (.so dans lib/) et certains binaires système, mais le code applicatif est en bytecode Dalvik/ART (.dex). Un APK Android contient typiquement : plusieurs .dex (Java/Kotlin compilé) + .so ELF (code natif JNI, ARM ou x86) + ressources + manifest. Pour les systèmes embarqués Android (AOSP), les binaires OS core sont tous ELF. iOS utilise Mach-O pour tous ses binaires natifs, y compris dans les .ipa (qui contient Mach-O + Info.plist + ressources).
  • Comment sont signés les binaires dans chaque format ?
    PE utilise Authenticode (Microsoft) : signature PKCS#7 attachée à la fin du binaire, visible via `signtool verify` ou propriétés Windows. Mach-O intègre le code signing nativement via LC_CODE_SIGNATURE : hashes des pages de code + entitlements + équipe développeur Apple. iOS exige des binaires signés par Apple pour exécution, macOS depuis Catalina (2019) exige notarization. ELF n'a pas de mécanisme de signature standard natif - signatures externes via GPG détachées, ou mécanismes distribution-spécifiques (RPM, DEB, APT signed repos). Secure Boot UEFI et kernel module signing sur Linux utilisent des signatures mais hors format ELF.

Écrit par

Naim Aouaichia

Expert cybersécurité et fondateur de Zeroday Cyber Academy

Expert cybersécurité avec un master spécialisé et un parcours hybride : développement, DevOps, DevSecOps, SOC, GRC. Fondateur de Hash24Security et Zeroday Cyber Academy. Formateur et créateur de contenu technique sur la cybersécurité appliquée, la sécurité des LLM et le DevSecOps.