
ein iOS-Kernel-Funktions-Hooking-Framework für checkra1n-fähige Geräte

Ausgabe aus dem Kernel-Log nach dem Kompilieren und Ausführen von example/open1_hook.c
xnuspy ist ein pongoOS-Modul, das einen neuen Systemaufruf, xnuspy_ctl, installiert, mit dem Sie Kernel-Funktionen aus dem Userspace hooken können. Es unterstützt iOS 13.x, iOS 14.x und iOS 15.x auf checkra1n 0.12.2 und höher. 4K-Geräte werden nicht unterstützt.
Dieses Modul macht KTRR/KPP vollständig unwirksam und ermöglicht es, RWX-Speicher innerhalb von EL1 zu erstellen. Verwenden Sie dies nicht auf Ihrem Hauptgerät.
Erfordert libusb: brew install libusb
Führen Sie make im obersten Verzeichnis aus. Es wird den Loader und das Modul bauen.
Fügen Sie diese vor make hinzu.
XNUSPY_DEBUG=1
kprintf).XNUSPY_SERIAL=1
IOLog senden.XNUSPY_LEAKED_PAGE_LIMIT=n
64. Weitere Informationen finden Sie unter Debugging von Kernel-Panics.XNUSPY_TRAMP_PAGES=n
XNUSPY_DEBUG und XNUSPY_SERIAL sind voneinander unabhängig.
Nachdem Sie alles gebaut haben, lassen Sie checkra1n Ihr Gerät in eine pongo-Shell booten: /Applications/checkra1n.app/Contents/MacOS/checkra1n -p
Führen Sie im selben Verzeichnis, in dem Sie den Loader und das Modul gebaut haben, loader/loader module/xnuspy aus. Danach wird xnuspy seine Arbeit verrichten und in einigen Sekunden wird Ihr Gerät booten. Der loader wartet nach dem Senden von xnuspy-getkernelv noch ein paar Sekunden, falls SEPROM ausgenutzt werden muss.
Manchmal bleiben ein paar meiner Telefone nach dem Ausführen von checkra1ns KPF bei "Booting" hängen. Ich habe noch nicht herausgefunden, was das verursacht, aber falls es passiert, versuchen Sie es erneut. Wenn das Gerät nach bootx hängt, versuchen Sie es ebenfalls erneut. Schließlich ist das Markieren des kompilierten xnuspy_ctl-Codes als ausführbar auf meinem iPhone X mit iOS 13.3.1 etwas unzuverlässig, funktioniert aber auf meinen anderen Telefonen zu 100%. Wenn Sie beim Ausführen Ihres Hook-Programms einen Kernel Instruction Fetch Absturz haben, versuchen Sie es erneut.
xnuspy wird einen enosys-Systemaufruf patchen, sodass er auf xnuspy_ctl_tramp verweist. Dies ist ein kleines Trampolin, das den kompilierten xnuspy_ctl-Code als ausführbar markiert und zu ihm springt. Sie finden die Implementierung von xnuspy_ctl unter module/el1/xnuspy_ctl/xnuspy_ctl.c und Beispiele im example-Verzeichnis.
In include/xnuspy/ befindet sich xnuspy_ctl.h, ein Header, der Konstanten für xnuspy_ctl definiert. Er soll in alle Programme eingebunden werden, die Kernel-Funktionen hooken.
Sie können sysctlbyname verwenden, um herauszufinden, welcher Systemaufruf gepatcht wurde:```
size_t oldlen = sizeof(long);
long SYS_xnuspy_ctl = 0;
sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, &oldlen, NULL, 0);
Dieser Systemaufruf hat vier Argumente: `flavor`, `arg1`, `arg2` und `arg3`.
Der `flavor` kann entweder `XNUSPY_CHECK_IF_PATCHED`, `XNUSPY_INSTALL_HOOK`,
`XNUSPY_REGISTER_DEATH_CALLBACK`, `XNUSPY_CALL_HOOKME`, `XNUSPY_CACHE_READ`,
`XNUSPY_KREAD`, `XNUSPY_KWRITE` oder `XNUSPY_GET_CURRENT_THREAD` sein.
Die Bedeutung der drei folgenden Argumente hängt vom `flavor` ab.
## `XNUSPY_CHECK_IF_PATCHED`
Dies existiert, damit Sie überprüfen können, ob `xnuspy_ctl` vorhanden ist. Die Verwendung mit diesem `flavor` führt zur Rückgabe von `999`. Die Werte der anderen Argumente werden ignoriert.
## `XNUSPY_INSTALL_HOOK`
Ich habe diesen `flavor` so entworfen, dass er der API von [`MSHookFunction`](http://www.cydiasubstrate.com/api/c/MSHookFunction/) entspricht.
`arg1` ist die *UNVERSCHOBENE* Adresse der Kernel-Funktion, die Sie hooken möchten. Wenn Sie eine verschobene Adresse angeben, wird es höchstwahrscheinlich zu einem Kernel-Panic kommen. `arg2` ist ein Zeiger auf Ihre ABI-kompatible Ersatzfunktion. `arg3` ist ein Zeiger für `xnuspy_ctl`, um die Adresse eines Trampolins, das die ursprüngliche Kernel-Funktion darstellt, mittels `copyout` auszugeben. Dies kann `NULL` sein, wenn Sie nicht beabsichtigen, das Original aufzurufen.
## `XNUSPY_REGISTER_DEATH_CALLBACK`
Dieser `flavor` ermöglicht es Ihnen, einen optionalen "Death-Callback" zu registrieren, eine Funktion, die xnuspy aufruft, wenn Ihr Hook-Programm beendet wird. Er gibt Ihnen die Möglichkeit, alles zu bereinigen, was Sie mit Ihren Kernel-Hooks erstellt haben. Falls Sie Kernel-Threads erstellt haben, teilen Sie ihnen in dieser Funktion mit, dass sie sich beenden sollen.
Ihr Callback wird nicht asynchron aufgerufen. Wenn Sie also blockieren, verhindern Sie die Ausführung des Garbage-Collection-Threads von xnuspy.
`arg1` ist ein Zeiger auf Ihre Callback-Funktion. Die Werte der anderen Argumente werden ignoriert.
## `XNUSPY_CALL_HOOKME`
`hookme` ist ein kleiner Assembly-Stub, den xnuspy über den xnuspy-Cache exportiert, damit Sie ihn hooken können. Der Aufruf von `xnuspy_ctl` mit diesem `flavor` führt dazu, dass `hookme` aufgerufen wird. Dies bietet eine Möglichkeit, einfach Kernel-Code auszuführen, ohne eine tatsächliche Kernel-Funktion hooken zu müssen.
`arg1` ist ein Argument, das beim Aufruf an `hookme` übergeben wird.
Dies kann `NULL` sein.
## `XNUSPY_CACHE_READ`
Dieser `flavor` gibt Ihnen eine Möglichkeit, aus dem xnuspy-Cache zu lesen. Er enthält viele nützliche Dinge wie `kprintf`, `current_proc`, `kernel_thread_start`, einige libc-Funktionen und den Kernel-Slide, sodass Sie sie nicht selbst finden müssen. Eine vollständige Liste der Cache-IDs finden Sie in `example/xnuspy_ctl.h`.
`arg1` ist eine der in `xnuspy_ctl.h` definierten Cache-IDs und `arg2` ist ein Zeiger für `xnuspy_ctl`, um die Adresse oder den Wert des Angeforderten mittels `copyout` auszugeben. Die Werte der anderen Argumente werden ignoriert.
## `XNUSPY_KREAD`
Dieser `flavor` bietet eine einfache Möglichkeit, aus dem Userspace Kernel-Speicher zu lesen, ohne tfp0.
`arg1` ist eine virtuelle Kernel-Adresse, `arg2` die Adresse eines Userspace-Puffers und `arg3` die Größe dieses Userspace-Puffers. Es werden `arg3` Bytes von `arg1` nach `arg2` geschrieben.
## `XNUSPY_KWRITE`
Dieser `flavor` bietet eine einfache Möglichkeit, aus dem Userspace in den Kernel-Speicher zu schreiben, ohne tfp0.
`arg1` ist eine virtuelle Kernel-Adresse, `arg2` die Adresse eines Userspace-Puffers und `arg3` die Größe dieses Userspace-Puffers. Es werden `arg3` Bytes von `arg2` nach `arg1` geschrieben.
## `XNUSPY_GET_CURRENT_THREAD`
Dieser `flavor` stellt dem Userspace die Kernel-Adresse des aufrufenden Threads zur Verfügung.
`arg1` ist ein Zeiger für `xnuspy_ctl`, um den Rückgabewert von `current_thread` mittels `copyout` auszugeben. Die Werte der anderen Argumente werden ignoriert.
### Fehler
Für alle `flavor` außer `XNUSPY_CHECK_IF_PATCHED` wird bei Erfolg `0` zurückgegeben. Bei einem Fehler wird `-1` zurückgegeben und `errno` gesetzt. `XNUSPY_CHECK_IF_PATCHED` gibt keine Fehler zurück. XNU's `mach_to_bsd_errno` wird verwendet, um einen `kern_return_t` in das entsprechende `errno` zu konvertieren.
#### Fehler im Zusammenhang mit `XNUSPY_INSTALL_HOOK`
`errno` wird gesetzt auf...
- `EEXIST`, wenn:
- Für die durch `arg1` bezeichnete unverschobene Kernel-Funktion bereits ein Hook existiert.
- `ENOMEM`, wenn:
- `unified_kalloc` `NULL` zurückgegeben hat.
- `ENOSPC`, wenn:
- Es keine freien `xnuspy_tramp`-Strukturen gibt, eine Datenstruktur innerhalb von xnuspy. Dies sollte nicht passieren, es sei denn, Sie hooken hunderte von Kernel-Funktionen *gleichzeitig*. Wenn Sie mehr Funktions-Hooks benötigen, lesen Sie [Grenzen](#limits).
- `ENOTSUP`, wenn:
- Der Aufrufer nicht von einer Mach-O-Datei (ausführbare Datei oder dynamische Bibliothek) stammt.
- `ENOENT`, wenn:
- `mh_for_addr` nicht in der Lage war, den Mach-O-Header zu bestimmen, der `arg2` innerhalb des Adressraums des Aufrufers entspricht.
- `EFAULT`, wenn:
- Der ermittelte Mach-O-Header tatsächlich kein Mach-O-Header ist. Dies wird wahrscheinlich nie passieren.
- `EIO`, wenn:
- `mach_make_memory_entry_64` keinen Speichereintrag für die Gesamtheit der ermittelten Segmente `__TEXT` und `__DATA` des Mach-0-Headers zurückgegeben hat.
`errno` hängt auch vom Rückgabewert von `vm_map_wire_external`, `mach_vm_map_external`, `mach_make_memory_entry_64`, `copyin`, `copyout` und gegebenenfalls der einmaligen Initialisierungsfunktion ab.
Wenn dieser `flavor` einen Fehler zurückgibt, wurde die Ziel-Kernel-Funktion nicht gehookt. Wenn Sie einen Nicht-`NULL`-Zeiger für `arg3` übergeben haben, wurde dieser möglicherweise initialisiert oder auch nicht. Es ist unsicher, ihn zu verwenden, wenn er initialisiert wurde.
#### Fehler im Zusammenhang mit `XNUSPY_REGISTER_DEATH_CALLBACK`
`errno` wird gesetzt auf...
- `ENOENT`, wenn:
- Der aufrufende Prozess keine Kernel-Funktionen gehookt hat.
Wenn dieser `flavor` einen Fehler zurückgibt, wurde Ihr Death-Callback nicht registriert.
#### Fehler im Zusammenhang mit `XNUSPY_CALL_HOOKME`
`errno` wird gesetzt auf...
- `ENOTSUP`, wenn:
- `hookme` zu weit von dem Speicher entfernt ist, der die `xnuspy_tramp`-Strukturen enthält. Dies wird innerhalb von pongoOS festgestellt und kann nur passieren, wenn xnuspy auf ungenutzten Code innerhalb des Kernelcaches zurückgreifen musste. In diesem Fall würde der Aufruf von `hookme` mit ziemlicher Sicherheit einen Kernel-Panic auslösen, und Sie müssen eine andere Kernel-Funktion zum Hooken finden.
Wenn dieser `flavor` einen Fehler zurückgibt, wurde `hookme` nicht aufgerufen.
#### Fehler im Zusammenhang mit `XNUSPY_CACHE_READ`
`errno` wird gesetzt auf...
- `EINVAL`, wenn:
- Die durch `arg1` bezeichnete Konstante nichts im Cache repräsentiert.
- `arg1` `IO_LOCK` war, aber der Kernel iOS 14.4.2 oder älter bzw. iOS 15.x ist.
- `arg1` `IPC_OBJECT_LOCK` war, aber der Kernel iOS 15.x ist.
- `arg1` `IPC_PORT_RELEASE_SEND` war, aber der Kernel iOS 14.5 oder neuer ist.
- `arg1` `IPC_PORT_RELEASE_SEND_AND_UNLOCK` war, aber der Kernel iOS 14.4.2 oder älter ist.
- `arg1` `KALLOC_CANBLOCK` war, aber der Kernel iOS 14.x oder neuer ist.
- `arg1` `KALLOC_EXTERNAL` war, aber der Kernel iOS 13.x ist.
- `arg1` `KFREE_ADDR` war, aber der Kernel iOS 14.x oder neuer ist.
- `arg1` `KFREE_EXT` war, aber der Kernel iOS 13.x ist.
- `arg1` `PROC_REF` war, aber der Kernel iOS 14.8 oder älter ist.
- `arg1` `PROC_REF_LOCKED` war, aber der Kernel iOS 15.x ist.
- `arg1` `PROC_RELE` war, aber der Kernel iOS 14.8 oder älter ist.
- `arg1` `PROC_RELE_LOCKED` war, aber der Kernel iOS 15.x ist.
- `arg1` `VM_MAP_UNWIRE` war, aber der Kernel iOS 15.x ist.
- `arg1` `VM_MAP_UNWIRE_NESTED` war, aber der Kernel iOS 14.8 oder älter ist.
`errno` hängt auch vom Rückgabewert von `copyout` und gegebenenfalls vom Rückgabewert der einmaligen Initialisierungsfunktion ab.
Wenn dieser `flavor` einen Fehler zurückgibt, wurde der von Ihnen für `arg2` übergebene Zeiger nicht initialisiert.
#### Fehler im Zusammenhang mit `XNUSPY_KREAD` und `XNUSPY_KWRITE`
`errno` wird gesetzt auf...
- `EFAULT`, wenn:
- Die Adressübersetzung für `arg1` oder `arg2` fehlgeschlagen ist. Wenn Sie mit `XNUSPY_DEBUG=1` kompiliert haben, wird eine entsprechende Meldung im Kernel-Log ausgegeben.
Wenn dieser `flavor` einen Fehler zurückgibt, wurde der Kernel-Speicher nicht gelesen/geschrieben.
#### Fehler im Zusammenhang mit `XNUSPY_GET_CURRENT_THREAD`
Wenn `copyout` fehlschlägt, wird `errno` auf dessen Rückgabewert gesetzt.
# Wichtige Informationen
### Häufige Fallstricke
Beim Schreiben von Ersatzfunktionen habe ich leicht vergessen, dass ich Kernel-Code schreibe. Hier sind ein paar Dinge, die Sie bei der Implementierung von Hooks beachten sollten:
- *Sie können keinen Userspace-Code ausführen, der sich außerhalb des `__TEXT`-Segments Ihres Programms befindet*. Es kommt zu einem Kernel-Panic, wenn Sie zum Beispiel versehentlich `printf` anstelle von `kprintf` aufrufen. Sie müssen jede libc-Funktion, die Sie aufrufen möchten, neu implementieren, sofern diese Funktion nicht bereits über `XNUSPY_CACHE_READ` verfügbar ist. Sie können jedoch Funktionszeiger auf andere Kernel-Funktionen erstellen und diese aufrufen.
- *Viele in Userspace-Code übliche Makros sind für den Kernel unsicher.* Beispielsweise erweitert sich `PAGE_SIZE` zu `vm_page_size`, nicht zu einer Konstante. Sie müssen PAN (auf A10+, was ich ebenfalls nicht empfehle) deaktivieren, bevor Sie diese Variable lesen, sonst kommt es zu einem Kernel-Panic.
- *Stellen Sie sicher, dass Sie Ihren Code mit `-fno-stack-protector` und `-D_FORTIFY_SOURCE=0` kompilieren.* In einigen Fällen muss das Gerät `___stack_chk_guard` lesen, indem es einen weiteren Userspace-Zeiger dereferenziert, was auf A10+ einen Kernel-Panic auslöst.
- *Um auf Nummer sicher zu gehen, kompilieren Sie Ihre Hook-Programme nicht mit Compiler-Optimierungen.*
Ein Blick auf https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/style/style.html wird ebenfalls empfohlen.
### Debugging von Kernel-Panics
Fehler sind beim Schreiben von Code unvermeidlich, also werden Sie irgendwann einen Kernel-Panic verursachen. Ein Panic muss nicht unbedingt einen Fehler in xnuspy bedeuten. Bevor Sie ein Issue eröffnen, stellen Sie bitte sicher, dass Sie immer noch einen Panic haben, wenn Sie nichts weiter tun, als die ursprüngliche Funktion aufzurufen und deren Wert zurückzugeben (falls erforderlich). Wenn Sie immer noch einen Panic haben, handelt es sich wahrscheinlich um einen xnuspy-Fehler (dann bitte ein Issue eröffnen). Wenn nicht, stimmt etwas mit Ihrer Ersatzfunktion nicht.
Da xnuspy die Ausführung nicht tatsächlich auf EL0-Seiten umleitet, ist das Debuggen eines Panics nicht ganz einfach. Öffnen Sie `module/el1/xnuspy_ctl/xnuspy_ctl.c` und fügen Sie kurz vor dem einzigen Aufruf von `kwrite_instr` in `xnuspy_install_hook` einen Aufruf von `IOSleep` für einige Sekunden ein. Dies geschieht, um sicherzustellen, dass genügend Zeit bleibt, bevor das Gerät einen Panic auslöst, damit Logs propagieren können. Kompilieren Sie xnuspy mit `XNUSPY_DEBUG=1 make -B` neu und laden Sie das Modul erneut. Wenn Sie das noch nicht getan haben, kompilieren Sie `klog` aus `klog/`. Laden Sie es auf Ihr Gerät hoch und führen Sie `stdbuf -o0 ./klog | grep shared_mapping_kva` aus. Starten Sie Ihr Hook-Programm erneut und achten Sie auf eine Zeile von `klog`, die wie folgt aussieht:
`shared_mapping_kva: dist 0x7af4 uaddr 0x104797af4 umh 0x104790000 kmh 0xfffffff00c90c000`
Wenn Sie mehr als einen Hook installieren, gibt es mehr als ein Vorkommen. In diesem Fall variieren `dist` und `uaddr`, aber `umh` und `kmh` nicht. `kmh` zeigt auf den Anfang der Kernel-Abbildung des `__TEXT`-Segments Ihres Programms. Werfen Sie Ihr Hook-Programm in Ihren bevorzugten Disassembler und basen Sie es neu ein, sodass sein Mach-O-Header an der Adresse von `kmh` liegt. Für IDA Pro ist das `Edit -> Segments -> Rebase program...` mit ausgewähltem `Image base`. Nachdem Ihr Gerät erneut einen Panic hatte und neu gestartet ist, können Sie, falls sich im Panic-Log Adressen befinden, die der Kernel-Abbildung Ihrer Ersatzfunktion entsprechen, diese mit der Disassemblierung abgleichen. Wenn es keine gibt, liegt wahrscheinlich eine subtile Speicherkorruption in Ihrer Ersatzfunktion vor.
xnuspy hat auch keine Möglichkeit zu wissen, ob ein Kernel-Thread nach der Deinstallation Ihrer Hooks noch auf der Kernel-Abbildung des `__TEXT`-Segments Ihres Programms läuft (oder laufen wird). Eine der Maßnahmen, die xnuspy ergreift, um damit umzugehen, besteht darin, diese Abbildung nicht sofort nach dem Beenden Ihres Hook-Programms freizugeben. Stattdessen wird sie am Ende einer Warteschlange hinzugefügt. Sobald der Garbage-Collection-Thread von xnuspy feststellt, dass ein bestimmtes Limit überschritten wurde, wie viele Seiten an Abbildungen in dieser Warteschlange gehalten werden, beginnt er, von vorne zu deallozieren und fährt damit fort, bis das Limit nicht mehr überschritten wird. Standardmäßig beträgt dieses Limit 1 MB oder 64 Seiten.
Dies hilft zwar enorm, aber je größer die Segmente `__TEXT` und `__DATA` Ihres Hook-Programms werden, desto geringer ist die Wahrscheinlichkeit, dass xnuspy dieses Rennen gewinnt. Wenn Sie regelmäßig einen Panic haben und ein eher großes Hook-Programm besitzen, versuchen Sie, dieses Limit zu erhöhen, indem Sie `XNUSPY_LEAKED_PAGE_LIMIT=n` vor `make` hinzufügen. Dies setzt das Limit auf `n` Seiten anstatt 64.
### Grenzen
xnuspy reserviert vor dem Booten von XNU eine Seite statischen Kernel-Speichers für seine `xnuspy_tramp`-Strukturen. Dadurch können Sie gleichzeitig etwa 225 Kernel-Funktionen hooken. Wenn Sie mehr möchten, können Sie `XNUSPY_TRAMP_PAGES=n` vor `make` hinzufügen. Dies weist xnuspy an, `n` Seiten statischen Speichers für `xnuspy_tramp`-Strukturen zu reservieren. Falls xnuspy jedoch auf ungenutzten Code im Kernelcache zurückgreifen muss, wird dies ignoriert. Wann dies passiert, wird in [Funktionsweise](#how-it-works) detailliert beschrieben.
### Logging
Aus irgendeinem Grund erscheinen Protokolle von `os_log_with_args` nicht im Ausgabestream des Kommandozeilentools `oslog`. Protokolle von `kprintf` schaffen es ebenfalls nicht dorthin, sind aber mit `dmesg` sichtbar. Allerdings ist `dmesg` kein Live-Feed, daher habe ich `klog` geschrieben, ein Tool, das `kprintf`-Protokolle in Echtzeit anzeigt. Sie finden es in `klog/`. Ich empfehle dringend, dies anstelle von ständigen `dmesg`-Aufrufen für Ihre `kprintf`-Meldungen zu verwenden.
Wenn Sie nach dem Ausführen von `klog` die Meldung `open: Resource busy` erhalten, führen Sie diesen Befehl aus: `launchctl unload /System/Library/LaunchDaemons/com.apple.syslogd.plist` und versuchen Sie es erneut.
Leider können Sie keine `NSLog`-Ausgaben sehen, wenn `atm_diagnostic_config=0x20000000` in den Bootargs von XNU gesetzt ist. `klog` ist auf dieses Boot-Argument angewiesen. Wenn Sie `NSLog` zurückhaben möchten, entfernen Sie dieses Boot-Argument aus `pongo_send_command` in `loader.c`.
### Deinstallation von Hooks
xnuspy wird dies für Sie erledigen. Sobald ein Prozess beendet wird, werden alle Kernel-Hooks, die von diesem Prozess installiert wurden, innerhalb von etwa einer Sekunde deinstalliert.
### Hookbare Kernel-Funktionen
Die meisten Funktions-Hooking-Frameworks haben eine Mindestlänge, die eine bestimmte Funktion hookbar macht. xnuspy hat dieses Limit *nur*, wenn Sie planen, die Originalfunktion aufzurufen *und* die erste Anweisung der gehookten Funktion nicht `B` ist. In diesem Fall beträgt die Mindestlänge acht Bytes. Andernfalls gibt es keine Mindestlänge.
xnuspy verwendet `X16` und `X17` für seine Trampoline. Daher können Kernel-Funktionen, die erwarten, dass diese über Funktionsaufrufe hinweg erhalten bleiben, nicht gehookt werden (es gibt nicht viele, die dies erwarten). Wenn die gewünschte Funktion mit `BL` beginnt und Sie das Original aufrufen möchten, können Sie dies nur tun, wenn die Ausführung der Originalfunktion `X17` nicht verändert.
### Thread-Sicherheit
`xnuspy_ctl` führt beim ersten Aufruf nach einem frischen Boot eine einmalige Initialisierung durch. Dies ist der einzige Teil von xnuspy, der race-anfällig ist, da ich die von mir verwendete Lese-/Schreibsperre nicht statisch initialisieren kann. Nach dem ersten Aufruf ist jeder zukünftige Aufruf garantiert thread-sicher.
# Funktionsweise
Dies ist vereinfacht, aber es erfasst die Hauptidee gut. Ein Funktions-Hook in xnuspy ist eine Struktur, die sich auf beschreibbarem, ausführbarem Kernel-Speicher befindet. In den meisten Fällen handelt es sich um Speicher, der von `alloc_static` innerhalb von pongoOS zurückgegeben wird. Es lässt sich wie folgt zusammenfassen:
struct { uint64_t replacement; uint32_t tramp[2]; uint32_t orig[10]; };
Wobei `replacement` die Kernel-Virtualadresse (später erläutert) der Ersatzfunktion ist, `tramp` ein kleiner Trampolin, der die Ausführung zu `replacement` umleitet, und `orig` ein größerer, komplizierterer Trampolin, der die ursprüngliche Funktion repräsentiert.
Eine der ersten Dinge, die xnuspy tut, ist zu bestimmen, wo sich der EL0-Ersatz im Adressraum des aufrufenden Prozesses befindet. Dies wird getan, damit Kernel-Funktionen aus dynamischen Bibliotheken heraus gehooked werden können. Der Mach-O-Header, der der Adresse dieses Ersatzes entspricht, wird gespeichert.
Danach wird eine gemeinsame User-Kernel-Abbildung der `__TEXT`- und `__DATA`-Segmente dieses Headers (sowie aller dazwischen liegenden Segmente, falls vorhanden) erstellt. `__TEXT` wird geteilt, damit Sie von Ihren Hooks aus andere Funktionen aufrufen können. `__DATA` wird geteilt, damit Änderungen an globalen Variablen sowohl von EL1 als auch von EL0 gesehen werden.
Da diese Abbildung eine eins-zu-eins-Kopie von `__TEXT` und `__DATA` ist, ist es einfach, die Adresse der Ersatzfunktion des Users darauf zu ermitteln. Gegeben die Adresse des Mach-O-Headers des aufrufenden Prozesses `u`, die Adresse des Beginns der gemeinsamen Abbildung `k` und die Adresse der Ersatzfunktion des Users `r`, wenden wir die folgende Formel an: `replacement = k + (r - u)`
Danach ist `replacement` die Kernel-Virtualadresse der Ersatzfunktion des Users auf der gemeinsamen Abbildung und wird in die Funktions-Hook-Struktur geschrieben. xnuspy leitet die Ausführung nicht auf die EL0-Adresse der Ersatzfunktion um, weil das extrem unsicher ist: Dadurch sind wir nicht nur dem Scheduler ausgeliefert, sondern haben auch keine Kontrolle über das Szenario, in dem ein Prozess mit einem Kernel-Hook stirbt, während ein Kernel-Thread noch auf dem Ersatz ausgeführt wird.
Schließlich wird die gemeinsame Abbildung als ausführbar markiert und eine unbedingte unmittelbare Verzweigung („B“) assembliert. Diese leitet die Ausführung an den Beginn von `tramp` und ersetzt die erste Instruktion der nun gehookten Kernel-Funktion. Leider schränkt uns dies darauf ein, Hook-Strukturen höchstens 128 MB von einer gegebenen Kernel-Funktion entfernt zu verzweigen. xnuspy prüft dieses Szenario vor dem Booten und fällt auf ungenutzten Code im Kernelcache zurück, auf dem die Hook-Strukturen stattdessen residieren, falls er feststellt, dass dies passieren könnte.
## Weitere Hinweise
Ich gebe mein Bestes, um sicherzustellen, dass die Patchfinder funktionieren. Wenn also etwas nicht funktioniert, eröffnen Sie bitte ein Issue.