17 KiB
macOS Binaires universels et format Mach-O
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT !
- Découvrez The PEASS Family, notre collection exclusive de NFT
- Obtenez le swag officiel PEASS & HackTricks
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez moi sur Twitter 🐦@carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.
Informations de base
Les binaires Mac OS sont généralement compilés en tant que binaires universels. Un binaire universel peut prendre en charge plusieurs architectures dans le même fichier.
Ces binaires suivent la structure Mach-O qui est essentiellement composée de :
- En-tête
- Commandes de chargement
- Données
En-tête Fat
Recherchez le fichier avec : mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
#define FAT_MAGIC 0xcafebabe
#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */
struct fat_header {
uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
uint32_t nfat_arch; /* nombre de structures qui suivent */
};
struct fat_arch {
cpu_type_t cputype; /* spécificateur de CPU (int) */
cpu_subtype_t cpusubtype; /* spécificateur de machine (int) */
uint32_t offset; /* décalage de fichier vers ce fichier objet */
uint32_t size; /* taille de ce fichier objet */
uint32_t align; /* alignement en puissance de 2 */
};
L'en-tête a les octets magic suivis du nombre d'architectures que le fichier contient (nfat_arch
) et chaque architecture aura une structure fat_arch
.
Vérifiez-le avec :
% file /bin/ls
/bin/ls: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/ls (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/ls (for architecture arm64e): Mach-O 64-bit executable arm64e
% otool -f -v /bin/ls
Fat headers
fat_magic FAT_MAGIC
nfat_arch 2
architecture x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
offset 16384
size 72896
align 2^14 (16384)
architecture arm64e
cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
offset 98304
size 88816
align 2^14 (16384)
ou en utilisant l'outil Mach-O View :
Comme vous pouvez le penser, un binaire universel compilé pour 2 architectures double la taille de celui compilé pour une seule architecture.
En-tête Mach-O
L'en-tête contient des informations de base sur le fichier, telles que les octets magiques pour l'identifier en tant que fichier Mach-O et des informations sur l'architecture cible. Vous pouvez le trouver dans : mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
#define MH_MAGIC 0xfeedface /* the mach magic number */
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
struct mach_header {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier (e.g. I386) */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file (usage and alignment for the file) */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
};
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
int32_t cputype; /* cpu specifier */
int32_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
Types de fichiers:
- MH_EXECUTE (0x2): Exécutable Mach-O standard
- MH_DYLIB (0x6): Une bibliothèque dynamique Mach-O (c'est-à-dire .dylib)
- MH_BUNDLE (0x8): Un bundle Mach-O (c'est-à-dire .bundle)
# Checking the mac header of a binary
otool -arch arm64e -hv /bin/ls
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 ARM64 E USR00 EXECUTE 19 1728 NOUNDEFS DYLDLINK TWOLEVEL PIE
Ou en utilisant Mach-O View:
Commandes de chargement Mach-O
Cela spécifie la disposition du fichier en mémoire. Il contient l'emplacement de la table des symboles, le contexte du thread principal au début de l'exécution et les bibliothèques partagées requises.
Les commandes instruisent essentiellement le chargeur dynamique (dyld) sur la façon de charger le binaire en mémoire.
Les commandes de chargement commencent toutes par une structure load_command, définie dans le loader.h
précédemment mentionné:
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
Il existe environ 50 types différents de commandes de chargement que le système gère différemment. Les plus courantes sont : LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
et LC_CODE_SIGNATURE
.
LC_SEGMENT/LC_SEGMENT_64
{% hint style="success" %} Essentiellement, ce type de commande de chargement définit comment charger les sections qui sont stockées dans DATA lorsque le binaire est exécuté. {% endhint %}
Ces commandes définissent des segments qui sont cartographiés dans l'espace de mémoire virtuelle d'un processus lorsqu'il est exécuté.
Il existe différents types de segments, tels que le segment __TEXT, qui contient le code exécutable d'un programme, et le segment __DATA, qui contient les données utilisées par le processus. Ces segments sont situés dans la section de données du fichier Mach-O.
Chaque segment peut être divisé en plusieurs sections. La structure de commande de chargement contient des informations sur ces sections dans le segment respectif.
Dans l'en-tête, vous trouvez d'abord l'en-tête de segment :
struct segment_command_64 { /* pour les architectures 64 bits */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* inclut la taille des structures section_64 */
char segname[16]; /* nom du segment */
uint64_t vmaddr; /* adresse mémoire de ce segment */
uint64_t vmsize; /* taille mémoire de ce segment */
uint64_t fileoff; /* décalage du fichier de ce segment */
uint64_t filesize; /* quantité à mapper depuis le fichier */
int32_t maxprot; /* protection VM maximale */
int32_t initprot; /* protection VM initiale */
uint32_t nsects; /* nombre de sections dans le segment */
uint32_t flags; /* indicateurs */
};
Exemple d'en-tête de segment :
Cet en-tête définit le nombre de sections dont les en-têtes apparaissent après lui :
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint64_t addr; /* memory address of this section */
uint64_t size; /* size in bytes of this section */
uint32_t offset; /* file offset of this section */
uint32_t align; /* section alignment (power of 2) */
uint32_t reloff; /* file offset of relocation entries */
uint32_t nreloc; /* number of relocation entries */
uint32_t flags; /* flags (section type and attributes)*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
Exemple d'en-tête de section:
Si vous ajoutez le décalage de section (0x37DC) + le décalage où commence l'architecture, dans ce cas 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Il est également possible d'obtenir des informations d'en-tête à partir de la ligne de commande avec:
otool -lv /bin/ls
Segments communs chargés par cette commande :
__PAGEZERO
: Il indique au noyau de mapper l'adresse zéro afin qu'elle ne puisse pas être lue, écrite ou exécutée. Les variables maxprot et minprot dans la structure sont définies à zéro pour indiquer qu'il n'y a aucun droit de lecture-écriture-exécution sur cette page.- Cette allocation est importante pour atténuer les vulnérabilités de référence de pointeur NULL.
__TEXT
: Contient du code exécutable et des données en lecture seule. Sections communes de ce segment :__text
: Code binaire compilé__const
: Données constantes__cstring
: Constantes de chaîne__stubs
et__stubs_helper
: Impliqués pendant le processus de chargement de bibliothèque dynamique
__DATA
: Contient des données qui sont modifiables.__data
: Variables globales (qui ont été initialisées)__bss
: Variables statiques (qui n'ont pas été initialisées)__objc_*
(__objc_classlist, __objc_protolist, etc) : Informations utilisées par le runtime Objective-C
__LINKEDIT
: Contient des informations pour le linker (dyld) telles que "les entrées de table de symboles, de chaînes et de relocation".__OBJC
: Contient des informations utilisées par le runtime Objective-C. Bien que ces informations puissent également être trouvées dans le segment __DATA, dans diverses sections __objc_*.
LC_MAIN
Contient le point d'entrée dans l'attribut entryoff. Au moment du chargement, dyld ajoute simplement cette valeur à la base du binaire en mémoire, puis saute à cette instruction pour démarrer l'exécution du code binaire.
LC_CODE_SIGNATURE
Contient des informations sur la signature de code du fichier Macho-O. Il ne contient qu'un décalage qui pointe vers le blob de signature. Cela se trouve généralement à la fin du fichier.
LC_LOAD_DYLINKER
Contient le chemin d'accès à l'exécutable du lien dynamique qui mappe les bibliothèques partagées dans l'espace d'adressage du processus. La valeur est toujours définie sur /usr/lib/dyld
. Il est important de noter que sous macOS, le mappage dylib se produit en mode utilisateur, pas en mode noyau.
LC_LOAD_DYLIB
Cette commande de chargement décrit une dépendance de bibliothèque dynamique qui instructe le chargeur (dyld) à charger et lier ladite bibliothèque. Il y a une commande de chargement LC_LOAD_DYLIB pour chaque bibliothèque que le binaire Mach-O requiert.
- Cette commande de chargement est une structure de type
dylib_command
(qui contient une structure dylib, décrivant la bibliothèque dynamique dépendante réelle) :
struct dylib_command {
uint32_t cmd; /* LC_LOAD_{,WEAK_}DYLIB */
uint32_t cmdsize; /* includes pathname string */
struct dylib dylib; /* the library identification */
};
struct dylib {
union lc_str name; /* library's path name */
uint32_t timestamp; /* library's build time stamp */
uint32_t current_version; /* library's current version number */
uint32_t compatibility_version; /* library's compatibility vers number*/
};
Vous pouvez également obtenir ces informations depuis la ligne de commande avec :
otool -L /bin/ls
/bin/ls:
/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
Certaines bibliothèques potentiellement liées à des logiciels malveillants sont :
- DiskArbitration : Surveillance des lecteurs USB
- AVFoundation : Capture audio et vidéo
- CoreWLAN : Analyses Wifi.
{% hint style="info" %}
Un binaire Mach-O peut contenir un ou plusieurs constructeurs, qui seront exécutés avant l'adresse spécifiée dans LC_MAIN.
Les décalages de tous les constructeurs sont contenus dans la section __mod_init_func du segment __DATA_CONST.
{% endhint %}
Données Mach-O
Le cœur du fichier est la région finale, les données, qui se composent de plusieurs segments tels que définis dans la région des commandes de chargement. Chaque segment peut contenir plusieurs sections de données. Chacune de ces sections contient du code ou des données d'un type particulier.
{% hint style="success" %} Les données sont essentiellement la partie contenant toutes les informations chargées par les commandes de chargement LC_SEGMENTS_64. {% endhint %}
Cela inclut :
- Table des fonctions : qui contient des informations sur les fonctions du programme.
- Table des symboles : qui contient des informations sur les fonctions externes utilisées par le binaire.
- Il peut également contenir des noms de fonctions internes, de variables et plus encore.
Pour vérifier cela, vous pouvez utiliser l'outil Mach-O View :
Ou depuis la ligne de commande :
size -m /bin/ls
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT !
- Découvrez The PEASS Family, notre collection exclusive de NFTs
- Obtenez le swag officiel PEASS & HackTricks
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez moi sur Twitter 🐦@carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.