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)
Binarne pliki Mac OS są zazwyczaj kompilowane jako universal binaries. Universal binary może obsługiwać wiele architektur w tym samym pliku.
Te binarne pliki mają strukturę Mach-O, która składa się zasadniczo z:
Nagłówka
Komend ładowania
Danych
Szukaj pliku za pomocą: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
Nagłówek zawiera magiczne bajty, po których następuje liczba architektur, które plik zawiera (nfat_arch
), a każda architektura będzie miała strukturę fat_arch
.
Sprawdź to za pomocą:
lub używając narzędzia Mach-O View:
Jak możesz się domyślać, zazwyczaj uniwersalny plik binarny skompilowany dla 2 architektur podwaja rozmiar pliku skompilowanego tylko dla 1 arch.
Nagłówek zawiera podstawowe informacje o pliku, takie jak magiczne bajty identyfikujące go jako plik Mach-O oraz informacje o docelowej architekturze. Możesz go znaleźć w: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Istnieją różne typy plików, które można znaleźć zdefiniowane w kodzie źródłowym, na przykład tutaj. Najważniejsze z nich to:
MH_OBJECT
: Przenośny plik obiektowy (produkty pośrednie kompilacji, jeszcze nie wykonywalne).
MH_EXECUTE
: Pliki wykonywalne.
MH_FVMLIB
: Stały plik biblioteki VM.
MH_CORE
: Zrzuty kodu
MH_PRELOAD
: Wstępnie załadowany plik wykonywalny (już nieobsługiwany w XNU)
MH_DYLIB
: Biblioteki dynamiczne
MH_DYLINKER
: Ładowarka dynamiczna
MH_BUNDLE
: "Pliki wtyczek". Generowane za pomocą -bundle w gcc i ładowane explicite przez NSBundle
lub dlopen
.
MH_DYSM
: Towarzyszący plik .dSym
(plik z symbolami do debugowania).
MH_KEXT_BUNDLE
: Rozszerzenia jądra.
Or using Mach-O View:
Kod źródłowy definiuje również kilka flag przydatnych do ładowania bibliotek:
MH_NOUNDEFS
: Brak niezdefiniowanych odniesień (w pełni powiązane)
MH_DYLDLINK
: Łączenie Dyld
MH_PREBOUND
: Dynamiczne odniesienia wstępnie powiązane.
MH_SPLIT_SEGS
: Plik dzieli segmenty r/o i r/w.
MH_WEAK_DEFINES
: Plik binarny ma słabo zdefiniowane symbole
MH_BINDS_TO_WEAK
: Plik binarny używa słabych symboli
MH_ALLOW_STACK_EXECUTION
: Umożliwia wykonanie na stosie
MH_NO_REEXPORTED_DYLIBS
: Biblioteka nie ma poleceń LC_REEXPORT
MH_PIE
: Wykonywalny niezależny od pozycji
MH_HAS_TLV_DESCRIPTORS
: Istnieje sekcja z lokalnymi zmiennymi wątku
MH_NO_HEAP_EXECUTION
: Brak wykonania dla stron sterty/danych
MH_HAS_OBJC
: Plik binarny ma sekcje oBject-C
MH_SIM_SUPPORT
: Wsparcie dla symulatora
MH_DYLIB_IN_CACHE
: Używane w dylibach/frameworkach w pamięci podręcznej biblioteki współdzielonej.
Układ pliku w pamięci jest określony tutaj, szczegółowo opisując lokalizację tabeli symboli, kontekst głównego wątku na początku wykonania oraz wymagane biblioteki współdzielone. Instrukcje są przekazywane do dynamicznego ładowacza (dyld) dotyczące procesu ładowania pliku binarnego do pamięci.
Używa struktury load_command, zdefiniowanej w wspomnianym loader.h
:
There are about 50 different types of load commands that the system handles differently. The most common ones are: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
, and LC_CODE_SIGNATURE
.
W zasadzie ten typ polecenia ładującego definiuje jak załadować __TEXT (kod wykonywalny) i __DATA (dane dla procesu) segmenty zgodnie z offsetami wskazanymi w sekcji danych podczas wykonywania binarnego.
Te polecenia definiują segmenty, które są mapowane do przestrzeni pamięci wirtualnej procesu, gdy jest on wykonywany.
Istnieją różne typy segmentów, takie jak segment __TEXT, który zawiera kod wykonywalny programu, oraz segment __DATA, który zawiera dane używane przez proces. Te segmenty znajdują się w sekcji danych pliku Mach-O.
Każdy segment może być dalej podzielony na wiele sekcji. Struktura polecenia ładującego zawiera informacje o tych sekcjach w odpowiednim segmencie.
W nagłówku najpierw znajdziesz nagłówek segmentu:
Example of segment header:
This header defines the number of sections whose headers appear after it:
Przykład nagłówka sekcji:
Jeśli dodasz offset sekcji (0x37DC) + offset, w którym arch zaczyna się, w tym przypadku 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Możliwe jest również uzyskanie informacji o nagłówkach z wiersza poleceń za pomocą:
Common segments loaded by this cmd:
__PAGEZERO
: Instrukcja dla jądra, aby mapować adres zero, aby nie można go było odczytać, zapisać ani wykonać. Zmienne maxprot i minprot w strukturze są ustawione na zero, aby wskazać, że nie ma praw do odczytu-zapisu-wykonania na tej stronie.
Ta alokacja jest ważna, aby złagodzić podatności na dereferencję wskaźnika NULL. Dzieje się tak, ponieważ XNU egzekwuje twardą stronę zero, która zapewnia, że pierwsza strona (tylko pierwsza) pamięci jest niedostępna (z wyjątkiem i386). Plik binarny może spełniać te wymagania, tworząc mały __PAGEZERO (używając -pagezero_size
), aby pokryć pierwsze 4k, a reszta pamięci 32-bitowej była dostępna zarówno w trybie użytkownika, jak i jądra.
__TEXT
: Zawiera wykonywalny kod z uprawnieniami do odczytu i wykonywania (brak zapisu). Typowe sekcje tego segmentu:
__text
: Skonstruowany kod binarny
__const
: Dane stałe (tylko do odczytu)
__[c/u/os_log]string
: Stałe ciągi C, Unicode lub os logów
__stubs
i __stubs_helper
: Uczestniczą w procesie ładowania biblioteki dynamicznej
__unwind_info
: Dane o rozwijaniu stosu.
Zauważ, że cała ta zawartość jest podpisana, ale także oznaczona jako wykonywalna (tworząc więcej opcji do wykorzystania sekcji, które niekoniecznie potrzebują tego przywileju, jak sekcje dedykowane ciągom).
__DATA
: Zawiera dane, które są czytelne i zapisywalne (brak wykonywalnych).
__got:
Tabela Global Offset
__nl_symbol_ptr
: Wskaźnik symbolu non lazy (wiąż w czasie ładowania)
__la_symbol_ptr
: Wskaźnik symbolu lazy (wiąż przy użyciu)
__const
: Powinny być danymi tylko do odczytu (nie do końca)
__cfstring
: Ciągi CoreFoundation
__data
: Zmienne globalne (które zostały zainicjowane)
__bss
: Zmienne statyczne (które nie zostały zainicjowane)
__objc_*
(__objc_classlist, __objc_protolist, itd): Informacje używane przez środowisko wykonawcze Objective-C
__DATA_CONST
: __DATA.__const nie jest gwarantowane jako stałe (uprawnienia do zapisu), ani inne wskaźniki i GOT. Ta sekcja sprawia, że __const
, niektóre inicjalizatory i tabela GOT (po rozwiązaniu) są tylko do odczytu przy użyciu mprotect
.
__LINKEDIT
: Zawiera informacje dla linkera (dyld), takie jak symbole, ciągi i wpisy tabeli relokacji. Jest to ogólny kontener dla treści, które nie znajdują się w __TEXT
ani __DATA
, a jego zawartość jest opisana w innych poleceniach ładowania.
Informacje dyld: Rebase, Non-lazy/lazy/weak binding opcodes i informacje o eksporcie
Funkcje startowe: Tabela adresów startowych funkcji
Dane w kodzie: Wyspy danych w __text
Tabela symboli: Symbole w binarnym
Tabela symboli pośrednich: Wskaźniki/symbole stub
Tabela ciągów
Podpis kodu
__OBJC
: Zawiera informacje używane przez środowisko wykonawcze Objective-C. Chociaż te informacje mogą być również znalezione w segmencie __DATA, w różnych sekcjach __objc_*.
__RESTRICT
: Segment bez zawartości z jedną sekcją o nazwie __restrict
(również pusta), która zapewnia, że podczas uruchamiania binarnego zignoruje zmienne środowiskowe DYLD.
Jak można było zobaczyć w kodzie, segmenty również obsługują flagi (chociaż nie są zbyt często używane):
SG_HIGHVM
: Tylko rdzeń (nieużywane)
SG_FVMLIB
: Nie używane
SG_NORELOC
: Segment nie ma relokacji
SG_PROTECTED_VERSION_1
: Szyfrowanie. Używane na przykład przez Findera do szyfrowania tekstu w segmencie __TEXT
.
LC_UNIXTHREAD/LC_MAIN
LC_MAIN
zawiera punkt wejścia w atrybucie entryoff. W czasie ładowania, dyld po prostu dodaje tę wartość do (w pamięci) bazy binarnej, a następnie przechodzi do tej instrukcji, aby rozpocząć wykonanie kodu binarnego.
LC_UNIXTHREAD
zawiera wartości, jakie rejestry muszą mieć przy uruchamianiu głównego wątku. To już zostało wycofane, ale dyld
wciąż to używa. Można zobaczyć wartości rejestrów ustawione przez to za pomocą:
LC_CODE_SIGNATURE
Zawiera informacje o podpisie kodu pliku Macho-O. Zawiera tylko offset, który wskazuje na blob podpisu. Zazwyczaj znajduje się on na samym końcu pliku. Jednak można znaleźć pewne informacje na temat tej sekcji w tym wpisie na blogu oraz w tym gists.
LC_ENCRYPTION_INFO[_64]
Wsparcie dla szyfrowania binarnego. Jednak, oczywiście, jeśli atakujący zdoła skompromitować proces, będzie mógł zrzucić pamięć w postaci nieszyfrowanej.
LC_LOAD_DYLINKER
Zawiera ścieżkę do wykonywalnego pliku dynamicznego linkera, który mapuje biblioteki współdzielone w przestrzeni adresowej procesu. Wartość jest zawsze ustawiona na /usr/lib/dyld
. Ważne jest, aby zauważyć, że w macOS mapowanie dylib odbywa się w trybie użytkownika, a nie w trybie jądra.
LC_IDENT
Nieaktualne, ale gdy jest skonfigurowane do generowania zrzutów w przypadku paniki, tworzony jest zrzut rdzenia Mach-O, a wersja jądra jest ustawiana w poleceniu LC_IDENT
.
LC_UUID
Losowy UUID. Jest przydatny do czegokolwiek bezpośrednio, ale XNU buforuje go z resztą informacji o procesie. Może być używany w raportach o awariach.
LC_DYLD_ENVIRONMENT
Pozwala wskazać zmienne środowiskowe dla dyld przed wykonaniem procesu. Może to być bardzo niebezpieczne, ponieważ może pozwolić na wykonanie dowolnego kodu wewnątrz procesu, więc to polecenie ładowania jest używane tylko w dyld zbudowanym z #define SUPPORT_LC_DYLD_ENVIRONMENT
i dodatkowo ogranicza przetwarzanie tylko do zmiennych w formie DYLD_..._PATH
określających ścieżki ładowania.
LC_LOAD_DYLIB
To polecenie ładowania opisuje zależność od dynamicznej biblioteki, która instrukuje ładowarkę (dyld) do załadowania i powiązania wspomnianej biblioteki. Istnieje polecenie ładowania LC_LOAD_DYLIB
dla każdej biblioteki, której wymaga binarny plik Mach-O.
To polecenie ładowania jest strukturą typu dylib_command
(która zawiera strukturę dylib, opisującą rzeczywistą zależną dynamiczną bibliotekę):
Możesz również uzyskać te informacje z wiersza poleceń za pomocą:
Some potential malware related libraries are:
DiskArbitration: Monitorowanie dysków USB
AVFoundation: Przechwytywanie audio i wideo
CoreWLAN: Skanowanie Wifi.
Binarne Mach-O może zawierać jeden lub więcej konstruktorów, które będą wykonywane przed adresem określonym w LC_MAIN. Offsety wszelkich konstruktorów są przechowywane w sekcji __mod_init_func segmentu __DATA_CONST.
W rdzeniu pliku znajduje się region danych, który składa się z kilku segmentów, jak zdefiniowano w regionie poleceń ładujących. W każdym segmencie może być przechowywanych wiele sekcji danych, z każdą sekcją zawierającą kod lub dane specyficzne dla danego typu.
Dane to zasadniczo część zawierająca wszystkie informacje, które są ładowane przez polecenia ładujące LC_SEGMENTS_64
To obejmuje:
Tabela funkcji: Która zawiera informacje o funkcjach programu.
Tabela symboli: Która zawiera informacje o zewnętrznych funkcjach używanych przez binarny plik
Może również zawierać wewnętrzne funkcje, nazwy zmiennych oraz inne.
Aby to sprawdzić, możesz użyć narzędzia Mach-O View:
Lub z poziomu cli:
W segmencie __TEXT
(r-x):
__objc_classname
: Nazwy klas (ciągi)
__objc_methname
: Nazwy metod (ciągi)
__objc_methtype
: Typy metod (ciągi)
W segmencie __DATA
(rw-):
__objc_classlist
: Wskaźniki do wszystkich klas Objective-C
__objc_nlclslist
: Wskaźniki do klas Objective-C bez leniwego ładowania
__objc_catlist
: Wskaźnik do Kategorii
__objc_nlcatlist
: Wskaźnik do Kategorii bez leniwego ładowania
__objc_protolist
: Lista protokołów
__objc_const
: Dane stałe
__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)