16 KiB
Uniwersalne pliki binarne macOS i format Mach-O
Dowiedz się, jak hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLAN SUBSKRYPCJI!
- Zdobądź oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @carlospolopm.
- Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów GitHub.
Podstawowe informacje
Binarki systemu Mac OS zazwyczaj są kompilowane jako uniwersalne pliki binarne. Uniwersalny plik binarny może obsługiwać wiele architektur w tym samym pliku.
Te binarki mają strukturę Mach-O, która składa się z:
- Nagłówek
- Polecenia ładowania
- Dane
Nagłówek Fat
Wyszukaj plik za pomocą polecenia: 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; /* liczba struktur, które następują */
};
struct fat_arch {
cpu_type_t cputype; /* określacz CPU (int) */
cpu_subtype_t cpusubtype; /* określacz maszyny (int) */
uint32_t offset; /* przesunięcie pliku do tego pliku obiektowego */
uint32_t size; /* rozmiar tego pliku obiektowego */
uint32_t align; /* wyrównanie jako potęga liczby 2 */
};
Nagłówek zawiera bajty magiczne, a następnie liczbę architektur, które plik zawiera (nfat_arch
), a każda architektura będzie miała strukturę fat_arch
.
Sprawdź to za pomocą:
% file /bin/ls
/bin/ls: Mach-O uniwersalny plik binarny z 2 architekturami: [x86_64:Mach-O 64-bitowy plik wykonywalny x86_64] [arm64e:Mach-O 64-bitowy plik wykonywalny arm64e]
/bin/ls (dla architektury x86_64): Mach-O 64-bitowy plik wykonywalny x86_64
/bin/ls (dla architektury arm64e): Mach-O 64-bitowy plik wykonywalny arm64e
% otool -f -v /bin/ls
Nagłówki Fat
fat_magic FAT_MAGIC
nfat_arch 2
architektura x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
przesunięcie 16384
rozmiar 72896
wyrównanie 2^14 (16384)
architektura arm64e
cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
przesunięcie 98304
rozmiar 88816
wyrównanie 2^14 (16384)
lub za pomocą narzędzia Mach-O View:
Jak możesz sobie wyobrazić, uniwersalny plik binarny skompilowany dla 2 architektur podwaja rozmiar w porównaniu do pliku skompilowanego tylko dla 1 architektury.
Nagłówek Mach-O
Nagłówek zawiera podstawowe informacje o pliku, takie jak bajty magiczne 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$"
#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 */
};
Typy plików:
- MH_EXECUTE (0x2): Standardowy plik wykonywalny Mach-O
- MH_DYLIB (0x6): Biblioteka dynamiczna Mach-O (np. .dylib)
- MH_BUNDLE (0x8): Pakiet Mach-O (np. .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
Lub używając Mach-O View:
Polecenia ładowania Mach-O
Tutaj określona jest układ pliku w pamięci, szczegółowo opisujący lokalizację tabeli symboli, kontekst głównego wątku podczas rozpoczęcia wykonywania oraz wymagane biblioteki współdzielone. Instrukcje są dostarczane do dynamicznego ładowacza (dyld) w procesie ładowania binarnego do pamięci.
Używana jest struktura load_command, zdefiniowana w wspomnianym pliku loader.h
:
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
Istnieje około 50 różnych typów poleceń ładowania, które system obsługuje w inny sposób. Najczęściej spotykane to: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
i LC_CODE_SIGNATURE
.
LC_SEGMENT/LC_SEGMENT_64
{% hint style="success" %} W zasadzie ten rodzaj polecenia ładowania definiuje, jak załadować segmenty __TEXT (kod wykonywalny) i __DATA (dane dla procesu) zgodnie z przesunięciami w sekcji danych podczas wykonywania binarnego pliku. {% endhint %}
Te polecenia definiują segmenty, które są mapowane do przestrzeni pamięci wirtualnej procesu podczas jego wykonywania.
Istnieją różne typy segmentów, takie jak segment __TEXT, który przechowuje 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 ładowania zawiera informacje na temat tych sekcji w odpowiednim segmencie.
W nagłówku najpierw znajduje się nagłówek segmentu:
struct segment_command_64 { /* dla architektur 64-bitowych */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* zawiera sizeof section_64 structs */
char segname[16]; /* nazwa segmentu */
uint64_t vmaddr; /* adres pamięci tego segmentu */
uint64_t vmsize; /* rozmiar pamięci tego segmentu */
uint64_t fileoff; /* przesunięcie pliku tego segmentu */
uint64_t filesize; /* ilość do zmapowania z pliku */
int32_t maxprot; /* maksymalna ochrona VM */
int32_t initprot; /* początkowa ochrona VM */
uint32_t nsects; /* liczba sekcji w segmencie */
uint32_t flags; /* flagi */
};
Przykład nagłówka segmentu:
Ten nagłówek definiuje liczbę sekcji, których nagłówki po nim się pojawiają:
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 */
};
Przykład nagłówka sekcji:
Jeśli dodasz przesunięcie sekcji (0x37DC) + przesunięcie, gdzie arch zaczyna się, w tym przypadku 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Można również uzyskać informacje o nagłówkach z wiersza poleceń za pomocą:
otool -lv /bin/ls
Wspólne segmenty ładowane przez to polecenie:
__PAGEZERO
: Instruuje jądro, aby mapowało adres zero, więc nie można go odczytywać, zapisywać ani wykonywać. Zmienne maxprot i minprot w strukturze są ustawione na zero, co oznacza, że na tej stronie nie ma praw do odczytu-zapisu-wykonania.- Ta alokacja jest ważna w celu zmniejszenia podatności na odwołania do wskaźników NULL.
__TEXT
: Zawiera wykonywalny kod z uprawnieniami do odczytu i wykonania (bez możliwości zapisu). Wspólne sekcje tego segmentu to:__text
: Skompilowany kod binarny__const
: Stałe dane__cstring
: Stałe ciągi znaków__stubs
i__stubs_helper
: Zaangażowane w proces dynamicznego ładowania bibliotek__DATA
: Zawiera dane, które są odczytywalne i zapisywalne (bez możliwości wykonania).__data
: Zmienne globalne (które zostały zainicjalizowane)__bss
: Zmienne statyczne (które nie zostały zainicjalizowane)__objc_*
(__objc_classlist, __objc_protolist, itp.): Informacje używane przez środowisko uruchomieniowe Objective-C__LINKEDIT
: Zawiera informacje dla łącznika (dyld), takie jak "wpisy do tabel symboli, ciągów i relokacji".__OBJC
: Zawiera informacje używane przez środowisko uruchomieniowe Objective-C. Choć te informacje mogą być również znalezione w segmencie __DATA, w różnych sekcjach __objc_*.
LC_MAIN
Zawiera punkt wejścia w atrybucie entryoff. Podczas ładowania, dyld po prostu dodaje tę wartość do (w pamięci) bazowego adresu binarnego, a następnie przechodzi do tej instrukcji, aby rozpocząć wykonywanie kodu binarnego.
LC_CODE_SIGNATURE
Zawiera informacje na temat podpisu kodu pliku Mach-O. Zawiera tylko przesunięcie, które wskazuje na blok podpisu. Zazwyczaj znajduje się na samym końcu pliku.
Jednak można znaleźć pewne informacje na temat tej sekcji w tym wpisie na blogu i tym gists.
LC_LOAD_DYLINKER
Zawiera ścieżkę do dynamicznego łącznika wykonywalnego, który mapuje biblioteki współdzielone do przestrzeni adresowej procesu. Wartość zawsze jest ustawiona na /usr/lib/dyld
. Ważne jest zauważenie, że w macOS mapowanie dylibów odbywa się w trybie użytkownika, a nie w trybie jądra.
LC_LOAD_DYLIB
To polecenie ładowania opisuje zależność od dynamicznej biblioteki, której ładowanie i połączenie jest instruowane przez ładowacz (dyld). Istnieje polecenie ładowania LC_LOAD_DYLIB dla każdej biblioteki, którą wymaga plik Mach-O.
- To polecenie ładowania jest strukturą typu
dylib_command
(która zawiera strukturę dylib, opisującą właściwą zależną dynamiczną bibliotekę):
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*/
};
Możesz również uzyskać te informacje za pomocą wiersza poleceń, wpisując:
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)
Niektóre potencjalnie złośliwe biblioteki to:
- DiskArbitration: Monitorowanie dysków USB
- AVFoundation: Przechwytywanie dźwięku i obrazu
- CoreWLAN: Skanowanie sieci Wi-Fi.
{% hint style="info" %}
Plik Mach-O może zawierać jeden lub więcej konstruktorów, które zostaną wykonane przed adresem określonym w LC_MAIN.
Przesunięcia dowolnych konstruktorów są przechowywane w sekcji __mod_init_func segmentu __DATA_CONST.
{% endhint %}
Dane Mach-O
W centrum pliku znajduje się region danych, który składa się z kilku segmentów zdefiniowanych w regionie komend ładowania. W każdym segmencie może znajdować się wiele sekcji danych, z których każda sekcja zawiera kod lub dane specyficzne dla danego typu.
{% hint style="success" %} Dane to w zasadzie część zawierająca wszystkie informacje, które są ładowane przez komendy ładowania LC_SEGMENTS_64 {% endhint %}
Obejmuje to:
- Tabela funkcji: Która zawiera informacje o funkcjach programu.
- Tabela symboli: Która zawiera informacje o zewnętrznych funkcjach używanych przez plik binarny.
- Może również zawierać wewnętrzne funkcje, nazwy zmiennych i wiele innych.
Aby to sprawdzić, można użyć narzędzia Mach-O View:
Lub z wiersza poleceń:
size -m /bin/ls
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLAN SUBSKRYPCJI!
- Zdobądź oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @carlospolopm.
- Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.