macOS Universal binaries & Mach-O Format
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Mac OS-Binärdateien werden normalerweise als universelle Binärdateien kompiliert. Eine universelle Binärdatei kann mehrere Architekturen in derselben Datei unterstützen.
Diese Binärdateien folgen der Mach-O-Struktur, die im Wesentlichen aus besteht:
Header
Ladebefehle
Daten
Suchen Sie die Datei mit: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
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:
oder mit dem Mach-O View Tool:
Wie Sie vielleicht denken, verdoppelt eine universelle Binärdatei, die für 2 Architekturen kompiliert wurde, normalerweise die Größe einer, die nur für 1 Architektur kompiliert wurde.
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$"
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-Dumps
MH_PRELOAD
: Vorgebundene ausführbare Datei (wird in XNU nicht mehr unterstützt)
MH_DYLIB
: Dynamische Bibliotheken
MH_DYLINKER
: Dynamischer Linker
MH_BUNDLE
: "Plugin-Dateien". Generiert mit -bundle in gcc und explizit geladen von NSBundle
oder dlopen
.
MH_DYSM
: Begleitende .dSym
-Datei (Datei mit Symbolen für das Debugging).
MH_KEXT_BUNDLE
: Kernel-Erweiterungen.
Or using Mach-O View:
Der Quellcode definiert auch mehrere Flags, die nützlich für das Laden von Bibliotheken sind:
MH_NOUNDEFS
: Keine undefinierten Referenzen (vollständig verlinkt)
MH_DYLDLINK
: Dyld-Verlinkung
MH_PREBOUND
: Dynamische Referenzen vorgebunden.
MH_SPLIT_SEGS
: Datei trennt r/o und r/w Segmente.
MH_WEAK_DEFINES
: Binärdatei hat schwach definierte Symbole
MH_BINDS_TO_WEAK
: Binärdatei verwendet schwache Symbole
MH_ALLOW_STACK_EXECUTION
: Machen Sie den Stack ausführbar
MH_NO_REEXPORTED_DYLIBS
: Bibliothek hat keine LC_REEXPORT-Befehle
MH_PIE
: Positionsunabhängige ausführbare Datei
MH_HAS_TLV_DESCRIPTORS
: Es gibt einen Abschnitt mit thread-lokalen Variablen
MH_NO_HEAP_EXECUTION
: Keine Ausführung für Heap/Daten-Seiten
MH_HAS_OBJC
: Binärdatei hat oBject-C-Abschnitte
MH_SIM_SUPPORT
: Simulatorunterstützung
MH_DYLIB_IN_CACHE
: Wird bei dylibs/Frameworks im gemeinsamen Bibliothekscache verwendet.
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.
Die verwendet die load_command-Struktur, die in der erwähnten loader.h
definiert ist:
Es gibt etwa 50 verschiedene Arten von Ladebefehlen, die das System unterschiedlich behandelt. Die häufigsten sind: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
und LC_CODE_SIGNATURE
.
Grundsätzlich definiert dieser Typ von Ladebefehl wie 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 Ladebefehlsstruktur enthält Informationen über diese Sektionen innerhalb des jeweiligen Segments.
Im Header finden Sie zuerst den Segment-Header:
Beispiel eines Segment-Headers:
Dieser Header definiert die Anzahl der Sektionen, deren Header nach ihm erscheinen:
Beispiel für Abschnittsüberschrift:
Wenn Sie den Abschnittsoffset (0x37DC) + den Offset, wo der Arch beginnt, in diesem Fall 0x18000
hinzufügen --> 0x37DC + 0x18000 = 0x1B7DC
Es ist auch möglich, Header-Informationen über die Befehlszeile zu erhalten mit:
Common segments loaded by this cmd:
__PAGEZERO
: Es weist den Kernel an, die Adresse null zu mappen, sodass sie nicht gelesen, beschrieben oder ausgeführt werden kann. Die maxprot- und minprot-Variablen 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 diese Berechtigung nicht unbedingt 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 mit mprotect
.
__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 sein Inhalt wird in anderen Ladebefehlen beschrieben.
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
(ebenfalls 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 verwendet
SG_NORELOC
: Segment hat keine Relokation
SG_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:
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. Allerdings finden Sie einige Informationen zu diesem Abschnitt in diesem Blogbeitrag und diesem Gist.
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-Ausführungsdatei, 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 es so konfiguriert ist, dass es Dumps bei einem Panic generiert, 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ächlich abhängige dynamische Bibliothek beschreibt):
Sie könnten diese Informationen auch über die CLI erhalten mit:
Einige potenzielle mit Malware verbundene Bibliotheken sind:
DiskArbitration: Überwachung von USB-Laufwerken
AVFoundation: Audio und Video erfassen
CoreWLAN: Wifi-Scans.
Ein Mach-O-Binary kann einen oder mehrere Konstruktoren enthalten, die ausgeführt werden, bevor die Adresse, die in LC_MAIN angegeben ist. Die Offsets der Konstruktoren werden im Abschnitt __mod_init_func des Segments __DATA_CONST gespeichert.
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.
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 Binary 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:
Oder von der CLI:
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_typeref
, _swift3_capture
, _swift3_assocty
, _swift3_types, _swift3_proto
, _swift3_fieldmd
, _swift3_builtin
, _swift3_reflstr
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)