16 KiB
Uniwersalne pliki wykonywalne w macOS i format Mach-O
Nauka hakerskiego 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ź PLANY SUBSKRYPCYJNE!
- 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 na GitHubie.
Podstawowe informacje
Binaria w systemie Mac OS zazwyczaj są kompilowane jako uniwersalne pliki wykonywalne. Uniwersalny plik wykonywalny może obsługiwać wiele architektur w tym samym pliku.
Te pliki wykonywalne stosują strukturę Mach-O, która składa się z:
- Nagłówka
- Komend ładowania
- Danych
Nagłówek Fat
Wyszukaj plik za pomocą: 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 obiektu */
uint32_t size; /* rozmiar tego pliku obiektu */
uint32_t align; /* wyrównanie jako potęga liczby 2 */
};
Nagłówek zawiera magiczne bajty, a następnie liczbę architektur, które plik zawiera (nfat_arch
) i każda architektura będzie miała strukturę fat_arch
.
Sprawdź to za pomocą:
% file /bin/ls
/bin/ls: Mach-O uniwersalny plik wykonywalny 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 używając narzędzia Mach-O View:
Jak możesz sobie wyobrazić, zazwyczaj uniwersalny plik skompilowany dla 2 architektur podwaja rozmiar w porównaniu z plikiem skompilowanym tylko dla 1 architektury.
Nagłówek Mach-O
Nagłówek zawiera podstawowe informacje o pliku, takie jak magiczne bajty identyfikujące go jako plik Mach-O oraz informacje o architekturze docelowej. 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
Układ pliku w pamięci jest tutaj określony, szczegółowo opisując lokalizację tabeli symboli, kontekst głównego wątku na początku wykonania oraz wymagane biblioteki współdzielone. Instrukcje są dostarczane do dynamicznego ładowacza (dyld) dotyczące procesu ładowania binarnego do pamięci.
Używa struktury load_command, zdefiniowanej w wspomnianym 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 rodzajów poleceń ładowania, które system obsługuje w inny sposób. Najczęstsze z nich 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 procesu) zgodnie z przesunięciami wskazanymi 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 rodzaje 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 o tych sekcjach w odpowiednim segmencie.
W nagłówku znajduje się najpierw nagłówek segmentu:
struct segment_command_64 { /* dla architektur 64-bitowych */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* zawiera rozmiar struktur section_64 */
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 występują:
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żliwe jest również uzyskanie informacji o nagłówkach z wiersza poleceń za pomocą:
otool -lv /bin/ls
Wspólne segmenty ładowane przez tę komendę:
* **`__PAGEZERO`:** Instruuje jądro, aby **mapowało** **adres zero**, więc **nie można go odczytać, zapisać ani wykonać**. Zmienne maxprot i minprot w strukturze są ustawione na zero, aby wskazać, że na tej stronie **nie ma praw do odczytu-zapisu-wykonania**.
* Ta alokacja jest ważna do **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:
* `__text`: Skompilowany kod binarny
* `__const`: Dane stałe
* `__cstring`: Stałe ciągi znaków
* `__stubs` i `__stubs_helper`: Zaangażowane podczas procesu dynamicznego ładowania bibliotek
* **`__DATA`**: Zawiera dane, które są **do odczytu** i **zapisu** (bez możliwości wykonania)**.**
* `__data`: Zmienne globalne (które zostały zainicjowane)
* `__bss`: Zmienne statyczne (które nie zostały zainicjowane)
* `__objc_*` (\_\_objc\_classlist, \_\_objc\_protolist, itp.): Informacje używane przez środowisko uruchomieniowe Objective-C
* **`__LINKEDIT`**: Zawiera informacje dla linkera (dyld) takie jak "wpisy tabeli symboli, ciągów i relokacji."
* **`__OBJC`**: Zawiera informacje używane przez środowisko uruchomieniowe Objective-C. Chociaż 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 o **podpisie kodu pliku Mach-O**. Zawiera tylko **przesunięcie**, które **wskazuje** na **blok podpisu**. Zazwyczaj znajduje się to na samym końcu pliku.\
Jednak informacje na temat tej sekcji można znaleźć w [**tym wpisie na blogu**](https://davedelong.com/blog/2018/01/10/reading-your-own-entitlements/) oraz w tym [**gists**](https://gist.github.com/carlospolop/ef26f8eb9fafd4bc22e69e1a32b81da4).
### **LC\_LOAD\_DYLINKER**
Zawiera **ścieżkę do wykonywalnego dynamicznego linkera**, który mapuje współdzielone biblioteki do przestrzeni adresowej procesu. **Wartość zawsze jest ustawiona na `/usr/lib/dyld`**. Warto zauważyć, że w macOS mapowanie dylibów odbywa się w **trybie użytkownika**, a nie w trybie jądra.
### **`LC_LOAD_DYLIB`**
Ta komenda ładowania opisuje zależność od **dynamicznej** **biblioteki**, która **instruuje** **ładowacz** (dyld) do **załadowania i połączenia tej biblioteki**. Istnieje komenda ładowania LC\_LOAD\_DYLIB **dla każdej biblioteki**, którą wymaga plik Mach-O.
* Ta komenda ładowania jest strukturą typu **`dylib_command`** (która zawiera strukturę dylib, opisującą rzeczywistą 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ń:
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)
Potencjalne biblioteki związane z złośliwym oprogramowaniem to:
- DiskArbitration: Monitorowanie dysków USB
- AVFoundation: Przechwytywanie dźwięku i obrazu
- CoreWLAN: Skanowanie sieci Wifi.
{% hint style="info" %}
Binarny 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 poleceń ładowania. W każdym segmencie może być przechowywanych wiele sekcji danych, z każda sekcją zawierającą kod lub dane specyficzne dla danego typu.
{% hint style="success" %} Dane to w zasadzie część zawierająca wszystkie informacje, które są ładowane przez polecenia ł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 binarny plik
- Może również zawierać wewnętrzne funkcje, nazwy zmiennych i inne.
Aby to sprawdzić, można skorzystać z 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ą na HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLANY SUBSKRYPCYJNE!
- 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 na githubie.