macOS Universal binaries & Mach-O Format
Reading time: 13 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.
Temel Bilgiler
Mac OS ikili dosyaları genellikle evrensel ikili dosyalar olarak derlenir. Bir evrensel ikili dosya aynı dosyada birden fazla mimariyi destekleyebilir.
Bu ikili dosyalar Mach-O yapısını takip eder ve bu yapı temelde şunlardan oluşur:
- Başlık
- Yükleme Komutları
- Veri
Fat Başlık
Dosyayı aramak için: 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 veya FAT_MAGIC_64 */
uint32_t nfat_arch; /* takip eden yapıların sayısı */
};
struct fat_arch {
cpu_type_t cputype; /* cpu belirleyici (int) */
cpu_subtype_t cpusubtype; /* makine belirleyici (int) */
uint32_t offset; /* bu nesne dosyasına dosya ofseti */
uint32_t size; /* bu nesne dosyasının boyutu */
uint32_t align; /* 2'nin kuvveti olarak hizalama */
};
Başlık, magic baytları ile başlar ve dosyanın içerdiği mimari sayısını (nfat_arch
) takip eder ve her mimari bir fat_arch
yapısına sahip olacaktır.
Bunu kontrol etmek için:
% file /bin/ls
/bin/ls: Mach-O evrensel ikili dosya, 2 mimari ile: [x86_64:Mach-O 64-bit yürütülebilir x86_64] [arm64e:Mach-O 64-bit yürütülebilir arm64e]
/bin/ls (mimari x86_64 için): Mach-O 64-bit yürütülebilir x86_64
/bin/ls (mimari arm64e için): Mach-O 64-bit yürütülebilir arm64e
% otool -f -v /bin/ls
Fat başlıklar
fat_magic FAT_MAGIC
nfat_arch 2
mimari x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
offset 16384
size 72896
align 2^14 (16384)
mimari arm64e
cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
offset 98304
size 88816
align 2^14 (16384)
ya da Mach-O View aracını kullanarak:
Düşündüğünüz gibi, genellikle 2 mimari için derlenmiş bir evrensel ikili dosya, sadece 1 mimari için derlenmiş olanın boyutunu iki katına çıkarır.
Mach-O Başlığı
Başlık, dosyanın Mach-O dosyası olarak tanımlanmasını sağlayan magic baytları ve hedef mimari hakkında bilgi gibi temel bilgileri içerir. Bunu şurada bulabilirsiniz: 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 */
};
Mach-O Dosya Türleri
Farklı dosya türleri vardır, bunlar örnek olarak burada tanımlanmıştır. En önemli olanları şunlardır:
MH_OBJECT
: Yeniden yerleştirilebilir nesne dosyası (derleme ara ürünleri, henüz çalıştırılabilir değil).MH_EXECUTE
: Çalıştırılabilir dosyalar.MH_FVMLIB
: Sabit VM kütüphane dosyası.MH_CORE
: Kod DökümleriMH_PRELOAD
: Önceden yüklenmiş çalıştırılabilir dosya (XNU'da artık desteklenmiyor)MH_DYLIB
: Dinamik KütüphanelerMH_DYLINKER
: Dinamik BağlayıcıMH_BUNDLE
: "Eklenti dosyaları". gcc'de -bundle kullanılarak oluşturulur veNSBundle
veyadlopen
ile açıkça yüklenir.MH_DYSM
: Eşlik eden.dSym
dosyası (hata ayıklama için semboller içeren dosya).MH_KEXT_BUNDLE
: Çekirdek Uzantıları.
# 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
Or using Mach-O View:
Mach-O Bayrakları
Kaynak kodu, kütüphaneleri yüklemek için yararlı olan birkaç bayrak tanımlar:
MH_NOUNDEFS
: Tanımsız referans yok (tamamen bağlantılı)MH_DYLDLINK
: Dyld bağlantısıMH_PREBOUND
: Dinamik referanslar önceden bağlanmış.MH_SPLIT_SEGS
: Dosya r/o ve r/w segmentlerini ayırır.MH_WEAK_DEFINES
: İkili zayıf tanımlı sembollere sahiptirMH_BINDS_TO_WEAK
: İkili zayıf semboller kullanırMH_ALLOW_STACK_EXECUTION
: Yığını çalıştırılabilir hale getirMH_NO_REEXPORTED_DYLIBS
: Kütüphane LC_REEXPORT komutları yokMH_PIE
: Konum Bağımsız YürütülebilirMH_HAS_TLV_DESCRIPTORS
: Thread yerel değişkenlerle bir bölüm varMH_NO_HEAP_EXECUTION
: Yığın/veri sayfaları için yürütme yokMH_HAS_OBJC
: İkili oBject-C bölümlerine sahiptirMH_SIM_SUPPORT
: Simülatör desteğiMH_DYLIB_IN_CACHE
: Paylaşılan kütüphane önbelleğinde dylibs/frameworks üzerinde kullanılır.
Mach-O Yükleme komutları
Dosyanın bellek düzeni burada belirtilmiştir, sembol tablosunun konumu, yürütme başlangıcındaki ana iş parçacığının bağlamı ve gerekli paylaşılan kütüphaneler detaylandırılmıştır. Dinamik yükleyiciye (dyld) ikilinin belleğe yüklenme süreci hakkında talimatlar verilir.
load_command yapısını kullanır, belirtilen loader.h
dosyasında tanımlanmıştır:
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
**Sistem tarafından farklı şekilde işlenen yaklaşık 50 farklı yükleme komutu türü vardır. En yaygın olanları: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
ve LC_CODE_SIGNATURE
.
LC_SEGMENT/LC_SEGMENT_64
tip
Temelde, bu tür bir Yükleme Komutu, ikili dosya çalıştırıldığında __TEXT (yürütülebilir kod) ve __DATA (işlem için veri) segmentlerini Veri bölümünde belirtilen ofsetlere göre nasıl yükleyeceğini tanımlar.
Bu komutlar, bir işlem çalıştırıldığında sanallaştırılmış bellek alanına haritalanan segmentleri tanımlar.
__TEXT segmenti gibi farklı türde segmentler vardır; bu segment, bir programın yürütülebilir kodunu tutar ve __DATA segmenti, işlem tarafından kullanılan verileri içerir. Bu segmentler, Mach-O dosyasının veri bölümünde yer alır.
Her segment, daha fazla bölüme ayrılabilir. Yükleme komutu yapısı, ilgili segment içindeki bu bölümler hakkında bilgi içerir.
Başlıkta önce segment başlığı bulunur:
struct segment_command_64 { /* 64-bit mimariler için */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* section_64 yapı boyutunu içerir */
char segname[16]; /* segment adı */
uint64_t vmaddr; /* bu segmentin bellek adresi */
uint64_t vmsize; /* bu segmentin bellek boyutu */
uint64_t fileoff; /* bu segmentin dosya ofseti */
uint64_t filesize; /* dosyadan haritalanacak miktar */
int32_t maxprot; /* maksimum VM koruma */
int32_t initprot; /* başlangıç VM koruma */
uint32_t nsects; /* segmentteki bölüm sayısı */
uint32_t flags; /* bayraklar */
};
Segment başlığı örneği:
Bu başlık, sonrasında görünen bölüm başlıklarının sayısını tanımlar:
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 */
};
Örnek bölüm başlığı:
Eğer bölüm ofsetini (0x37DC) + mimari başlangıç ofsetini eklerseniz, bu durumda 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Ayrıca başlık bilgilerini komut satırından almak da mümkündür:
otool -lv /bin/ls
Bu cmd tarafından yüklenen yaygın segmentler:
__PAGEZERO
: Çekirdeğe adres sıfırını haritalandırmasını söyler, böylece okunamaz, yazılamaz veya çalıştırılamaz. Yapıdaki maxprot ve minprot değişkenleri, bu sayfada okuma-yazma-çalıştırma hakları olmadığını belirtmek için sıfıra ayarlanır.- Bu tahsis, NULL işaretçi dereferans zafiyetlerini azaltmak için önemlidir. Bunun nedeni, XNU'nun ilk sayfanın (sadece ilk) belleğin erişilemez olmasını sağlayan sert bir sayfa sıfırı uygulamasıdır (i386 dışında). Bir ikili, ilk 4k'yi kapsamak için küçük bir __PAGEZERO oluşturarak ve geri kalan 32bit belleği hem kullanıcı hem de çekirdek modunda erişilebilir hale getirerek bu gereksinimleri karşılayabilir.
__TEXT
: okunabilir ve çalıştırılabilir (yazılabilir değil) kod içerir. Bu segmentin yaygın bölümleri:__text
: Derlenmiş ikili kod__const
: Sabit veri (sadece okunabilir)__[c/u/os_log]string
: C, Unicode veya os log string sabitleri__stubs
ve__stubs_helper
: Dinamik kütüphane yükleme sürecinde yer alır__unwind_info
: Yığın açılma verisi.- Tüm bu içeriğin imzalandığını ancak aynı zamanda çalıştırılabilir olarak işaretlendiğini unutmayın (bu ayrıcalığı gerektirmeyen bölümlerin istismar seçeneklerini artırır, örneğin string'e özel bölümler).
__DATA
: okunabilir ve yazılabilir (çalıştırılabilir değil) veri içerir.__got:
Küresel Ofset Tablosu__nl_symbol_ptr
: Tembel olmayan (yükleme sırasında bağlanan) sembol işaretçisi__la_symbol_ptr
: Tembel (kullanımda bağlanan) sembol işaretçisi__const
: Sadece okunabilir veri olmalıdır (gerçekte değil)__cfstring
: CoreFoundation string'leri__data
: Küresel değişkenler (başlatılmış olanlar)__bss
: Statik değişkenler (başlatılmamış olanlar)__objc_*
(__objc_classlist, __objc_protolist, vb): Objective-C çalışma zamanı tarafından kullanılan bilgiler__DATA_CONST
: __DATA.__const sabit olacağı garanti edilmez (yazma izinleri), diğer işaretçiler ve GOT da öyle. Bu bölüm,__const
, bazı başlatıcılar ve GOT tablosunu (bir kez çözüldüğünde) sadece okunabilir hale getirir.__LINKEDIT
: Bağlayıcı (dyld) için sembol, string ve yer değiştirme tablosu girişleri gibi bilgileri içerir.__TEXT
veya__DATA
içinde olmayan içerikler için genel bir konteynırdır ve içeriği diğer yükleme komutlarında tanımlanır.- dyld bilgisi: Yeniden temel alma, Tembel/tembel olmayan/zayıf bağlama opcode'ları ve dışa aktarma bilgisi
- Fonksiyon başlangıçları: Fonksiyonların başlangıç adresleri tablosu
- Kod İçindeki Veri: __text içindeki veri adaları
- Sembol Tablosu: İkili içindeki semboller
- Dolaylı Sembol Tablosu: İşaretçi/stub sembolleri
- String Tablosu
- Kod İmzası
__OBJC
: Objective-C çalışma zamanı tarafından kullanılan bilgileri içerir. Bu bilgilere __DATA segmentinde, __objc_* bölümleri içinde de rastlanabilir.__RESTRICT
: İçeriği olmayan ve__restrict
adında tek bir bölümü olan bir segment (aynı zamanda boş) olup, ikilinin çalıştırılması sırasında DYLD çevresel değişkenlerini göz ardı edeceğini garanti eder.
Kodda görüldüğü gibi, segmentler ayrıca bayrakları destekler (her ne kadar çok kullanılmasa da):
SG_HIGHVM
: Sadece çekirdek (kullanılmıyor)SG_FVMLIB
: KullanılmıyorSG_NORELOC
: Segmentin yer değiştirmesi yokSG_PROTECTED_VERSION_1
: Şifreleme. Örneğin, Finder tarafından__TEXT
segmentini şifrelemek için kullanılır.
LC_UNIXTHREAD/LC_MAIN
LC_MAIN
entryoff niteliğinde giriş noktasını içerir. Yükleme sırasında, dyld bu değeri (bellekteki) ikili tabanına ekler, ardından bu talimata atlar ve ikilinin kodunun yürütülmesine başlar.
LC_UNIXTHREAD
ana iş parçacığı başlatıldığında kayıtların sahip olması gereken değerleri içerir. Bu zaten kullanımdan kaldırılmıştır ancak dyld
hala bunu kullanır. Bu kayıtların ayarlandığı değerleri görmek mümkündür:
otool -l /usr/lib/dyld
[...]
Load command 13
cmd LC_UNIXTHREAD
cmdsize 288
flavor ARM_THREAD_STATE64
count ARM_THREAD_STATE64_COUNT
x0 0x0000000000000000 x1 0x0000000000000000 x2 0x0000000000000000
x3 0x0000000000000000 x4 0x0000000000000000 x5 0x0000000000000000
x6 0x0000000000000000 x7 0x0000000000000000 x8 0x0000000000000000
x9 0x0000000000000000 x10 0x0000000000000000 x11 0x0000000000000000
x12 0x0000000000000000 x13 0x0000000000000000 x14 0x0000000000000000
x15 0x0000000000000000 x16 0x0000000000000000 x17 0x0000000000000000
x18 0x0000000000000000 x19 0x0000000000000000 x20 0x0000000000000000
x21 0x0000000000000000 x22 0x0000000000000000 x23 0x0000000000000000
x24 0x0000000000000000 x25 0x0000000000000000 x26 0x0000000000000000
x27 0x0000000000000000 x28 0x0000000000000000 fp 0x0000000000000000
lr 0x0000000000000000 sp 0x0000000000000000 pc 0x0000000000004b70
cpsr 0x00000000
[...]
LC_CODE_SIGNATURE
Mach-O dosyasının kod imzası hakkında bilgi içerir. Sadece imza blob'una işaret eden bir offset içerir. Bu genellikle dosyanın en sonunda bulunur.
Ancak, bu bölüm hakkında bazı bilgileri bu blog yazısında ve bu gists bulabilirsiniz.
LC_ENCRYPTION_INFO[_64]
İkili şifreleme desteği. Ancak, elbette, bir saldırgan süreci tehlikeye atmayı başarırsa, şifrelenmemiş belleği dökebilecektir.
LC_LOAD_DYLINKER
Paylaşılan kütüphaneleri süreç adres alanına haritalayan dinamik bağlayıcı yürütülebilir dosyasının yolunu içerir. Değer her zaman /usr/lib/dyld
olarak ayarlanır. macOS'ta, dylib haritalamanın kullanıcı modunda, çekirdek modunda değil olduğunu belirtmek önemlidir.
LC_IDENT
Eski ama panik durumunda dökümler oluşturacak şekilde yapılandırıldığında, bir Mach-O çekirdek dökümü oluşturulur ve çekirdek sürümü LC_IDENT
komutunda ayarlanır.
LC_UUID
Rastgele UUID. Doğrudan herhangi bir şey için faydalıdır ama XNU bunu süreç bilgileriyle birlikte önbelleğe alır. Çökme raporlarında kullanılabilir.
LC_DYLD_ENVIRONMENT
Süreç çalıştırılmadan önce dyld'ye ortam değişkenlerini belirtmeye olanak tanır. Bu, süreç içinde rastgele kod çalıştırılmasına izin verebileceğinden oldukça tehlikeli olabilir, bu nedenle bu yükleme komutu yalnızca #define SUPPORT_LC_DYLD_ENVIRONMENT
ile derlenmiş dyld'de kullanılır ve işleme yalnızca DYLD_..._PATH
biçimindeki değişkenlerle sınırlıdır.
LC_LOAD_DYLIB
Bu yükleme komutu, yükleyiciye (dyld) söz konusu kütüphaneyi yüklemesi ve bağlaması talimatını veren bir dinamik kütüphane bağımlılığını tanımlar. Mach-O ikilisinin ihtiyaç duyduğu her kütüphane için bir LC_LOAD_DYLIB
yükleme komutu vardır.
- Bu yükleme komutu, gerçek bağımlı dinamik kütüphaneyi tanımlayan bir struct dylib içeren
dylib_command
türünde bir yapıdır:
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*/
};
Bu bilgiyi cli ile de alabilirsiniz:
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)
Bazı potansiyel kötü amaçlı yazılım ile ilgili kütüphaneler şunlardır:
- DiskArbitration: USB sürücüleri izleme
- AVFoundation: Ses ve video yakalama
- CoreWLAN: Wifi taramaları.
note
Bir Mach-O ikili dosyası, LC_MAIN'de belirtilen adresten önce çalıştırılacak bir veya daha fazla constructor içerebilir.
Herhangi bir constructor'ın ofsetleri, __DATA_CONST segmentinin __mod_init_func bölümünde tutulur.
Mach-O Verisi
Dosyanın merkezinde, yükleme komutları bölgesinde tanımlanan birkaç segmentten oluşan veri bölgesi yer alır. Her segment içinde çeşitli veri bölümleri barındırabilir, her bölüm bir türle ilgili kod veya veriyi tutar.
tip
Veri, esasen yükleme komutları LC_SEGMENTS_64 tarafından yüklenen tüm bilgileri içeren kısımdır.
Bu şunları içerir:
- Fonksiyon tablosu: Program fonksiyonları hakkında bilgi tutar.
- Sembol tablosu: İkili dosya tarafından kullanılan dış fonksiyonlar hakkında bilgi içerir.
- Ayrıca iç fonksiyon, değişken adları ve daha fazlasını da içerebilir.
Bunu kontrol etmek için Mach-O View aracını kullanabilirsiniz:
Ya da cli'dan:
size -m /bin/ls
Objetive-C Ortak Bölümler
In __TEXT
segment (r-x):
__objc_classname
: Sınıf adları (dizeler)__objc_methname
: Metot adları (dizeler)__objc_methtype
: Metot türleri (dizeler)
In __DATA
segment (rw-):
__objc_classlist
: Tüm Objetive-C sınıflarına işaretçiler__objc_nlclslist
: Tembel Olmayan Objective-C sınıflarına işaretçiler__objc_catlist
: Kategorilere işaretçi__objc_nlcatlist
: Tembel Olmayan Kategorilere işaretçi__objc_protolist
: Protokol listesi__objc_const
: Sabit veri__objc_imageinfo
,__objc_selrefs
,objc__protorefs
...
Swift
_swift_typeref
,_swift3_capture
,_swift3_assocty
,_swift3_types, _swift3_proto
,_swift3_fieldmd
,_swift3_builtin
,_swift3_reflstr
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.