macOS Universal binaries & Mach-O Format
Grundlegende Informationen
Mac OS-Binärdateien sind 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 folgendem besteht:
Header
Ladebefehle
Daten
Fat Header
Suchen Sie nach der 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 wird eine fat_arch
-Struktur haben.
Ü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 ist, normalerweise die Größe einer, die nur für 1 Architektur kompiliert ist.
Mach-O Header
Der Header enthält grundlegende Informationen über die Datei, wie magische Bytes zur Identifizierung als Mach-O-Datei und Informationen über die Zielarchitektur. Sie finden ihn unter: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Mach-O Dateitypen
Es gibt verschiedene Dateitypen, die in der Quellcode-Beispiel hier definiert sind. Die wichtigsten sind:
MH_OBJECT
: Verschiebbares Objektfile (Zwischenprodukte der Kompilierung, noch keine ausführbaren Dateien).MH_EXECUTE
: Ausführbare Dateien.MH_FVMLIB
: Festes VM-Bibliotheksfile.MH_CORE
: Code-DumpsMH_PRELOAD
: Vorab geladene ausführbare Datei (nicht mehr unterstützt in XNU)MH_DYLIB
: Dynamische BibliothekenMH_DYLINKER
: Dynamischer LinkerMH_BUNDLE
: "Plugin-Dateien". Generiert mit -bundle in gcc und explizit geladen vonNSBundle
oderdlopen
.MH_DYSM
: Begleitendes.dSym
-File (File mit Symbolen für Debugging).MH_KEXT_BUNDLE
: Kernel-Erweiterungen.
Oder mit Mach-O View:
Mach-O Flags
Der Quellcode definiert auch mehrere nützliche Flags zum Laden von Bibliotheken:
MH_NOUNDEFS
: Keine undefinierten Verweise (vollständig verknüpft)MH_DYLDLINK
: Dyld-VerknüpfungMH_PREBOUND
: Dynamische Verweise vorab gebunden.MH_SPLIT_SEGS
: Datei teilt r/o- und r/w-Segmente auf.MH_WEAK_DEFINES
: Binärdatei hat schwach definierte SymboleMH_BINDS_TO_WEAK
: Binärdatei verwendet schwache SymboleMH_ALLOW_STACK_EXECUTION
: Den Stack ausführbar machenMH_NO_REEXPORTED_DYLIBS
: Bibliothek ohne 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
: Verwendet auf dylibs/Frameworks im gemeinsamen Bibliotheks-Cache.
Mach-O Load-Befehle
Die Speicherlayout der Datei ist hier festgelegt, wobei der Speicherort der Symboltabelle, der Kontext des Hauptthreads beim Start der Ausführung und die erforderlichen gemeinsam genutzten Bibliotheken beschrieben werden. Anweisungen werden dem dynamischen Loader (dyld) zum Laden des Binärprogramms in den Speicher bereitgestellt.
Es wird die load_command-Struktur verwendet, die in der genannten loader.h
definiert ist:
Es gibt ungefähr 50 verschiedene Arten von Ladungsbefehlen, 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
Grundsätzlich definieren diese Arten von Ladungsbefehlen, wie der __TEXT (ausführbarer Code) und __DATA (Daten für den Prozess) Segmenten entsprechend der Offsets geladen werden, die im Datenabschnitt angegeben sind, wenn die Binärdatei ausgeführt wird.
Diese Befehle definieren Segmente, die in den virtuellen Speicherbereich eines Prozesses abgebildet 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 von dem Prozess verwendete Daten enthält. Diese Segmente befinden sich im Datenabschnitt der Mach-O-Datei.
Jedes Segment kann weiter in mehrere Abschnitte unterteilt werden. Die Ladungsbefehlsstruktur enthält Informationen zu diesen Abschnitten innerhalb des jeweiligen Segments.
Im Header finden Sie zuerst den Segment-Header:
Beispiel für einen Segment-Header:
Dieser Header definiert die Anzahl der Abschnitte, deren Header danach erscheinen:
Beispiel für Abschnittsüberschrift:
Wenn Sie den Abschnittsversatz (0x37DC) + den Versatz hinzufügen, an dem die Architektur beginnt, in diesem Fall 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Es ist auch möglich, Headerinformationen von der Befehlszeile aus abzurufen:
Gemeinsame Segmente, die von diesem Befehl geladen werden:
__PAGEZERO
: Es weist den Kernel an, die Adresse Null so zu zuordnen, dass sie nicht gelesen, geschrieben oder ausgeführt werden kann. Die maxprot- und minprot-Variablen in der Struktur sind auf Null gesetzt, um anzuzeigen, dass es keine Lese-Schreib-Ausführungsrechte auf dieser Seite gibt.Diese Zuweisung ist wichtig, um NULL-Pointer-Dereferenz-Schwachstellen 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ärfile könnte diese Anforderungen erfüllen, indem es ein kleines __PAGEZERO (unter Verwendung der
-pagezero_size
) erstellt, um die ersten 4 KB 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ührungsrechten (nicht schreibbar). Gemeinsame Abschnitte dieses Segments:__text
: Kompilierter Binärcode__const
: Konstante Daten (nur lesbar)__[c/u/os_log]string
: C-, Unicode- oder os-Log-Zeichenfolgenkonstanten__stubs
und__stubs_helper
: Während des dynamischen Bibliotheksladevorgangs involviert__unwind_info
: Stack-Unwind-Daten.
Beachten Sie, dass dieser Inhalt zwar signiert ist, aber auch als ausführbar markiert ist (was mehr Möglichkeiten für die Ausnutzung von Abschnitten schafft, die diese Berechtigung nicht unbedingt benötigen, wie z. B. spezielle Zeichenfolgenabschnitte).
__DATA
: Enthält Daten, die lesbar und schreibbar sind (nicht ausführbar).__got:
Global Offset Table__nl_symbol_ptr
: Nicht träge (bei Laden binden) Symbolzeiger__la_symbol_ptr
: Träge (bei Verwendung binden) Symbolzeiger__const
: Sollte schreibgeschützte Daten sein (nicht wirklich)__cfstring
: CoreFoundation-Zeichenfolgen__data
: Globale Variablen (die initialisiert wurden)__bss
: Statische Variablen (die nicht initialisiert wurden)__objc_*
(__objc_classlist, __objc_protolist, usw.): Informationen, die vom Objective-C-Laufzeitumgebung verwendet werden
__DATA_CONST
: __DATA.__const ist nicht garantiert konstant zu sein (Schreibberechtigungen), ebenso wie andere Zeiger und die GOT. Dieser Abschnitt macht__const
, einige Initialisierer und die GOT-Tabelle (einmal aufgelöst) mitmprotect
schreibgeschützt.__LINKEDIT
: Enthält Informationen für den Linker (dyld) wie Symbol-, Zeichenfolgen- und Relokationstabelleneinträge. Es ist ein generischer Container für Inhalte, die weder in__TEXT
noch in__DATA
sind, und sein Inhalt wird in anderen Ladebefehlen beschrieben.dyld-Informationen: Rebase, Nicht-träge/träge/schwache Bindungsoperationen und Exportinformationen
Funktionsstarts: Tabelle der Startadressen von Funktionen
Daten im Code: Dateninseln in __text
Symboltabelle: Symbole im Binärfile
Indirekte Symboltabelle: Zeiger/Stub-Symbole
Zeichentabelle
Codesignatur
__OBJC
: Enthält Informationen, die von der Objective-C-Laufzeitumgebung verwendet werden. Diese Informationen können auch im __DATA-Segment in verschiedenen __objc_*-Abschnitten gefunden werden.__RESTRICT
: Ein Segment ohne Inhalt mit einem einzigen Abschnitt namens__restrict
(ebenfalls leer), das sicherstellt, dass beim Ausführen des Binärfiles die 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 Core (nicht verwendet)SG_FVMLIB
: Nicht verwendetSG_NORELOC
: Segment hat keine RelokationSG_PROTECTED_VERSION_1
: Verschlüsselung. Wird beispielsweise vom Finder verwendet, um den Text im__TEXT
-Segment zu verschlüsseln.
LC_UNIXTHREAD/LC_MAIN
LC_UNIXTHREAD/LC_MAIN
LC_MAIN
enthält den Einstiegspunkt im entryoff-Attribut. Zur Ladezeit addiert dyld einfach diesen Wert zur (im Speicher befindlichen) Basis des Binärfiles und springt dann zu dieser Anweisung, um die Ausführung des Codes des Binärfiles zu starten.
LC_UNIXTHREAD
enthält die Werte, die die Register haben müssen, wenn der Hauptthread gestartet wird. Dies wurde bereits veraltet, aber dyld
verwendet es immer noch. Es ist möglich, die Werte der Register, die durch dies gesetzt werden, mit anzusehen:
LC_CODE_SIGNATURE
LC_CODE_SIGNATURE
Enthält Informationen zur Codesignatur der Mach-O-Datei. Es enthält nur einen Offset, der auf den Signatur-Blob zeigt. Dies befindet sich normalerweise am Ende der Datei. Sie können jedoch einige Informationen zu diesem Abschnitt in diesem Blog-Beitrag und in diesem Gist finden.
LC_ENCRYPTION_INFO[_64]
LC_ENCRYPTION_INFO[_64]
Unterstützung für die Binärverschlüsselung. Wenn es einem Angreifer jedoch gelingt, den Prozess zu kompromittieren, kann er den Speicher unverschlüsselt ablegen.
LC_LOAD_DYLINKER
LC_LOAD_DYLINKER
Enthält den Pfad zum dynamischen Linker-Programm, das gemeinsam genutzte Bibliotheken in den Adressraum des Prozesses abbildet. Der Wert ist immer auf /usr/lib/dyld
festgelegt. Es ist wichtig zu beachten, dass in macOS das Dylib-Mapping im Benutzermodus und nicht im Kernelmodus erfolgt.
LC_IDENT
LC_IDENT
Veraltet, aber wenn konfiguriert ist, um Dumps bei einem Absturz zu generieren, wird ein Mach-O-Kerndump erstellt und die Kernelversion wird im LC_IDENT
-Befehl festgelegt.
LC_UUID
LC_UUID
Zufällige UUID. Es ist direkt für nichts nützlich, aber XNU speichert es zusammen mit dem Rest der Prozessinformationen im Cache. Es kann in Absturzberichten verwendet werden.
LC_DYLD_ENVIRONMENT
LC_DYLD_ENVIRONMENT
Ermöglicht das Angeben von Umgebungsvariablen für den dyld, bevor der Prozess ausgeführt wird. Dies kann sehr gefährlich sein, da es ermöglichen kann, beliebigen Code innerhalb des Prozesses auszuführen. Daher wird dieser Ladungsbefehl nur in dyld-Builds mit #define SUPPORT_LC_DYLD_ENVIRONMENT
verwendet und beschränkt die Verarbeitung weiterhin nur auf Variablen im Format DYLD_..._PATH
, die Ladepfade angeben.
LC_LOAD_DYLIB
LC_LOAD_DYLIB
Dieser Ladungsbefehl beschreibt eine dynamische Bibliotheksabhängigkeit, die den Loader (dyld) anweist, diese Bibliothek zu laden und zu verknüpfen. Es gibt einen LC_LOAD_DYLIB
-Ladungsbefehl für jede Bibliothek, die die Mach-O-Binärdatei benötigt.
Dieser Ladungsbefehl ist eine Struktur vom Typ
dylib_command
(die eine Struktur dylib enthält, die die tatsächliche abhängige dynamische Bibliothek beschreibt):
Sie könnten diese Informationen auch über die Befehlszeile mit erhalten:
Einige potenziell mit Malware verbundene Bibliotheken sind:
DiskArbitration: Überwachung von USB-Laufwerken
AVFoundation: Aufnahme von Audio und Video
CoreWLAN: Wifi-Scans.
Ein Mach-O-Binärdatei kann einen oder mehrere Konstruktoren enthalten, die vor der in LC_MAIN angegebenen Adresse ausgeführt werden. Die Offsets aller Konstruktoren werden im Abschnitt __mod_init_func des Segments __DATA_CONST gehalten.
Mach-O-Daten
Im Kern der Datei befindet sich der Datenbereich, der aus mehreren Segmenten besteht, wie im Abschnitt der Ladungsbefehle definiert. In jedem Segment können verschiedene Datensektionen untergebracht sein, wobei jede Sektion Code oder Daten spezifisch für einen Typ enthält.
Die Daten sind im Wesentlichen der Teil, der alle Informationen enthält, die von den Ladungsbefehlen LC_SEGMENTS_64 geladen werden.
Dies beinhaltet:
Funktionstabelle: Die Informationen über die Programm-Funktionen enthält.
Symboltabelle: Enthält Informationen über die externen Funktionen, 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:
Oder von der Befehlszeile aus:
Objective-C Gemeinsame Abschnitte
Im __TEXT
Segment (r-x):
__objc_classname
: Klassennamen (Zeichenketten)__objc_methname
: Methodennamen (Zeichenketten)__objc_methtype
: Methodentypen (Zeichenketten)
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
: Protokolliste__objc_const
: Konstante Daten__objc_imageinfo
,__objc_selrefs
,objc__protorefs
...
Swift
_swift_typeref
,_swift3_capture
,_swift3_assocty
,_swift3_types, _swift3_proto
,_swift3_fieldmd
,_swift3_builtin
,_swift3_reflstr
Last updated