macOS Universal-Binaries & Mach-O-Format
Reading time: 14 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Grundinformationen
Mac OS-Binaries werden normalerweise als universelle Binaries kompiliert. Ein universelles Binary kann mehrere Architekturen in derselben Datei unterstützen.
Diese Binaries folgen der Mach-O-Struktur, die im Wesentlichen aus besteht:
- Header
- Ladebefehle
- Daten
Fat Header
Suchen Sie die Datei mit: 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 oder FAT_MAGIC_64 */
uint32_t nfat_arch; /* Anzahl der folgenden Strukturen */
};
struct fat_arch {
cpu_type_t cputype; /* CPU-Spezifizierer (int) */
cpu_subtype_t cpusubtype; /* Maschinen-Spezifizierer (int) */
uint32_t offset; /* Dateiverschiebung zu dieser Objektdatei */
uint32_t size; /* Größe dieser Objektdatei */
uint32_t align; /* Ausrichtung als Potenz von 2 */
};
Der Header enthält die magischen Bytes, gefolgt von der Anzahl der Architekturen, die die Datei enthält (nfat_arch
), und jede Architektur hat eine fat_arch
-Struktur.
Überprüfen Sie es mit:
% file /bin/ls
/bin/ls: Mach-O universelles Binary mit 2 Architekturen: [x86_64:Mach-O 64-Bit ausführbar x86_64] [arm64e:Mach-O 64-Bit ausführbar arm64e]
/bin/ls (für Architektur x86_64): Mach-O 64-Bit ausführbar x86_64
/bin/ls (für Architektur arm64e): Mach-O 64-Bit ausführbar arm64e
% otool -f -v /bin/ls
Fat-Header
fat_magic FAT_MAGIC
nfat_arch 2
architektur x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
offset 16384
size 72896
align 2^14 (16384)
architektur arm64e
cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
offset 98304
size 88816
align 2^14 (16384)
oder mit dem Mach-O View Tool:
.png)
Wie Sie vielleicht denken, verdoppelt ein universelles Binary, das für 2 Architekturen kompiliert wurde, normalerweise die Größe eines, das nur für 1 Architektur kompiliert wurde.
Mach-O Header
Der Header enthält grundlegende Informationen über die Datei, wie magische Bytes, um sie als Mach-O-Datei zu identifizieren, und Informationen über die Zielarchitektur. Sie finden es unter: 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 */
};
Mach-O-Dateitypen
Es gibt verschiedene Dateitypen, die in dem Quellcode zum Beispiel hier definiert sind. Die wichtigsten sind:
MH_OBJECT
: Umsetzbare Objektdatei (Zwischenprodukte der Kompilierung, noch keine ausführbaren Dateien).MH_EXECUTE
: Ausführbare Dateien.MH_FVMLIB
: Feste VM-Bibliotheksdatei.MH_CORE
: Code-DumpsMH_PRELOAD
: Vorgelegte ausführbare Datei (wird in XNU nicht mehr unterstützt)MH_DYLIB
: Dynamische BibliothekenMH_DYLINKER
: Dynamischer LinkerMH_BUNDLE
: "Plugin-Dateien". Generiert mit -bundle in gcc und explizit geladen vonNSBundle
oderdlopen
.MH_DYSM
: Begleitende.dSym
-Datei (Datei mit Symbolen für das Debugging).MH_KEXT_BUNDLE
: Kernel-Erweiterungen.
# 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
Oder verwenden Sie Mach-O View:
.png)
Mach-O-Flags
Der Quellcode definiert auch mehrere Flags, die nützlich sind, um Bibliotheken zu laden:
MH_NOUNDEFS
: Keine undefinierten Referenzen (vollständig verlinkt)MH_DYLDLINK
: Dyld-VerlinkungMH_PREBOUND
: Dynamische Referenzen vorgebunden.MH_SPLIT_SEGS
: Datei teilt r/o und r/w Segmente.MH_WEAK_DEFINES
: Binärdatei hat schwach definierte SymboleMH_BINDS_TO_WEAK
: Binärdatei verwendet schwache SymboleMH_ALLOW_STACK_EXECUTION
: Machen Sie den Stack ausführbarMH_NO_REEXPORTED_DYLIBS
: Bibliothek hat keine LC_REEXPORT-BefehleMH_PIE
: Positionsunabhängige ausführbare DateiMH_HAS_TLV_DESCRIPTORS
: Es gibt einen Abschnitt mit thread-lokalen VariablenMH_NO_HEAP_EXECUTION
: Keine Ausführung für Heap/Daten-SeitenMH_HAS_OBJC
: Binärdatei hat oBject-C-AbschnitteMH_SIM_SUPPORT
: SimulatorunterstützungMH_DYLIB_IN_CACHE
: Wird bei dylibs/Frameworks im gemeinsamen Bibliothekscache verwendet.
Mach-O-Ladebefehle
Das Layout der Datei im Speicher wird hier spezifiziert, wobei der Standort der Symboltabelle, der Kontext des Hauptthreads beim Start der Ausführung und die erforderlichen gemeinsamen Bibliotheken detailliert beschrieben werden. Anweisungen werden dem dynamischen Loader (dyld) zum Ladeprozess der Binärdatei in den Speicher bereitgestellt.
Es verwendet die load_command-Struktur, die in der erwähnten loader.h
definiert ist:
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
Es gibt etwa 50 verschiedene Arten von Load Commands, die das System unterschiedlich behandelt. Die häufigsten sind: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
und LC_CODE_SIGNATURE
.
LC_SEGMENT/LC_SEGMENT_64
tip
Grundsätzlich definiert dieser Typ von Load Command wie man die __TEXT (ausführbarer Code) und __DATA (Daten für den Prozess) Segmente gemäß den Offsets, die im Datenbereich angegeben sind, wenn die Binärdatei ausgeführt wird.
Diese Befehle definieren Segmente, die in den virtuellen Adressraum eines Prozesses gemappt werden, wenn er ausgeführt wird.
Es gibt verschiedene Arten von Segmenten, wie das __TEXT-Segment, das den ausführbaren Code eines Programms enthält, und das __DATA-Segment, das Daten enthält, die vom Prozess verwendet werden. Diese Segmente befinden sich im Datenbereich der Mach-O-Datei.
Jedes Segment kann weiter in mehrere Sektionen unterteilt werden. Die Struktur des Load Commands enthält Informationen über diese Sektionen innerhalb des jeweiligen Segments.
Im Header finden Sie zuerst den Segment-Header:
struct segment_command_64 { /* für 64-Bit-Architekturen */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* umfasst sizeof section_64 Strukturen */
char segname[16]; /* Segmentname */
uint64_t vmaddr; /* Speicheradresse dieses Segments */
uint64_t vmsize; /* Speichergröße dieses Segments */
uint64_t fileoff; /* Dateiverschiebung dieses Segments */
uint64_t filesize; /* Menge, die aus der Datei gemappt werden soll */
int32_t maxprot; /* maximale VM-Schutz */
int32_t initprot; /* anfänglicher VM-Schutz */
uint32_t nsects; /* Anzahl der Sektionen im Segment */
uint32_t flags; /* Flags */
};
Beispiel eines Segment-Headers:
.png)
Dieser Header definiert die Anzahl der Sektionen, deren Header danach erscheinen:
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 */
};
Beispiel für Abschnittsüberschrift:
.png)
Wenn Sie den Abschnittsversatz (0x37DC) + den Versatz, wo der Arch beginnt, in diesem Fall 0x18000
hinzufügen --> 0x37DC + 0x18000 = 0x1B7DC
.png)
Es ist auch möglich, Header-Informationen über die Befehlszeile zu erhalten mit:
otool -lv /bin/ls
Häufige Segmente, die von diesem Befehl geladen werden:
__PAGEZERO
: Es weist den Kernel an, die Adresse null zu mappen, sodass sie nicht gelesen, beschrieben oder ausgeführt werden kann. Die Variablen maxprot und minprot in der Struktur sind auf null gesetzt, um anzuzeigen, dass es keine Lese-, Schreib- oder Ausführungsrechte auf dieser Seite gibt.- Diese Zuweisung ist wichtig, um NULL-Zeiger-Dereferenzierungsanfälligkeiten zu mildern. Dies liegt daran, dass XNU eine harte Seite null durchsetzt, die sicherstellt, dass die erste Seite (nur die erste) des Speichers unzugänglich ist (außer in i386). Ein Binärprogramm könnte diese Anforderungen erfüllen, indem es eine kleine __PAGEZERO erstellt (unter Verwendung von
-pagezero_size
), um die ersten 4k abzudecken und den Rest des 32-Bit-Speichers sowohl im Benutzer- als auch im Kernelmodus zugänglich zu machen. __TEXT
: Enthält ausführbaren Code mit Lese- und Ausführungsberechtigungen (kein schreibbarer). Häufige Abschnitte dieses Segments:__text
: Kompilierter Binärcode__const
: Konstanten Daten (nur lesbar)__[c/u/os_log]string
: C-, Unicode- oder os-Log-String-Konstanten__stubs
und__stubs_helper
: Beteiligt am Prozess des Ladens dynamischer Bibliotheken__unwind_info
: Stack-Unwind-Daten.- Beachten Sie, dass all dieser Inhalt signiert, aber auch als ausführbar markiert ist (was mehr Optionen für die Ausnutzung von Abschnitten schafft, die nicht unbedingt dieses Privileg benötigen, wie z.B. stringdedizierte Abschnitte).
__DATA
: Enthält Daten, die lesbar und schreibbar sind (nicht ausführbar).__got:
Globale Offset-Tabelle__nl_symbol_ptr
: Nicht faul (Bindung beim Laden) Symbolzeiger__la_symbol_ptr
: Faul (Bindung bei Verwendung) Symbolzeiger__const
: Soll schreibgeschützte Daten sein (nicht wirklich)__cfstring
: CoreFoundation-Strings__data
: Globale Variablen (die initialisiert wurden)__bss
: Statische Variablen (die nicht initialisiert wurden)__objc_*
(__objc_classlist, __objc_protolist usw.): Informationen, die von der Objective-C-Laufzeit verwendet werden__DATA_CONST
: __DATA.__const ist nicht garantiert konstant (Schreibberechtigungen), noch sind andere Zeiger und die GOT. Dieser Abschnitt macht__const
, einige Initialisierer und die GOT-Tabelle (einmal aufgelöst) schreibgeschützt mitmprotect
.__LINKEDIT
: Enthält Informationen für den Linker (dyld), wie z.B. Symbol-, String- und Relocation-Tabelleneinträge. Es ist ein generischer Container für Inhalte, die sich weder in__TEXT
noch in__DATA
befinden, und dessen Inhalt in anderen Ladebefehlen beschrieben wird.- dyld-Informationen: Rebase, Nicht-faule/faul/schwache Bindungs-Opcode und Exportinformationen
- Funktionsstarts: Tabelle der Startadressen von Funktionen
- Daten im Code: Dateninseln in __text
- Symboltabelle: Symbole im Binärformat
- Indirekte Symboltabelle: Zeiger/stub-Symbole
- Zeichenfolgen-Tabelle
- Codesignatur
__OBJC
: Enthält Informationen, die von der Objective-C-Laufzeit verwendet werden. Obwohl diese Informationen auch im __DATA-Segment gefunden werden können, innerhalb verschiedener __objc_* Abschnitte.__RESTRICT
: Ein Segment ohne Inhalt mit einem einzigen Abschnitt namens__restrict
(auch leer), das sicherstellt, dass beim Ausführen des Binärprogramms DYLD-Umgebungsvariablen ignoriert werden.
Wie im Code zu sehen war, unterstützen Segmente auch Flags (obwohl sie nicht sehr häufig verwendet werden):
SG_HIGHVM
: Nur Kern (nicht verwendet)SG_FVMLIB
: Nicht verwendetSG_NORELOC
: Segment hat keine RelokationSG_PROTECTED_VERSION_1
: Verschlüsselung. Wird beispielsweise vom Finder verwendet, um den Text des__TEXT
-Segments zu verschlüsseln.
LC_UNIXTHREAD/LC_MAIN
LC_MAIN
enthält den Einstiegspunkt im entryoff-Attribut. Zur Ladezeit fügt dyld einfach diesen Wert zur (im Speicher) Basis des Binärprogramms hinzu und springt dann zu dieser Anweisung, um die Ausführung des Codes des Binärprogramms zu starten.
LC_UNIXTHREAD
enthält die Werte, die die Register haben müssen, wenn der Hauptthread gestartet wird. Dies wurde bereits als veraltet markiert, aber dyld
verwendet es immer noch. Es ist möglich, die von diesem gesetzten Registerwerte zu sehen mit:
otool -l /usr/lib/dyld
[...]
Load command 13
cmd LC_UNIXTHREAD
cmdsize 288
flavor ARM_THREAD_STATE64
count ARM_THREAD_STATE64_COUNT
x0 0x0000000000000000 x1 0x0000000000000000 x2 0x0000000000000000
x3 0x0000000000000000 x4 0x0000000000000000 x5 0x0000000000000000
x6 0x0000000000000000 x7 0x0000000000000000 x8 0x0000000000000000
x9 0x0000000000000000 x10 0x0000000000000000 x11 0x0000000000000000
x12 0x0000000000000000 x13 0x0000000000000000 x14 0x0000000000000000
x15 0x0000000000000000 x16 0x0000000000000000 x17 0x0000000000000000
x18 0x0000000000000000 x19 0x0000000000000000 x20 0x0000000000000000
x21 0x0000000000000000 x22 0x0000000000000000 x23 0x0000000000000000
x24 0x0000000000000000 x25 0x0000000000000000 x26 0x0000000000000000
x27 0x0000000000000000 x28 0x0000000000000000 fp 0x0000000000000000
lr 0x0000000000000000 sp 0x0000000000000000 pc 0x0000000000004b70
cpsr 0x00000000
[...]
LC_CODE_SIGNATURE
Enthält Informationen über die Code-Signatur der Macho-O-Datei. Es enthält nur einen Offset, der auf den Signatur-Blob zeigt. Dies befindet sich typischerweise am Ende der Datei.
Sie können jedoch einige Informationen zu diesem Abschnitt in diesem Blogbeitrag und diesen Gists finden.
LC_ENCRYPTION_INFO[_64]
Unterstützung für die binäre Verschlüsselung. Wenn ein Angreifer jedoch den Prozess kompromittiert, kann er den Speicher unverschlüsselt dumpen.
LC_LOAD_DYLINKER
Enthält den Pfad zur dynamischen Linker-Executable, die gemeinsam genutzte Bibliotheken in den Adressraum des Prozesses einbindet. Der Wert ist immer auf /usr/lib/dyld
gesetzt. Es ist wichtig zu beachten, dass in macOS das Dylib-Mapping im Benutzermodus und nicht im Kernelmodus erfolgt.
LC_IDENT
Veraltet, aber wenn konfiguriert, um Dumps bei einem Panic zu erzeugen, wird ein Mach-O-Core-Dump erstellt und die Kernelversion wird im LC_IDENT
-Befehl gesetzt.
LC_UUID
Zufällige UUID. Es ist nützlich für alles direkt, aber XNU cached es mit dem Rest der Prozessinformationen. Es kann in Absturzberichten verwendet werden.
LC_DYLD_ENVIRONMENT
Erlaubt es, Umgebungsvariablen an den dyld anzugeben, bevor der Prozess ausgeführt wird. Dies kann sehr gefährlich sein, da es die Ausführung beliebigen Codes innerhalb des Prozesses ermöglichen kann, sodass dieser Ladebefehl nur in dyld-Bauten mit #define SUPPORT_LC_DYLD_ENVIRONMENT
verwendet wird und die Verarbeitung weiter auf Variablen der Form DYLD_..._PATH
beschränkt ist, die Ladepfade angeben.
LC_LOAD_DYLIB
Dieser Ladebefehl beschreibt eine dynamische Bibliotheks-Abhängigkeit, die den Loader (dyld) anweist, die angegebene Bibliothek zu laden und zu verlinken. Es gibt einen LC_LOAD_DYLIB
-Ladebefehl für jede Bibliothek, die die Mach-O-Binärdatei benötigt.
- Dieser Ladebefehl ist eine Struktur vom Typ
dylib_command
(die eine Struktur dylib enthält, die die tatsächliche abhängige dynamische Bibliothek beschreibt):
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*/
};
Sie können diese Informationen auch über die CLI erhalten mit:
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)
Einige potenzielle malwarebezogene Bibliotheken sind:
- DiskArbitration: Überwachung von USB-Laufwerken
- AVFoundation: Audio und Video erfassen
- CoreWLAN: Wifi-Scans.
note
Eine Mach-O-Binärdatei kann einen oder mehrere Konstruktoren enthalten, die ausgeführt werden, bevor die Adresse, die in LC_MAIN angegeben ist, erreicht wird.
Die Offsets aller Konstruktoren befinden sich im Abschnitt __mod_init_func des Segments __DATA_CONST.
Mach-O-Daten
Im Kern der Datei liegt der Datenbereich, der aus mehreren Segmenten besteht, wie im Bereich der Ladebefehle definiert. Eine Vielzahl von Datensektionen kann innerhalb jedes Segments untergebracht werden, wobei jede Sektion Code oder Daten enthält, die spezifisch für einen Typ sind.
tip
Die Daten sind im Grunde der Teil, der alle Informationen enthält, die durch die Ladebefehle LC_SEGMENTS_64 geladen werden.
Dazu gehören:
- Funktionstabelle: Die Informationen über die Programmfunktionen enthält.
- Symboltabelle: Die Informationen über die externen Funktionen enthält, die von der Binärdatei verwendet werden.
- Es könnte auch interne Funktionen, Variablennamen und mehr enthalten.
Um dies zu überprüfen, könnten Sie das Mach-O View Tool verwenden:
.png)
Oder über die CLI:
size -m /bin/ls
Objetive-C Gemeinsame Abschnitte
Im __TEXT
Segment (r-x):
__objc_classname
: Klassennamen (Strings)__objc_methname
: Methodennamen (Strings)__objc_methtype
: Methodentypen (Strings)
Im __DATA
Segment (rw-):
__objc_classlist
: Zeiger auf alle Objective-C Klassen__objc_nlclslist
: Zeiger auf Nicht-Lazy Objective-C Klassen__objc_catlist
: Zeiger auf Kategorien__objc_nlcatlist
: Zeiger auf Nicht-Lazy Kategorien__objc_protolist
: Protokollliste__objc_const
: Konstanten Daten__objc_imageinfo
,__objc_selrefs
,objc__protorefs
...
Swift
_swift_typeref
,_swift3_capture
,_swift3_assocty
,_swift3_types, _swift3_proto
,_swift3_fieldmd
,_swift3_builtin
,_swift3_reflstr
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.