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)
Mac OS ikili dosyaları genellikle evrensel ikili dosyalar olarak derlenir. Bir evrensel ikili dosya, aynı dosyada birden fazla mimariyi destekleyebilir.
Bu ikili dosyalar, temelde aşağıdaki Mach-O yapısını takip eder:
Başlık
Yükleme Komutları
Veri
Dosyayı aramak için: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
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:
veya Mach-O View aracını kullanarak:
Düşündüğünüz gibi, genellikle 2 mimari için derlenmiş bir evrensel ikili dosya, yalnızca 1 mimari için derlenmiş olanın boyutunu iki katına çıkarır.
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 şu komutla bulabilirsiniz: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Farklı dosya türleri vardır, bunları örneğin burada tanımlanmış bulabilirsiniz. En önemli olanları şunlardır:
MH_OBJECT
: Yeniden yerleştirilebilir nesne dosyası (derlemenin 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ümleri
MH_PRELOAD
: Önceden yüklenmiş çalıştırılabilir dosya (XNU'da artık desteklenmiyor)
MH_DYLIB
: Dinamik Kütüphaneler
MH_DYLINKER
: Dinamik Bağlayıcı
MH_BUNDLE
: "Eklenti dosyaları". gcc'de -bundle kullanılarak oluşturulur ve NSBundle
veya dlopen
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ı.
Or using Mach-O View:
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 böler.
MH_WEAK_DEFINES
: İkili zayıf tanımlı sembollere sahiptir
MH_BINDS_TO_WEAK
: İkili zayıf semboller kullanır
MH_ALLOW_STACK_EXECUTION
: Yığını çalıştırılabilir hale getir
MH_NO_REEXPORTED_DYLIBS
: Kütüphane LC_REEXPORT komutları yok
MH_PIE
: Konum Bağımsız Yürütülebilir
MH_HAS_TLV_DESCRIPTORS
: Thread yerel değişkenlerle bir bölüm var
MH_NO_HEAP_EXECUTION
: Yığın/veri sayfaları için yürütme yok
MH_HAS_OBJC
: İkili oBject-C bölümleri var
MH_SIM_SUPPORT
: Simülatör desteği
MH_DYLIB_IN_CACHE
: Paylaşılan kütüphane önbelleğinde kullanılan dylibs/frameworks.
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 hakkında ayrıntılar sunar. Dinamik yükleyiciye (dyld) ikilinin bellek yükleme süreci hakkında talimatlar verilir.
load_command yapısını kullanır, belirtilen loader.h
dosyasında tanımlanmıştır:
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
.
Temelde, bu tür bir Load Command, ikili dosya çalıştırıldığında __TEXT (çalıştırılabilir kod) ve __DATA (işlem için veri) segmentlerini Data 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, bir programın çalıştırılabilir kodunu tutan ve __DATA segmenti gibi, işlem tarafından kullanılan verileri içeren farklı türde segmentler vardır. Bu segmentler, Mach-O dosyasının veri bölümünde yer alır.
Her segment, daha fazla bölümlere ayrılabilir. Load command yapısı, ilgili segment içindeki bu bölümler hakkında bilgi içerir.
Başlıkta önce segment başlığı bulunur:
Example of segment header:
This header defines the number of sections whose headers appear after it:
Ö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:
Common segments loaded by this cmd:
__PAGEZERO
: Çekirdekten adres sıfırını haritalandırmasını ister, böylece okunamaz, yazılamaz veya çalıştırılamaz. Yapının 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 hatalarını azaltmak için önemlidir. Bunun nedeni, XNU'nun ilk sayfanın (sadece ilk) bellekte erişilemez olmasını sağlayan katı bir sayfa sıfırını zorlaması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 sabit dizeleri
__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 dizeye ö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 dizeleri
__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, dize ve yer değiştirme tablosu girişleri gibi bilgileri içerir. __TEXT
veya __DATA
içinde olmayan içerikler için genel bir konteynerdir ve içeriği diğer yükleme komutlarında tanımlanır.
dyld bilgisi: Yeniden temel alma, Tembel/tembel/zaaf 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
Dize Tablosu
Kod İmzası
__OBJC
: Objective-C çalışma zamanı tarafından kullanılan bilgileri içerir. Bu bilgilere __DATA segmentinde, __objc_* bölümlerinde de rastlanabilir.
__RESTRICT
: İçeriği olmayan ve __restrict
(aynı zamanda boş) adında tek bir bölümü olan bir segmenttir; bu, ikili çalıştırıldığı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ıyor
SG_NORELOC
: Segmentin yer değiştirmesi yok
SG_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ığını başlatırken kayıtların sahip olması gereken değerleri içerir. Bu zaten kullanımdan kaldırılmıştır ama dyld
hala bunu kullanır. Bu kayıtların ayarlandığı değerleri görmek mümkündür:
LC_CODE_SIGNATURE
Macho-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 gerçekleştiğini belirtmek önemlidir, çekirdek modunda değil.
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ı için talimat veren bir dinamik kütüphane bağımlılığını tanımlar. Mach-O ikilisinin gerektirdiği 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:
Bu bilgiyi cli ile de alabilirsiniz:
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ı.
Bir Mach-O ikili dosyası bir veya daha fazla constructor içerebilir, bu LC_MAIN'de belirtilen adresten önce çalıştırılacaktır. Herhangi bir constructor'ın ofsetleri __mod_init_func bölümünde __DATA_CONST segmentinde tutulur.
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.
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ç fonksiyonlar, 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:
__TEXT
segmentinde (r-x):
__objc_classname
: Sınıf adları (stringler)
__objc_methname
: Metot adları (stringler)
__objc_methtype
: Metot türleri (stringler)
__DATA
segmentinde (rw-):
__objc_classlist
: Tüm Objective-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_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)