Introduction to ARM64v8
Dowiedz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Dowiedz się i ćwicz Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Poziomy Wyjątków - EL (ARM64v8)
W architekturze ARMv8 poziomy wykonania, znane jako Poziomy Wyjątków (ELs), definiują poziom uprzywilejowania i możliwości środowiska wykonawczego. Istnieją cztery poziomy wyjątków, od EL0 do EL3, z których każdy pełni inną funkcję:
EL0 - Tryb Użytkownika:
Jest to najmniej uprzywilejowany poziom i służy do wykonywania zwykłego kodu aplikacji.
Aplikacje działające na poziomie EL0 są izolowane od siebie nawzajem i od oprogramowania systemowego, zwiększając bezpieczeństwo i stabilność.
EL1 - Tryb Jądra Systemu Operacyjnego:
Większość jąder systemów operacyjnych działa na tym poziomie.
EL1 ma większe uprawnienia niż EL0 i może uzyskać dostęp do zasobów systemowych, ale z pewnymi ograniczeniami, aby zapewnić integralność systemu.
EL2 - Tryb Hypervisora:
Ten poziom jest używany do wirtualizacji. Hypernadzorca działający na poziomie EL2 może zarządzać wieloma systemami operacyjnymi (każdy w swoim EL1) działającymi na tym samym sprzęcie fizycznym.
EL2 zapewnia funkcje izolacji i kontroli środowisk zewirtualizowanych.
EL3 - Tryb Monitora Bezpieczeństwa:
Jest to najbardziej uprzywilejowany poziom i często jest używany do bezpiecznego uruchamiania i zaufanych środowisk wykonawczych.
EL3 może zarządzać i kontrolować dostępy między stanami bezpiecznymi i niebezpiecznymi (takimi jak bezpieczne uruchamianie, zaufany system operacyjny, itp.).
Wykorzystanie tych poziomów pozwala na strukturalne i bezpieczne zarządzanie różnymi aspektami systemu, począwszy od aplikacji użytkownika, aż po najbardziej uprzywilejowane oprogramowanie systemowe. Podejście ARMv8 do poziomów uprzywilejowania pomaga w skutecznym izolowaniu różnych komponentów systemu, zwiększając tym samym bezpieczeństwo i niezawodność systemu.
Rejestry (ARM64v8)
ARM64 ma 31 rejestrów ogólnego przeznaczenia, oznaczonych jako x0
do x30
. Każdy może przechowywać wartość 64-bitową (8 bajtów). Dla operacji wymagających tylko wartości 32-bitowych, te same rejestry można uzyskać w trybie 32-bitowym, używając nazw w0 do w30.
x0
dox7
- Zazwyczaj są używane jako rejestry tymczasowe i do przekazywania parametrów do podprogramów.
x0
przenosi również dane zwracane przez funkcję.
x8
- W jądrze Linuxa,x8
jest używany jako numer wywołania systemowego dla instrukcjisvc
. W macOS używany jest x16!x9
dox15
- Więcej rejestrów tymczasowych, często używanych do zmiennych lokalnych.x16
ix17
- Rejestry Wywołań Wewnątrzproceduralnych. Rejestry tymczasowe dla wartości natychmiastowych. Są one również używane do pośrednich wywołań funkcji i osłon PLT (Procedure Linkage Table).
x16
jest używany jako numer wywołania systemowego dla instrukcjisvc
w macOS.
x18
- Rejestr platformy. Może być używany jako rejestr ogólnego przeznaczenia, ale na niektórych platformach ten rejestr jest zarezerwowany dla zastosowań specyficznych dla platformy: Wskaźnik do bieżącego bloku środowiska wątków w systemie Windows lub wskaźnik do bieżącej struktury zadania wykonywanego w jądrze Linux.x19
dox28
- Są to rejestry zachowywane przez wywołanego. Funkcja musi zachować wartości tych rejestrów dla swojego wywołującego, dlatego są one przechowywane na stosie i odzyskiwane przed powrotem do wywołującego.x29
- Wskaźnik ramki do śledzenia ramki stosu. Gdy tworzona jest nowa ramka stosu, ponieważ wywoływana jest funkcja, rejestrx29
jest przechowywany na stosie a nowy adres wskaźnika ramki (adressp
) jest przechowywany w tym rejestrze.
Ten rejestr może również być używany jako rejestr ogólnego przeznaczenia, chociaż zazwyczaj jest używany jako odniesienie do zmiennych lokalnych.
x30
lublr
- Rejestr łącza. Przechowuje adres powrotu, gdy instrukcjaBL
(Branch with Link) lubBLR
(Branch with Link to Register) jest wykonywana przez przechowywanie wartościpc
w tym rejestrze.
Może być również używany jak każdy inny rejestr.
Jeśli bieżąca funkcja ma wywołać nową funkcję i tym samym nadpisać
lr
, zostanie ona przechowana na stosie na początku, jest to epilog (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Przechowajfp
ilr
, wygeneruj miejsce i uzyskaj nowyfp
) i odzyskana na końcu, jest to prolog (ldp x29, x30, [sp], #48; ret
-> Odzyskajfp
ilr
i zwróć).
sp
- Wskaźnik stosu, używany do śledzenia góry stosu.
wartość
sp
powinna zawsze być utrzymywana co najmniej w wyrównaniu kwadratowym lub może wystąpić wyjątek wyrównania.
pc
- Licznik programu, który wskazuje na następną instrukcję. Ten rejestr może być aktualizowany tylko poprzez generowanie wyjątków, zwracanie wyjątków i skoki. Jedynymi zwykłymi instrukcjami, które mogą odczytać ten rejestr, są instrukcje skoku z linkiem (BL, BLR) do przechowywania adresupc
w rejestrzelr
(Rejestr łącza).xzr
- Rejestr zerowy. Nazywany równieżwzr
w swojej formie rejestru 32-bitowego. Może być używany do łatwego uzyskania wartości zerowej (częsta operacja) lub do wykonywania porównań za pomocąsubs
jaksubs XZR, Xn, #10
przechowując wynikowe dane nigdzie (wxzr
).
Rejestry Wn
to wersja 32-bitowa rejestru Xn
.
Rejestry SIMD i Zmiennoprzecinkowe
Ponadto istnieje kolejnych 32 rejestry o długości 128 bitów, które można używać w zoptymalizowanych operacjach jednoczesnego przetwarzania wielu danych (SIMD) i do wykonywania arytmetyki zmiennoprzecinkowej. Są one nazywane rejestrami Vn, chociaż mogą również działać w trybie 64-bitowym, 32-bitowym, 16-bitowym i 8-bitowym, a wtedy są nazywane Qn
, Dn
, Sn
, Hn
i Bn
.
Rejestry systemowe
Istnieje setki rejestrów systemowych, zwanych również rejestrami specjalnego przeznaczenia (SPR), które są używane do monitorowania i kontroli zachowania procesorów.
Mogą być odczytywane lub ustawiane tylko za pomocą dedykowanej specjalnej instrukcji mrs
i msr
.
Specjalne rejestry TPIDR_EL0
i TPIDDR_EL0
są często spotykane podczas odwracania inżynieryjnego. Przyrostek EL0
wskazuje na minimalny wyjątek, z którego można uzyskać dostęp do rejestru (w tym przypadku EL0 to zwykły poziom wyjątku (uprzywilejowanie), z którym uruchamiane są zwykłe programy).
Są one często używane do przechowywania bazowego adresu obszaru pamięci lokalnej wątku. Zazwyczaj pierwszy jest czytelny i zapisywalny dla programów działających w EL0, ale drugi może być odczytywany z EL0 i zapisywany z EL1 (jak jądro).
mrs x0, TPIDR_EL0 ; Odczytaj TPIDR_EL0 do x0
msr TPIDR_EL0, X0 ; Zapisz x0 do TPIDR_EL0
PSTATE
PSTATE zawiera kilka składowych procesu zserializowanych do widocznego w systemie operacyjnym specjalnego rejestru SPSR_ELx
, gdzie X to poziom uprawnień wywołanego wyjątku (umożliwia to przywrócenie stanu procesu po zakończeniu wyjątku).
Oto dostępne pola:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F600722126-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FJtticLwENZ4i7WwgHiQe%252Fuploads%252Fgit-blob-c3669e1a2c10b38f71e5d409a3165970f2620d12%252Fimage%2520%281196%29.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=93fa41b9&sv=1)
Flagi warunkowe
N
,Z
,C
iV
:N
oznacza, że operacja dała wynik ujemnyZ
oznacza, że operacja dała wynik zeroC
oznacza, że operacja została przeprowadzonaV
oznacza, że operacja dała wynik przekroczenia z zakresem:Suma dwóch liczb dodatnich daje wynik ujemny.
Suma dwóch liczb ujemnych daje wynik dodatni.
W odejmowaniu, gdy od mniejszej liczby dodatniej odejmowana jest większa liczba ujemna (lub odwrotnie), i wynik nie może być reprezentowany w zakresie podanej wielkości bitowej.
Oczywiście procesor nie wie, czy operacja jest znakowana czy nie, więc sprawdzi C i V w operacjach i wskaże, czy wystąpił przeniesienie w przypadku, gdy była znakowana lub nieznakowana.
Nie wszystkie instrukcje aktualizują te flagi. Niektóre, takie jak CMP
lub TST
, robią to, a inne, które mają przyrostek s, jak ADDS
, również to robią.
Bieżąca flaga szerokości rejestru (
nRW
): Jeśli flaga ma wartość 0, program będzie działał w stanie wykonania AArch64 po wznowieniu.Bieżący Poziom Wyjątku (
EL
): Zwykły program działający w EL0 będzie miał wartość 0Flagi jednokrokowego wykonywania (
SS
): Używane przez debugery do jednokrokowego wykonywania poprzez ustawienie flagi SS na 1 wewnątrzSPSR_ELx
poprzez wyjątek. Program wykona krok i wywoła wyjątek jednokrokowy.Flagi stanu nielegalnego wyjątku (
IL
): Służy do oznaczania, kiedy uprzywilejowane oprogramowanie wykonuje nieprawidłowy transfer poziomu wyjątku, ta flaga jest ustawiana na 1, a procesor wywołuje wyjątek stanu nielegalnego.Flagi
DAIF
: Te flagi pozwalają uprzywilejowanemu programowi selektywnie maskować pewne zewnętrzne wyjątki.Jeśli
A
wynosi 1, oznacza to, że zostaną wywołane przerwania asynchroniczne.I
konfiguruje odpowiedź na zewnętrzne żądania przerwań sprzętowych (IRQ), a F jest związane z szybkimi żądaniami przerwań (FIR).Flagi wyboru wskaźnika stosu (
SPS
): Uprzywilejowane programy działające w EL1 i wyżej mogą przełączać się między używaniem własnego rejestru wskaźnika stosu a rejestru modelu użytkownika (np. międzySP_EL1
aEL0
). To przełączanie jest wykonywane poprzez zapisanie do specjalnego rejestruSPSel
. Nie można tego zrobić z EL0.
Konwencja wywoływania (ARM64v8)
Konwencja wywoływania ARM64 określa, że pierwsze osiem parametrów funkcji jest przekazywane w rejestrach x0
do x7
. Dodatkowe parametry są przekazywane na stosie. Wartość zwracana jest przekazywana z powrotem w rejestrze x0
, lub również w x1
jeśli jest długa na 128 bitów. Rejestry x19
do x30
oraz sp
muszą być zachowane między wywołaniami funkcji.
Podczas czytania funkcji w asemblerze, należy szukać prologu i epilogu funkcji. Prolog zazwyczaj obejmuje zapisanie wskaźnika ramki (x29
), ustawienie nowego wskaźnika ramki oraz przydzielanie miejsca na stosie. Epilog zazwyczaj obejmuje przywrócenie zapisanego wskaźnika ramki i powrót z funkcji.
Konwencja wywoływania w Swift
Swift ma swoją własną konwencję wywoływania, którą można znaleźć pod adresem https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64
Powszechne instrukcje (ARM64v8)
Instrukcje ARM64 mają ogólnie format opcode dst, src1, src2
, gdzie opcode
to operacja do wykonania (takie jak add
, sub
, mov
, itp.), dst
to rejestr docelowy, w którym zostanie przechowany wynik, a src1
i src2
to rejestry źródłowe. Wartości natychmiastowe mogą również być używane zamiast rejestrów źródłowych.
mov
: Przenieś wartość z jednego rejestru do drugiego.Przykład:
mov x0, x1
— To przenosi wartość zx1
dox0
.ldr
: Załaduj wartość z pamięci do rejestru.Przykład:
ldr x0, [x1]
— To ładuje wartość z lokalizacji pamięci wskazywanej przezx1
dox0
.Tryb z przesunięciem: Wskazuje przesunięcie wpływające na wskaźnik oryginału, na przykład:
ldr x2, [x1, #8]
, to załaduje do x2 wartość z x1 + 8ldr x2, [x0, x1, lsl #2]
, to załaduje do x2 obiekt z tablicy x0, z pozycji x1 (indeks) * 4Tryb z wstępnym indeksowaniem: To zastosuje obliczenia do oryginału, uzyska wynik i przechowa nowy oryginał w oryginale.
ldr x2, [x1, #8]!
, to załadujex1 + 8
dox2
i przechowa w x1 wynikx1 + 8
str lr, [sp, #-4]!
, Przechowuje rejestr łącza w sp i aktualizuje rejestr spTryb z indeksowaniem po wykonaniu: Jest podobny do poprzedniego, ale adres pamięci jest dostępny, a następnie obliczane i przechowywane jest przesunięcie.
ldr x0, [x1], #8
, załadujx1
dox0
i zaktualizuj x1 nax1 + 8
Adresowanie względem PC: W tym przypadku adres do załadowania jest obliczany względem rejestru PC
ldr x1, =_start
, To załaduje adres, w którym zaczyna się symbol_start
w x1 względem bieżącego PC.str
: Zapisz wartość z rejestru do pamięci.Przykład:
str x0, [x1]
— To zapisuje wartość zx0
do lokalizacji pamięci wskazywanej przezx1
.ldp
: Załaduj parę rejestrów. Ta instrukcja ładuje dwa rejestry z kolejnych lokalizacji pamięci. Adres pamięci jest zazwyczaj tworzony przez dodanie przesunięcia do wartości w innym rejestrze.Przykład:
ldp x0, x1, [x2]
— To ładujex0
ix1
z lokalizacji pamięci wx2
ix2 + 8
, odpowiednio.stp
: Zapisz parę rejestrów. Ta instrukcja zapisuje dwa rejestry do kolejnych lokalizacji pamięci. Adres pamięci jest zazwyczaj tworzony przez dodanie przesunięcia do wartości w innym rejestrze.Przykład:
stp x0, x1, [sp]
— To zapisujex0
ix1
do lokalizacji pamięci wsp
isp + 8
, odpowiednio.stp x0, x1, [sp, #16]!
— To zapisujex0
ix1
do lokalizacji pamięci wsp+16
isp + 24
, odpowiednio, i aktualizujesp
nasp+16
.add
: Dodaj wartości dwóch rejestrów i przechowaj wynik w rejestrze.Składnia: add(s) Xn1, Xn2, Xn3 | #imm, [przesunięcie #N | RRX]
Xn1 -> Cel
Xn2 -> Operand 1
Xn3 | #imm -> Operand 2 (rejestr lub natychmiastowy)
[przesunięcie #N | RRX] -> Wykonaj przesunięcie lub wywołaj RRX
Przykład:
add x0, x1, x2
— Dodaje wartości wx1
ix2
i przechowuje wynik wx0
.add x5, x5, #1, lsl #12
— To równa się 4096 (1 przesunięte 12 razy) -> 1 0000 0000 0000 0000adds
Wykonuje operacjęadd
i aktualizuje flagisub
: Odejmowanie wartości dwóch rejestrów i przechowywanie wyniku w rejestrze.Sprawdź składnię
add
.Przykład:
sub x0, x1, x2
— Odejmuje wartość wx2
odx1
i przechowuje wynik wx0
.subs
To jak sub, ale aktualizuje flagęmul
: Mnożenie wartości dwóch rejestrów i przechowywanie wyniku w rejestrze.Przykład:
mul x0, x1, x2
— Mnoży wartości wx1
ix2
i przechowuje wynik wx0
.div
: Dzielenie wartości jednego rejestru przez drugi i przechowywanie wyniku w rejestrze.Przykład:
div x0, x1, x2
— Dzieli wartość wx1
przezx2
i przechowuje wynik wx0
.lsl
,lsr
,asr
,ror
,rrx
:Przesunięcie logiczne w lewo: Dodaje 0 z końca, przesuwając inne bity do przodu (mnożenie n razy przez 2)
Przesunięcie logiczne w prawo: Dodaje 1 z początku, przesuwając inne bity do tyłu (dzielenie n razy przez 2 w przypadku liczb bez znaku)
Przesunięcie arytmetyczne w prawo: Podobne do
lsr
, ale zamiast dodawania zer, jeśli najbardziej znaczący bit to 1, **dodawane są 1 (**dzielenie przez n razy 2 w przypadku liczb ze znakiem)Obrót w prawo: Podobne do
lsr
, ale to, co jest usuwane z prawej strony, jest dołączane z lewejObrót w prawo z rozszerzeniem: Podobne do
ror
, ale z flagą przeniesienia jako "najbardziej znaczący bit". Więc flaga przeniesienia jest przenoszona do bitu 31, a usunięty bit do flagi przeniesienia.bfm
: Przeniesienie pola bitowego, te operacje kopiują bity0...n
z wartości i umieszczają je na pozycjachm..m+n
.#s
określa najbardziej lewą pozycję bitu a#r
ilość przesunięcia w prawo.Przeniesienie pola bitowego:
BFM Xd, Xn, #r
Przeniesienie pola bitowego ze znakiem:
SBFM Xd, Xn, #r, #s
Przeniesienie pola bitowego bez znaku:
UBFM Xd, Xn, #r, #s
Wyciąganie i wstawianie pola bitowego: Kopiowanie pola bitowego z rejestru i kopiowanie go do innego rejestru.
BFI X1, X2, #3, #4
Wstawia 4 bity z X2 od 3. bitu X1BFXIL X1, X2, #3, #4
Wyciąga z 3. bitu X2 cztery bity i kopiuje je do X1SBFIZ X1, X2, #3, #4
Rozszerza znakowo 4 bity z X2 i wstawia je do X1 zaczynając od pozycji bitu 3, zerując prawe bitySBFX X1, X2, #3, #4
Wyciąga 4 bity zaczynając od bitu 3 z X2, rozszerza znakowo i umieszcza wynik w X1UBFIZ X1, X2, #3, #4
Rozszerza zerami 4 bity z X2 i wstawia je do X1 zaczynając od pozycji bitu 3, zerując prawe bityUBFX X1, X2, #3, #4
Wyciąga 4 bity zaczynając od bitu 3 z X2 i umieszcza wynik zerowo rozszerzony w X1.Rozszerzanie znaku do X: Rozszerza znak (lub dodaje tylko zera w wersji bez znaku) wartości, aby można było wykonywać na niej operacje:
SXTB X1, W2
Rozszerza znak bajtu z W2 do X1 (W2
to połowaX2
) wypełniając 64 bitySXTH X1, W2
Rozszerza znak 16-bitowej liczby z W2 do X1 wypełniając 64 bitySXTW X1, W2
Rozszerza znak bajtu z W2 do X1 wypełniając 64 bityUXTB X1, W2
Dodaje zera (bez znaku) do bajtu z W2 do X1 wypełniając 64 bityextr
: Wyciąga bity z określonej pary złączonych rejestrów.Przykład:
EXTR W3, W2, W1, #3
To połączy W1+W2 i pobierze od bitu 3 z W2 do bitu 3 z W1 i przechowa w W3.cmp
: Porównuje dwa rejestry i ustawia flagi warunkowe. Jest to aliassubs
ustawiający rejestr docelowy na zerowy rejestr. Przydatne do sprawdzenia, czym == n
.Obsługuje tę samą składnię co
subs
Przykład:
cmp x0, x1
— Porównuje wartości wx0
ix1
i ustawia flagi warunkowe odpowiednio.cmn
: Porównaj ujemne operandy. W tym przypadku jest to aliasadds
i obsługuje tę samą składnię. Przydatne do sprawdzenia, czym == -n
.ccmp
: Porównanie warunkowe, to porównanie, które zostanie wykonane tylko wtedy, gdy poprzednie porównanie było prawdziwe, i będzie specjalnie ustawiać bity nzcv.cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> jeśli x1 != x2 i x3 < x4, skocz do funkcjiDzieje się tak, ponieważ
ccmp
zostanie wykonane tylko wtedy, gdy poprzedniecmp
byłoNE
, jeśli nie, bitynzcv
zostaną ustawione na 0 (co nie spełni warunkublt
).Można go również używać jako
ccmn
(to samo, ale ujemne, jakcmp
vscmn
).tst
: Sprawdza, czy któreś z wartości porównania jest równe 1 (działa jak i ANDS bez przechowywania wyniku). Przydatne do sprawdzenia rejestru z wartością i sprawdzenia, czy którykolwiek z bitów rejestru wskazanego w wartości jest równy 1.Przykład:
tst X1, #7
Sprawdza, czy którykolwiek z ostatnich 3 bitów X1 jest równy 1teq
: Operacja XOR, odrzucająca wynikb
: Bezwarunkowy skokPrzykład:
b myFunction
Należy zauważyć, że nie wypełni to rejestru linku adresem powrotu (nieodpowiednie do wywołań podprogramów, które muszą wrócić)
bl
: Skok z linkiem, używany do wywołania podprogramu. Przechowuje adres powrotu wx30
.Przykład:
bl myFunction
— To wywołuje funkcjęmyFunction
i przechowuje adres powrotu wx30
.Należy zauważyć, że nie wypełni to rejestru linku adresem powrotu (nieodpowiednie do wywołań podprogramów, które muszą wrócić)
blr
: Skok z linkiem do rejestru, używany do wywołania podprogramu, gdzie cel jest określony w rejestrze. Przechowuje adres powrotu wx30
. (To jestPrzykład:
blr x1
— To wywołuje funkcję, której adres jest zawarty wx1
i przechowuje adres powrotu wx30
.ret
: Powrót z podprogramu, zwykle używając adresu wx30
.Przykład:
ret
— To powraca z bieżącego podprogramu, używając adresu powrotu zx30
.b.<cond>
: Warunkowe skokib.eq
: Skok jeśli równy, na podstawie poprzedniej instrukcjicmp
.Przykład:
b.eq label
— Jeśli poprzednia instrukcjacmp
znalazła dwie równe wartości, to skacze dolabel
.b.ne
: Branch if Not Equal. Ta instrukcja sprawdza flagi warunkowe (które zostały ustawione przez poprzednią instrukcję porównania) i jeśli porównywane wartości nie były równe, skacze do etykiety lub adresu.Przykład: Po instrukcji
cmp x0, x1
,b.ne label
— Jeśli wartości wx0
ix1
nie były równe, następuje skok dolabel
.cbz
: Porównaj i Skocz jeśli Zero. Ta instrukcja porównuje rejestr z zerem i jeśli są równe, skacze do etykiety lub adresu.Przykład:
cbz x0, label
— Jeśli wartość wx0
wynosi zero, następuje skok dolabel
.cbnz
: Porównaj i Skocz jeśli Nie-Zero. Ta instrukcja porównuje rejestr z zerem i jeśli nie są równe, skacze do etykiety lub adresu.Przykład:
cbnz x0, label
— Jeśli wartość wx0
jest niezerowa, następuje skok dolabel
.tbnz
: Testuj bit i skacz jeśli niezerowyPrzykład:
tbnz x0, #8, label
tbz
: Testuj bit i skacz jeśli zeroPrzykład:
tbz x0, #8, label
Operacje wyboru warunkowego: Są to operacje, których zachowanie zależy od bitów warunkowych.
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> Jeśli prawda, X0 = X1, jeśli fałsz, X0 = X2csinc Xd, Xn, Xm, cond
-> Jeśli prawda, Xd = Xn, jeśli fałsz, Xd = Xm + 1cinc Xd, Xn, cond
-> Jeśli prawda, Xd = Xn + 1, jeśli fałsz, Xd = Xncsinv Xd, Xn, Xm, cond
-> Jeśli prawda, Xd = Xn, jeśli fałsz, Xd = NOT(Xm)cinv Xd, Xn, cond
-> Jeśli prawda, Xd = NOT(Xn), jeśli fałsz, Xd = Xncsneg Xd, Xn, Xm, cond
-> Jeśli prawda, Xd = Xn, jeśli fałsz, Xd = - Xmcneg Xd, Xn, cond
-> Jeśli prawda, Xd = - Xn, jeśli fałsz, Xd = Xncset Xd, Xn, Xm, cond
-> Jeśli prawda, Xd = 1, jeśli fałsz, Xd = 0csetm Xd, Xn, Xm, cond
-> Jeśli prawda, Xd = <wszystkie 1>, jeśli fałsz, Xd = 0adrp
: Oblicz adres strony symbolu i zapisz go w rejestrze.Przykład:
adrp x0, symbol
— To oblicza adres stronysymbolu
i zapisuje go wx0
.ldrsw
: Załaduj podpisaną wartość 32-bitową z pamięci i rozszerz ją do 64 bitów.Przykład:
ldrsw x0, [x1]
— To ładuje podpisaną wartość 32-bitową z lokalizacji pamięci wskazywanej przezx1
, rozszerza ją do 64 bitów i zapisuje wx0
.stur
: Zapisz wartość rejestru do lokalizacji pamięci, używając przesunięcia od innego rejestru.Przykład:
stur x0, [x1, #4]
— To zapisuje wartość zx0
do lokalizacji pamięci, która jest o 4 bajty większa niż adres aktualnie wx1
.svc
: Wykonaj wywołanie systemowe. Oznacza "Supervisor Call". Gdy procesor wykonuje tę instrukcję, przełącza się z trybu użytkownika na tryb jądra i skacze do określonej lokalizacji w pamięci, gdzie znajduje się kod obsługi wywołania systemowego jądra.Przykład:
Prolog Funkcji
Zapisz rejestr linku i wskaźnik ramki na stosie:
Ustaw nowy wskaźnik ramki:
mov x29, sp
(ustawia nowy wskaźnik ramki dla bieżącej funkcji)Zaalokuj miejsce na stosie dla zmiennych lokalnych (jeśli jest to konieczne):
sub sp, sp, <size>
(gdzie<size>
to liczba bajtów potrzebna)
Epilog Funkcji
Zwolnij zmienne lokalne (jeśli jakiekolwiek zostały zaalokowane):
add sp, sp, <size>
Przywróć rejestr linku i wskaźnik ramki:
Powrót:
ret
(zwraca kontrolę do wywołującego, używając adresu w rejestrze łącza)
Stan Wykonania AARCH32
Armv8-A obsługuje wykonywanie programów 32-bitowych. AArch32 może działać w jednym z dwóch zestawów instrukcji: A32
i T32
oraz może przełączać się między nimi za pomocą interworking
.
Uprawnione programy 64-bitowe mogą zaplanować wykonanie programów 32-bitowych wykonując transfer poziomu wyjątku do programu o niższych uprawnieniach 32-bitowego.
Należy zauważyć, że przejście z 64-bitowego na 32-bitowe zachodzi przy niższym poziomie wyjątku (na przykład program 64-bitowy w EL1 wywołujący program w EL0). Jest to realizowane poprzez ustawienie bitu 4 rejestru specjalnego SPSR_ELx
na 1 gdy wątek procesu AArch32
jest gotowy do wykonania, a reszta SPSR_ELx
przechowuje stany CPSR programów AArch32
. Następnie uprawniony proces wywołuje instrukcję ERET
, aby procesor przełączył się na AArch32
, wchodząc w A32 lub T32 w zależności od CPSR**.**
Interworking
zachodzi za pomocą bitów J i T CPSR. J=0
i T=0
oznacza A32
, a J=0
i T=1
oznacza T32. Oznacza to w zasadzie ustawienie najniższego bitu na 1, aby wskazać, że zestaw instrukcji to T32.
Jest to ustawiane podczas instrukcji skoku interworking, ale można to również ustawić bezpośrednio za pomocą innych instrukcji, gdy PC jest ustawiony jako rejestr docelowy. Przykład:
Kolejny przykład:
Rejestry
Istnieje 16 rejestrów 32-bitowych (r0-r15). Od r0 do r14 mogą być używane do dowolnych operacji, jednak niektóre z nich są zazwyczaj zarezerwowane:
r15
: Licznik programu (zawsze). Zawiera adres następnej instrukcji. W A32 aktualny + 8, w T32 aktualny + 4.r11
: Wskaźnik ramkir12
: Rejestr wywołania wewnątrzproceduralnegor13
: Wskaźnik stosur14
: Rejestr łącza
Ponadto rejestry są tworzone w rejestrach bankowych
. Są to miejsca przechowujące wartości rejestrów, umożliwiające szybką zmianę kontekstu w obsłudze wyjątków i operacjach uprzywilejowanych, aby uniknąć konieczności ręcznego zapisywania i przywracania rejestrów za każdym razem.
Dzieje się to poprzez zapisanie stanu procesora z CPSR
do SPSR
trybu procesora, do którego jest wykonywany wyjątek. Po powrocie z wyjątku, CPSR
jest przywracany z SPSR
.
CPSR - Bieżący Rejestr Stanu Programu
W AArch32 CPSR działa podobnie jak PSTATE
w AArch64 i jest również przechowywany w SPSR_ELx
podczas obsługi wyjątku w celu późniejszego przywrócenia wykonania:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F600722126-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FJtticLwENZ4i7WwgHiQe%252Fuploads%252Fgit-blob-c31c79e9c30b4ff84b9f85d47ad7ae374214e85d%252Fimage%2520%281197%29.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=bca5198f&sv=1)
Pola są podzielone na kilka grup:
Rejestr Stanu Programu Aplikacji (APSR): Flagi arytmetyczne dostępne z EL0
Rejestry Stanu Wykonania: Zachowanie procesu (zarządzane przez system operacyjny).
Rejestr Stanu Programu Aplikacji (APSR)
Flagi
N
,Z
,C
,V
(tak jak w AArch64)Flaga
Q
: Jest ustawiana na 1, gdy występuje nasycenie liczb całkowitych podczas wykonywania specjalnej instrukcji arytmetyki nasycającej. Gdy raz zostanie ustawiona na1
, zachowa wartość do momentu ręcznego ustawienia na 0. Ponadto nie ma żadnej instrukcji, która sprawdzałaby jej wartość w sposób domyślny, należy odczytać ją ręcznie.GE
(Większe lub równe) Flagi: Są używane w operacjach SIMD (Single Instruction, Multiple Data), takich jak "dodawanie równoległe" i "odejmowanie równoległe". Te operacje pozwalają przetwarzać wiele punktów danych w pojedynczej instrukcji.
Na przykład instrukcja UADD8
dodaje cztery pary bajtów (z dwóch operandów 32-bitowych) równolegle i przechowuje wyniki w rejestrze 32-bitowym. Następnie ustawia flagi GE
w APSR
na podstawie tych wyników. Każda flaga GE odpowiada jednemu z dodawanych bajtów, wskazując, czy dodawanie dla tej pary bajtów przekroczyło zakres.
Instrukcja SEL
używa tych flag GE do wykonywania działań warunkowych.
Rejestry Stanu Wykonania
Bity
J
iT
:J
powinno być 0, a jeśliT
jest 0, używany jest zestaw instrukcji A32, a jeśli jest 1, używany jest zestaw instrukcji T32.Rejestr Stanu Bloku IT (
ITSTATE
): Są to bity od 10-15 i 25-26. Przechowują one warunki dla instrukcji wewnątrz grupy z prefiksemIT
.Bit
E
: Wskazuje kolejność bajtów.Bity Trybu i Maska Wyjątku (0-4): Określają bieżący stan wykonania. Piąty wskazuje, czy program działa jako 32-bitowy (1) czy 64-bitowy (0). Pozostałe 4 reprezentują tryb wyjątku obecnie używany (gdy występuje wyjątek i jest obsługiwany). Ustawiona liczba określa bieżący priorytet w przypadku wywołania innego wyjątku podczas obsługi tego.
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F600722126-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FJtticLwENZ4i7WwgHiQe%252Fuploads%252Fgit-blob-23ab4ba8cd085769cf6cfa1627f5f5d7c2a747b6%252Fimage%2520%281200%29.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=47feb95d&sv=1)
AIF
: Pewne wyjątki mogą być wyłączone za pomocą bitówA
,I
,F
. JeśliA
wynosi 1, oznacza to, że zostaną wywołane przerwania asynchroniczne.I
konfiguruje odpowiedź na zewnętrzne żądania przerwań sprzętowych (IRQ), a F dotyczy szybkich żądań przerwania (FIR).
macOS
Wywołania systemowe BSD
Sprawdź syscalls.master. Wywołania systemowe BSD będą miały x16 > 0.
Pułapki Mach
Sprawdź w syscall_sw.c mach_trap_table
oraz w mach_traps.h prototypy. Maksymalna liczba pułapek Mach to MACH_TRAP_TABLE_COUNT
= 128. Pułapki Mach będą miały x16 < 0, dlatego należy wywoływać numery z poprzedniej listy z użyciem znaku minus: _kernelrpc_mach_vm_allocate_trap
to -10
.
Możesz również sprawdzić libsystem_kernel.dylib
w deasemblerze, aby dowiedzieć się, jak wywołać te (i BSD) wywołania systemowe:
Czasami łatwiej jest sprawdzić zdekompilowany kod z libsystem_kernel.dylib
niż sprawdzanie kodu źródłowego, ponieważ kod kilku wywołań systemowych (BSD i Mach) jest generowany za pomocą skryptów (sprawdź komentarze w kodzie źródłowym), podczas gdy w pliku dylib można znaleźć, co jest wywoływane.
wywołania machdep
XNU obsługuje inny rodzaj wywołań zwany zależnym od maszyny. Liczba tych wywołań zależy od architektury i ani wywołania, ani ich liczby nie są gwarantowane, że pozostaną stałe.
strona comm
Jest to strona pamięci właściciela jądra, która jest odwzorowana w przestrzeni adresowej każdego procesu użytkownika. Ma to na celu przyspieszenie przejścia z trybu użytkownika do przestrzeni jądra szybciej niż przy użyciu wywołań systemowych do usług jądra, które są używane tak często, że to przejście byłoby bardzo nieefektywne.
Na przykład wywołanie gettimeofdate
odczytuje wartość timeval
bezpośrednio ze strony comm.
objc_msgSend
To bardzo często spotykana funkcja używana w programach Objective-C lub Swift. Ta funkcja pozwala na wywołanie metody obiektu Objective-C.
Parametry (więcej informacji w dokumentacji):
x0: self -> Wskaźnik do instancji
x1: op -> Selektor metody
x2... -> Pozostałe argumenty wywołanej metody
Więc jeśli ustawisz punkt przerwania przed skokiem do tej funkcji, łatwo można znaleźć, co jest wywoływane w lldb za pomocą (w tym przykładzie obiekt wywołuje obiekt z NSConcreteTask
, który uruchomi polecenie):
Ustawienie zmiennej środowiskowej NSObjCMessageLoggingEnabled=1
pozwala zalogować, kiedy ta funkcja jest wywoływana w pliku takim jak /tmp/msgSends-pid
.
Ponadto, ustawienie OBJC_HELP=1
i wywołanie dowolnego pliku binarnego pozwala zobaczyć inne zmienne środowiskowe, które można użyć do logowania wystąpienia określonych akcji Objc-C.
Gdy ta funkcja jest wywoływana, konieczne jest znalezienie wywołanej metody wskazanego egzemplarza, w tym celu dokonywane są różne wyszukiwania:
Wykonaj optymistyczne wyszukiwanie w pamięci podręcznej:
Jeśli sukces, zakończ
Pozyskaj runtimeLock (odczyt)
Jeśli (realize && !cls->realized) zrealizuj klasę
Jeśli (initialize && !cls->initialized) zainicjuj klasę
Spróbuj pamięć podręczną klasy:
Jeśli sukces, zakończ
Spróbuj listę metod klasy:
Jeśli znaleziono, wypełnij pamięć podręczną i zakończ
Spróbuj pamięć podręczną nadklasy:
Jeśli sukces, zakończ
Spróbuj listę metod nadklasy:
Jeśli znaleziono, wypełnij pamięć podręczną i zakończ
Jeśli (resolver) spróbuj rozwiązujący metodę i powtórz od wyszukiwania klasy
Jeśli nadal tutaj (= wszystko inne zawiodło) spróbuj forwarder
Kody Shell
Do kompilacji:
Aby wydobyć bajty:
Dla nowszych wersji macOS:
Last updated