macOS Universal binaries & Mach-O Format
Last updated
Last updated
Μάθε & εξάσκησε το Hacking στο AWS:Εκπαίδευση HackTricks AWS Red Team Expert (ARTE) Μάθε & εξάσκησε το Hacking στο GCP: Εκπαίδευση HackTricks GCP Red Team Expert (GRTE)
Τα δυαδικά αρχεία του Mac OS συνήθως μεταγλωττίζονται ως πανεπιστημιακά δυαδικά. Ένα πανεπιστημιακό δυαδικό μπορεί να υποστηρίζει πολλές αρχιτεκτονικές στον ίδιο φάκελο.
Αυτά τα δυαδικά ακολουθούν τη δομή Mach-O η οποία αποτελείται βασικά από:
Κεφαλίδα
Εντολές Φόρτωσης
Δεδομένα
Αναζήτηση για το αρχείο με: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
Η κεφαλίδα έχει τα μαγικά bytes ακολουθούμενα από τον αριθμό των αρχιτεκτονικών που περιέχει το αρχείο (nfat_arch
) και κάθε αρχιτεκτονική θα έχει μια δομή fat_arch
.
Ελέγξτε το με:
ή χρησιμοποιώντας το εργαλείο Mach-O View:
Όπως ίσως σκέφτεστε συνήθως ένα πανεπιστημιακό δυαδικό μεταγλωττισμένο για 2 αρχιτεκτονικές διπλασιάζει το μέγεθος ενός μεταγλωττισμένου για μόνο 1 αρχιτεκτονική.
Η κεφαλίδα περιέχει βασικές πληροφορίες σχετικά με το αρχείο, όπως τα μαγικά bytes για την αναγνώρισή του ως αρχείο Mach-O και πληροφορίες σχετικά με την επιθυμητή αρχιτεκτονική. Μπορείτε να το βρείτε στο: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Υπάρχουν διαφορετικοί τύποι αρχείων, μπορείτε να τους βρείτε ορισμένους ορισμένους στον πηγαίο κώδικα για παράδειγμα εδώ. Οι πιο σημαντικοί είναι:
MH_OBJECT
: Αρχείο αντικειμένου που μπορεί να μετακινηθεί (ενδιάμεσα προϊόντα σύνθεσης, αλλά όχι ακόμα εκτελέσιμα).
MH_EXECUTE
: Εκτελέσιμα αρχεία.
MH_FVMLIB
: Αρχείο βιβλιοθήκης σταθερής VM.
MH_CORE
: Αποθήκευση κώδικα
MH_PRELOAD
: Προφορτωμένο εκτελέσιμο αρχείο (πλέον δεν υποστηρίζεται στο XNU)
MH_DYLIB
: Δυναμικές βιβλιοθήκες
MH_DYLINKER
: Δυναμικός σύνδεσμος
MH_BUNDLE
: "Αρχεία πρόσθετων". Δημιουργούνται χρησιμοποιώντας την επιλογή -bundle στο gcc και φορτώνονται ρητά από NSBundle
ή dlopen
.
MH_DYSM
: Συνοδευτικό αρχείο .dSym
(αρχείο με σύμβολα για αποσφαλμάτωση).
MH_KEXT_BUNDLE
: Πρόσθετα πυρήνα.
Ή χρησιμοποιώντας το Mach-O View:
Ο πηγαίος κώδικας ορίζει επίσης αρκετές σημαίες χρήσιμες για τη φόρτωση βιβλιοθηκών:
MH_NOUNDEFS
: Χωρίς απροσδιόριστες αναφορές (πλήρως συνδεδεμένο)
MH_DYLDLINK
: Δέσμευση Dyld
MH_PREBOUND
: Δυναμικές αναφορές προδεμένες.
MH_SPLIT_SEGS
: Το αρχείο χωρίζει τμήματα r/o και r/w.
MH_WEAK_DEFINES
: Το δυαδικό έχει ασθενώς ορισμένα σύμβολα
MH_BINDS_TO_WEAK
: Το δυαδικό χρησιμοποιεί ασθενή σύμβολα
MH_ALLOW_STACK_EXECUTION
: Κάνει τη στοίβα εκτελέσιμη
MH_NO_REEXPORTED_DYLIBS
: Η βιβλιοθήκη δεν έχει εντολές LC_REEXPORT
MH_PIE
: Εκτελέσιμο με ανεξάρτητη θέση
MH_HAS_TLV_DESCRIPTORS
: Υπάρχει μια ενότητα με τοπικές μεταβλητές νήματος
MH_NO_HEAP_EXECUTION
: Χωρίς εκτέλεση για σελίδες σωρού/δεδομένων
MH_HAS_OBJC
: Το δυαδικό έχει ενότητες Object-C
MH_SIM_SUPPORT
: Υποστήριξη προσομοιωτή
MH_DYLIB_IN_CACHE
: Χρησιμοποιείται σε dylibs/frameworks στην κοινόχρηστη μνήμη βιβλιοθηκών.
Η διάταξη του αρχείου στη μνήμη καθορίζεται εδώ, λεπτομερώς η τοποθεσία του πίνακα συμβόλων, το πλαίσιο του κύριου νήματος στην έναρξη εκτέλεσης και οι απαιτούμενες κοινόχρηστες βιβλιοθήκες. Δίνονται οδηγίες στον δυναμικό φορτωτή (dyld) για τη διαδικασία φόρτωσης του δυαδικού στη μνήμη.
Χρησιμοποιεί τη δομή load_command, ορισμένη στο αναφερόμενο loader.h
:
Υπάρχουν περίπου 50 διαφορετικοί τύποι εντολών φόρτωσης που το σύστημα χειρίζεται διαφορετικά. Οι πιο κοινοί είναι: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
και LC_CODE_SIGNATURE
.
Βασικά, αυτός ο τύπος Load Command ορίζει πώς να φορτώσει το __TEXT (εκτελέσιμος κώδικας) και το __DATA (δεδομένα για τη διαδικασία) segments σύμφωνα με τα offsets που υποδεικνύονται στην ενότητα Δεδομένων όταν το δυαδικό εκτελείται.
Αυτές οι εντολές ορίζουν segments που αντιστοιχίζονται στο εικονικό χώρο μνήμης μιας διαδικασίας όταν εκτελείται.
Υπάρχουν διαφορετικοί τύποι segments, όπως το segment __TEXT, που κρατά τον εκτελέσιμο κώδικα ενός προγράμματος, και το segment __DATA, που περιέχει δεδομένα που χρησιμοποιούνται από τη διαδικασία. Αυτά τα segments βρίσκονται στην ενότητα δεδομένων του αρχείου Mach-O.
Κάθε segment μπορεί να χωριστεί περαιτέρω σε πολλαπλές ενότητες. Η δομή της εντολής φόρτωσης περιέχει πληροφορίες σχετικά με αυτές τις ενότητες εντός του αντίστοιχου segment.
Στην κεφαλίδα πρώτα βρίσκετε η κεφαλίδα του segment:
Παράδειγμα κεφαλίδας segment:
Αυτή η κεφαλίδα ορίζει τον αριθμό των ενοτήτων των οποίων οι κεφαλίδες εμφανίζονται μετά από αυτήν:
Παράδειγμα του επικεφαλίδας ενότητας:
Εάν προσθέσετε το μετατόπισμα της ενότητας (0x37DC) + το μετατόπισμα όπου ξεκινά η αρχιτεκτονική, σε αυτήν την περίπτωση 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Είναι επίσης δυνατό να πάρετε πληροφορίες επικεφαλίδων από τη γραμμή εντολών με:
Κοινά τμήματα που φορτώνονται από αυτήν την εντολή:
__PAGEZERO
: Οδηγεί τον πυρήνα να χαρτογραφήσει τη διεύθυνση μηδέν έτσι ώστε να μην μπορεί να διαβαστεί, να γραφτεί ή να εκτελεστεί. Οι μεταβλητές maxprot και minprot στη δομή ορίζονται σε μηδέν για να υποδείξουν ότι δεν υπάρχουν δικαιώματα ανάγνωσης-εγγραφής-εκτέλεσης σε αυτήν τη σελίδα.
Αυτή η δέσμευση είναι σημαντική για την αντιμετώπιση των ευπαθειών αναφοράς σε μηδενικό δείκτη. Αυτό συμβαίνει επειδή το XNU επιβάλλει ένα σκληρό μηδενικό σελίδας που εξασφαλίζει ότι η πρώτη σελίδα (μόνο η πρώτη) της μνήμης είναι μη προσβάσιμη (εκτός από το i386). Ένα δυαδικό αρχείο θα μπορούσε να πληροί αυτές τις απαιτήσεις δημιουργώντας ένα μικρό __PAGEZERO (χρησιμοποιώντας το -pagezero_size
) για να καλύψει τα πρώτα 4k και να έχει το υπόλοιπο της μνήμης 32bit προσβάσιμο και σε λειτουργία χρήστη και πυρήνα.
__TEXT
: Περιέχει εκτελέσιμο κώδικα με δικαιώματα ανάγνωσης και εκτέλεσης (χωρίς εγγραφή). Κοινές ενότητες αυτού του τμήματος:
__text
: Μεταγλωττισμένος δυαδικός κώδικας
__const
: Σταθερά δεδομένα (μόνο για ανάγνωση)
__[c/u/os_log]string
: Σταθερές συμβολοσειρές C, Unicode ή os logs
__stubs
και __stubs_helper
: Εμπλέκονται κατά τη διαδικασία φόρτωσης δυναμικής βιβλιοθήκης
__unwind_info
: Δεδομένα ανάπτυξης στοίβας.
Σημειώστε ότι όλο αυτό το περιεχόμενο είναι υπογεγραμμένο αλλά και επισημασμένο ως εκτελέσιμο (δημιουργώντας περισσότερες επιλογές για εκμετάλλευση τμημάτων που δεν χρειάζονται απαραίτητα αυτό το προνόμιο, όπως τμήματα αφιερωμένα σε συμβολοσειρές).
__DATA
: Περιέχει δεδομένα που είναι αναγνώσιμα και εγγράψιμα (χωρίς εκτέλεση).
__got:
Πίνακας Καθολικής Μετατόπισης
__nl_symbol_ptr
: Δείκτης συμβόλου μη-αργό (δεσμευμένος κατά τη φόρτωση)
__la_symbol_ptr
: Δείκτης συμβόλου αργό (δεσμευμένος κατά τη χρήση)
__const
: Θα έπρεπε να είναι δεδομένα μόνο για ανάγνωση (στην πραγματικότητα όχι)
__cfstring
: Συμβολοσειρές CoreFoundation
__data
: Παγκόσμιες μεταβλητές (που έχουν αρχικοποιηθεί)
__bss
: Στατικές μεταβλητές (που δεν έχουν αρχικοποιηθεί)
__objc_*
(__objc_classlist, __objc_protolist, κλπ): Πληροφορίες που χρησιμοποιούνται από το runtime της Objective-C
__DATA_CONST
: Το __DATA.__const δεν είναι εγγυημένα σταθερό (δικαιώματα εγγραφής), ούτε και άλλοι δείκτες και ο πίνακας GOT. Αυτή η ενότητα καθιστά το __const
, μερικούς αρχικοποιητές και τον πίνακα GOT (αφού επιλυθεί) μόνο για ανάγνωση χρησιμοποιώντας το mprotect
.
__LINKEDIT
: Περιέχει πληροφορίες για τον σύνδεσμο (dyld) όπως, σύμβολα, συμβολοσειρές και καταχωρήσεις πίνακα ανακατεύθυνσης. Είναι ένα γενικός δοχείο για περιεχόμενα που δεν βρίσκονται ούτε στο __TEXT
ούτε στο __DATA
και το περιεχόμενό του περιγράφεται σε άλλες εντολές φόρτωσης.
Πληροφορίες dyld: Επαντοποίηση, μη-αργή/αργή/αδύναμη σύνδεση συμβόλων και πληροφορίες εξαγωγής
Έναρξη συναρτήσεων: Πίνακας διευθύνσεων έναρξης συναρτήσεων
Δεδομένα Στον Κώδικα: Δεδομένα νησίδες στο __text
Πίνακας Συμβόλων: Σύμβολα στο δυαδικό
Έμμεσος Πίνακας Συμβόλων: Δείκτες συμβόλων/στάμπ
Πίνακας Συμβολοσειρών
Υπογραφή Κώδικα
__OBJC
: Περιέχει πληροφορίες που χρησιμοποιούνται από το runtime της Objective-C. Αυτές οι πληροφορίες μπορεί επίσης να βρεθούν στο τμήμα __DATA, εντός διαφόρων τμημάτων __objc_*.
__RESTRICT
: Ένα τμήμα χωρίς περιεχόμενο με ένα μόνο τμήμα που ονομάζεται __restrict
(επίσης κενό) που εξασφαλίζει ότι κατά την εκτέλεση του δυαδικού, θα αγνοήσει τις μεταβλητές περιβάλλοντος DYLD.
Όπως ήταν δυνατό να δει κανείς στον κώδικα, τα τμήματα υποστηρίζουν επίσης σημαίες (αν και δεν χρησιμοποιούνται πολύ):
SG_HIGHVM
: Μόνο πυρήνας (δεν χρησιμοποιείται)
SG_FVMLIB
: Δεν χρησιμοποιείται
SG_NORELOC
: Το τμήμα δεν έχει ανακατεύθυνση
SG_PROTECTED_VERSION_1
: Κρυπτογράφηση. Χρησιμοποιείται για παράδειγμα από το Finder για να κρυπτογραφήσει το κείμενο του τμήματος __TEXT
.
LC_UNIXTHREAD/LC_MAIN
Το LC_MAIN
περιέχει το σημείο εισόδου στο χαρακτηριστικό entryoff. Κατά τη φόρτωση, το dyld απλά προσθέτει αυτήν την τιμή στη (στη μνήμη) βάση του δυαδικού, και στη συνέχεια μεταβαίνει σε αυτήν την εντολή για να ξεκινήσει η εκτέλεση του κώδικα του δυαδικού.
Το LC_UNIXTHREAD
περιέχει τις τιμές που πρέπει να έχουν τα καταχωρητέα όταν ξεκινά ο κύριος νήματος. Αυτό έχει ήδη αποσυρθεί, αλλά το dyld
το χρησιμοποιεί ακόμα. Είναι δυνατό να δείτε τις τιμές των καταχωρητών που έχουν οριστεί από αυτό με:
LC_CODE_SIGNATURE
Περιέχει πληροφορίες σχετικά με τη υπογραφή κώδικα του αρχείου Mach-O. Περιέχει μόνο ένα μετατόπιση που δείχνει στο blob υπογραφής. Αυτό είναι τυπικά στο πολύ τέλος του αρχείου. Ωστόσο, μπορείτε να βρείτε πληροφορίες σχετικά με αυτήν την ενότητα στην ανάρτηση στο blog αυτό και αυτό το gists.
LC_ENCRYPTION_INFO[_64]
Υποστήριξη για κρυπτογράφηση δυαδικού κώδικα. Ωστόσο, φυσικά, αν ένας επιτιθέμενος καταφέρει να διακινδυνεύσει τη διαδικασία, θα μπορεί να ανακτήσει τη μνήμη μη κρυπτογραφημένη.
LC_LOAD_DYLINKER
Περιέχει τη διαδρομή προς το εκτελέσιμο δυναμικού συνδέτη που αντιστοιχεί τις κοινόχρηστες βιβλιοθήκες στο χώρο διεύθυνσης της διεργασίας. Η τιμή είναι πάντα ορισμένη σε /usr/lib/dyld
. Σημαντικό είναι να σημειωθεί ότι στο macOS, η αντιστοίχιση dylib συμβαίνει σε λειτουργία χρήστη, όχι σε λειτουργία πυρήνα.
LC_IDENT
Παρωχημένο αλλά όταν ρυθμιστεί για τη δημιουργία αναφορών σφαλμάτων, δημιουργείται ένας πυρήνας Mach-O και η έκδοση πυρήνα ορίζεται στην εντολή LC_IDENT
.
LC_UUID
Τυχαίο UUID. Είναι χρήσιμο για οτιδήποτε άμεσα, αλλά το XNU το αποθηκεύει μαζί με τις υπόλοιπες πληροφορίες της διεργασίας. Μπορεί να χρησιμοποιηθεί σε αναφορές σφαλμάτων.
LC_DYLD_ENVIRONMENT
Επιτρέπει την υποδειξη μεταβλητών περιβάλλοντος στο dyld πριν εκτελεστεί η διαδικασία. Αυτό μπορεί να είναι επικίνδυνο καθώς μπορεί να επιτρέψει την εκτέλεση αυθαίρετου κώδικα μέσα στη διαδικασία, οπότε αυτή η εντολή φόρτωσης χρησιμοποιείται μόνο σε dyld που χτίστηκε με #define SUPPORT_LC_DYLD_ENVIRONMENT
και περαιτέρω περιορίζει την επεξεργασία μόνο σε μεταβλητές της μορφής DYLD_..._PATH
που καθορίζουν διαδρομές φόρτωσης.
LC_LOAD_DYLIB
Αυτή η εντολή φόρτωσης περιγράφει μια δυναμική εξάρτηση βιβλιοθήκης που οδηγεί τον φορτωτή (dyld) να φορτώσει και να συνδέσει τη συγκεκριμένη βιβλιοθήκη. Υπάρχει μια εντολή φόρτωσης LC_LOAD_DYLIB
για κάθε βιβλιοθήκη που απαιτεί το δυαδικό Mach-O.
Αυτή η εντολή φόρτωσης είναι μια δομή τύπου dylib_command
(η οποία περιέχει μια δομή dylib, περιγράφοντας την πραγματική εξαρτώμενη δυναμική βιβλιοθήκη):
Μπορείτε επίσης να λάβετε αυτές τις πληροφορίες από το cli με:
Μερικές πιθανές βιβλιοθήκες που σχετίζονται με malware είναι:
DiskArbitration: Παρακολούθηση USB drives
AVFoundation: Καταγραφή ήχου και βίντεο
CoreWLAN: Σάρωση Wifi.
Ένα δυαδικό Mach-O μπορεί να περιέχει έναν ή περισσότερους κατασκευαστές, οι οποίοι θα εκτελεστούν πριν τη διεύθυνση που καθορίζεται στο LC_MAIN. Τα offsets οποιουδήποτε κατασκευαστή κρατούνται στην ενότητα __mod_init_func του τμήματος __DATA_CONST.
Στον πυρήνα του αρχείου βρίσκεται η περιοχή δεδομένων, η οποία αποτελείται από διάφορα τμήματα όπως ορίζεται στην περιοχή εντολών φόρτωσης. Μια ποικιλία τμημάτων δεδομένων μπορεί να φιλοξενείται σε κάθε τμήμα, με κάθε τμήμα να κρατάει κώδικα ή δεδομένα που είναι συγκεκριμένα για έναν τύπο.
Τα δεδομένα είναι βασικά η περιοχή που περιέχει όλες τις πληροφορίες που φορτώνονται από τις εντολές φόρτωσης LC_SEGMENTS_64
Αυτό περιλαμβάνει:
Πίνακας συναρτήσεων: Ο οποίος περιέχει πληροφορίες σχετικά με τις λειτουργίες του προγράμματος.
Πίνακας συμβόλων: Ο οποίος περιέχει πληροφορίες σχετικά με τις εξωτερικές λειτουργίες που χρησιμοποιούνται από το δυαδικό
Μπορεί επίσης να περιέχει εσωτερικές συναρτήσεις, ονόματα μεταβλητών και άλλα.
Για να το ελέγξετε μπορείτε να χρησιμοποιήσετε το εργαλείο Mach-O View:
Ή από το cli:
Στο τμήμα __TEXT
(r-x):
__objc_classname
: Ονόματα κλάσεων (αλφαριθμητικά)
__objc_methname
: Ονόματα μεθόδων (αλφαριθμητικά)
__objc_methtype
: Τύποι μεθόδων (αλφαριθμητικά)
Στο τμήμα __DATA
(rw-):
__objc_classlist
: Δείκτες προς όλες τις κλάσεις Objective-C
__objc_nlclslist
: Δείκτες προς μη-τεμπέλιες κλάσεις Objective-C
__objc_catlist
: Δείκτης προς Κατηγορίες
__objc_nlcatlist
: Δείκτης προς μη-τεμπέλιες Κατηγορίες
__objc_protolist
: Λίστα πρωτοκόλλων
__objc_const
: Σταθερά δεδομένα
__objc_imageinfo
, __objc_selrefs
, objc__protorefs
...
_swift_typeref
, _swift3_capture
, _swift3_assocty
, _swift3_types, _swift3_proto
, _swift3_fieldmd
, _swift3_builtin
, _swift3_reflstr