
Ausgabe aus dem Kernel -Protokoll nach dem Kompilieren und Ausführen example/open1_hook.c
Xnuspy ist ein Pongoos -Modul, das einen neuen Systemanruf installiert, xnuspy_ctl , mit dem Sie Kernelfunktionen von UserSpace anschließen können. Es unterstützt iOS 13.x, iOS 14.x und iOS 15.x auf checkra1n 0.12.2 und up. 4K -Geräte werden nicht unterstützt.
Dieses Modul neutiert Ktrr/KPP vollständig und ermöglicht es, RWX -Speicher in EL1 zu erstellen. Verwenden Sie dies nicht bei Ihrem täglichen Fahrer.
Benötigt libusb : brew install libusb
Run make im oberen Ebenenverzeichnis. Es baut den Lader und das Modul auf.
Fügen Sie diese vor make hinzu.
XNUSPY_DEBUG=1kprintf ).XNUSPY_SERIAL=1IOLog .XNUSPY_LEAKED_PAGE_LIMIT=n64 . Weitere Informationen finden Sie unter Debugging Kernel Panics.XNUSPY_TRAMP_PAGES=n XNUSPY_DEBUG und XNUSPY_SERIAL hängen nicht voneinander ab.
Nachdem Sie alles gebaut haben, lassen Sie Ihr Gerät auf eine Pongo -Shell: /Applications/checkra1n.app/Contents/MacOS/checkra1n -p starten
In demselben Verzeichnis, in dem Sie den Loader und das Modul erstellt haben, machen Sie loader/loader module/xnuspy . Danach wird Xnuspy sein Ding tun und in wenigen Sekunden startet Ihr Gerät. loader wartet noch ein paar Sekunden nach der Ausgabe xnuspy-getkernelv , falls Seprom ausgenutzt werden muss.
Manchmal blieben ein paar meiner Telefone beim "Booten" nach Checkra1ns KPF -Läufen. Ich muss noch herausgefunden, was dies verursacht, aber wenn es passiert, versuchen Sie es erneut. Wenn das Gerät nach bootx hängt, versuchen Sie es erneut. Schließlich ist es etwas fleckig, den kompilierten xnuspy_ctl -Code als ausführbar auf meinem iPhone X zu markieren. Wenn Sie mit einem Kernel -Befehl in Panik geraten, wenn Sie Ihr Hook -Programm ausführen, versuchen Sie es erneut.
Xnuspy wird einen enosys -System -Aufruf patchen, um auf xnuspy_ctl_tramp zu verweisen. Dies ist ein kleines Trampolin, das den kompilierten xnuspy_ctl -Code als ausführbare Datei markiert und sich darauf verzweigt. Die Implementierung von xnuspy_ctl finden Sie unter module/el1/xnuspy_ctl/xnuspy_ctl.c und Beispiele im example .
include/xnuspy/ IS xnuspy_ctl.h , ein Header, der Konstanten für xnuspy_ctl definiert. Es soll in allen Programmen enthalten sein, die Kernel -Funktionen hängen.
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 erfordert vier Argumente, flavor , arg1 , arg2 und arg3 . Der Geschmack kann entweder XNUSPY_CHECK_IF_PATCHED sein, XNUSPY_INSTALL_HOOK , XNUSPY_REGISTER_DEATH_CALLBACK , XNUSPY_CALL_HOOKME , XNUSPY_CACHE_READ , XNUSPY_KREAD , XNUSPY_KWRITE , or XNUSPY_GET_CURRENT_THREAD . Die Bedeutung der nächsten drei Argumente hängt vom Geschmack ab.
XNUSPY_CHECK_IF_PATCHED Dies existiert, sodass Sie prüfen können, ob xnuspy_ctl vorhanden ist. Wenn Sie es mit diesem Geschmack anrufen, wird 999 zurückgekehrt. Die Werte der anderen Argumente werden ignoriert.
XNUSPY_INSTALL_HOOK Ich habe diesen Geschmack so entworfen, dass er die API von MSHookFunction entspricht. arg1 ist die unglückliche Adresse der Kernel -Funktion, die Sie anhängen möchten. Wenn Sie eine Slid -Adresse angeben, werden Sie höchstwahrscheinlich in Panik geraten. arg2 ist ein Zeiger auf Ihre ABI-kompatible Ersatzfunktion. arg3 ist ein Zeiger für xnuspy_ctl , um die Adresse eines Trampolins zu copyout , das die ursprüngliche Kernelfunktion darstellt. Dies kann NULL sein, wenn Sie nicht beabsichtigen, das Original anzurufen.
XNUSPY_REGISTER_DEATH_CALLBACKMit diesem Geschmack können Sie einen optionalen "Death Callback" registrieren, eine Funktion Xnuspy wird anrufen, wenn Ihr Hook -Programm beendet ist. Es gibt Ihnen die Möglichkeit, alles aufzuräumen, was Sie aus Ihren Kernelhaken erstellt haben. Wenn Sie Kernel -Threads erstellt haben, werden Sie ihnen sagen, dass sie in dieser Funktion enden sollen.
Ihr Rückruf wird nicht asynchron aufgerufen. Wenn Sie also blockieren, verhindern Sie, dass Xnuspys Müllsammlungs -Thread ausgeführt wird.
arg1 ist ein Zeiger auf Ihre Rückruffunktion. Die Werte der anderen Argumente werden ignoriert.
XNUSPY_CALL_HOOKME hookme ist ein kleiner Montage -Stummel, den Xnuspy durch den Xnuspy -Cache exportiert, damit Sie haken können. Durch das Aufrufen xnuspy_ctl mit diesem Geschmack wird die Anrufen von hookme dazu, dass Sie die Ausführung von Kernel -Code problemlos erhalten, ohne eine tatsächliche Kernelfunktion einzuhalten.
arg1 ist ein Argument, das beim Aufrufen an hookme übergeben wird. Dies kann NULL sein.
XNUSPY_CACHE_READ Dieser Geschmack gibt Ihnen eine Möglichkeit, aus dem Xnuspy -Cache zu lesen. Es enthält viele nützliche Dinge wie kprintf , current_proc , kernel_thread_start , einige LIBC -Funktionen und die Kernel -Folie, sodass Sie sie nicht selbst finden müssen. Eine vollständige Liste von 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 von Ihnen angeforderten Wertes zu copyout . Die Werte der anderen Argumente werden ignoriert.
XNUSPY_KREADDieser Geschmack bietet Ihnen eine einfache Möglichkeit, den Kernel -Speicher von UserSpace ohne TFP0 zu lesen.
arg1 ist eine virtuelle Kerneladresse, arg2 ist die Adresse eines UserSpace -Puffers und arg3 die Größe dieses UserSpace -Puffer. arg3 -Bytes werden von arg1 nach arg2 geschrieben.
XNUSPY_KWRITEDieser Geschmack bietet Ihnen eine einfache Möglichkeit, von UserSpace ohne TFP0 an den Kernel -Speicher zu schreiben.
arg1 ist eine virtuelle Kerneladresse, arg2 ist die Adresse eines UserSpace -Puffers und arg3 die Größe dieses UserSpace -Puffer. arg3 -Bytes werden von arg2 nach arg1 geschrieben.
XNUSPY_GET_CURRENT_THREADDieser Geschmack bietet Nutzern die Kerneladresse des aufrufenden Threads.
arg1 ist ein Zeiger für xnuspy_ctl , um den Rückgabewert von current_thread zu copyout . Die Werte der anderen Argumente werden ignoriert.
Für alle Geschmacksrichtungen mit Ausnahme XNUSPY_CHECK_IF_PATCHED wird 0 zum Erfolg zurückgegeben. Bei Fehler wird -1 zurückgegeben und errno gesetzt. XNUSPY_CHECK_IF_PATCHED gibt keine Fehler zurück. XNUs mach_to_bsd_errno wird verwendet, um einen kern_return_t in das entsprechende errno umzuwandeln.
XNUSPY_INSTALL_HOOK errno ist auf ...
EEXIST wenn:arg1 bezeichnete unglide Kernelfunktion.ENOMEM wenn:unified_kalloc gab NULL zurück.ENOSPC wenn:xnuspy_tramp -Strukturen, eine Datenstruktur für Xnuspy. Dies sollte nicht passieren, wenn Sie nicht gleichzeitig Hunderte von Kernelfunktionen anschließen. Wenn Sie mehr Funktionshaken benötigen, überprüfen Sie die Grenzen.ENOTSUP wenn:ENOENT wenn:mh_for_addr konnte den Mach-O-Header, arg2 im Adressraum des Anrufers entspricht, nicht bestimmen.EFAULT wenn:EIO IF:mach_make_memory_entry_64 gab keinen Speichereintrag für die gesamte bestimmte Segmente des Mach-O-Headers __TEXT und __DATA zurück. 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 die einmalige Initialisierungsfunktion ab.
Wenn dieser Geschmack einen Fehler zurückgibt, war die Zielkernfunktion nicht süchtig. Wenn Sie einen Nicht- NULL -Zeiger für arg3 bestanden haben, kann er initialisiert oder nicht. Es ist unsicher zu verwenden, wenn es so war.
XNUSPY_REGISTER_DEATH_CALLBACK errno ist auf ...
ENOENT wenn:Wenn dieser Geschmack einen Fehler zurückgibt, wurde Ihr Todesrückruf nicht registriert.
XNUSPY_CALL_HOOKME errno ist auf ...
ENOTSUP wenn:hookme ist zu weit vom Speicher entfernt, der die xnuspy_tramp -Strukturen enthält. Dies wird innerhalb von Pongoos bestimmt und kann nur dann auftreten, wenn Xnuspy einen nicht verwendeten Code fallen musste, der bereits im Kernelcache ist. In diesem Fall würde es mit ziemlicher Sicherheit eine Kernel hookme bezeichnen, und Sie müssen eine andere Kernelfunktion herausfinden, um zu haken. Wenn dieser Geschmack einen Fehler zurückgibt, wurde hookme nicht aufgerufen.
XNUSPY_CACHE_READ errno ist auf ...
EINVAL wenn:arg1 bezeichnete Konstante repräsentiert nichts im Cache.arg1 war IO_LOCK , aber der Kernel ist iOS 14.4.2 oder unter oder iOS 15.x.arg1 war IPC_OBJECT_LOCK , aber der Kernel ist iOS 15.x.arg1 war IPC_PORT_RELEASE_SEND , aber der Kernel ist iOS 14.5 oder höher.arg1 war IPC_PORT_RELEASE_SEND_AND_UNLOCK , aber der Kernel ist iOS 14.4.2 oder unter.arg1 war KALLOC_CANBLOCK , aber der Kernel ist iOS 14.x oder höher.arg1 war KALLOC_EXTERNAL , aber der Kernel ist iOS 13.x.arg1 war KFREE_ADDR , aber der Kernel ist iOS 14.x oder höher.arg1 war KFREE_EXT , aber der Kernel ist iOS 13.x.arg1 war PROC_REF , aber der Kernel ist iOS 14.8 oder unten.arg1 wurde PROC_REF_LOCKED , aber der Kernel ist iOS 15.x.arg1 war PROC_RELE , aber der Kernel ist iOS 14.8 oder unten.arg1 wurde PROC_RELE_LOCKED , aber der Kernel ist iOS 15.x.arg1 war VM_MAP_UNWIRE , aber der Kernel ist iOS 15.x.arg1 war VM_MAP_UNWIRE_NESTED , aber der Kernel ist iOS 14.8 oder unten. errno hängt auch vom Rückgabewert von copyout und gegebenenfalls vom Rückgabewert der Einmalinitialisierungsfunktion ab.
Wenn dieser Geschmack einen Fehler zurückgibt, wurde der Zeiger, den Sie für arg2 bestanden haben, nicht initialisiert.
XNUSPY_KREAD und XNUSPY_KWRITE errno ist auf ...
EFAULT wenn:arg1 oder arg2 fehlgeschlagen. Wenn Sie mit XNUSPY_DEBUG=1 kompiliert haben, wird eine Nachricht darüber zum Kernel -Protokoll gedruckt.Wenn dieser Geschmack einen Fehler zurückgibt, wurde der Kernel -Speicher nicht gelesen/geschrieben.
XNUSPY_GET_CURRENT_THREAD Wenn copyout fehlschlägt, wird errno auf seinen Rückgabewert gesetzt.
Beim Schreiben von Ersatzfunktionen war es leicht zu vergessen, dass ich Kernelcode schrieb. Hier sind ein paar Dinge, die Sie beachten sollten, wenn Sie Haken schreiben:
__TEXT -Segments Ihres Programms liegt . Sie werden in Panik geraten, wenn Sie beispielsweise das Versehen printf anstelle von kprintf anrufen. Sie müssen jede LIBC-Funktion, die Sie aufrufen möchten, erneut implementieren, wenn diese Funktion noch nicht über XNUSPY_CACHE_READ verfügbar ist. Sie können jedoch Funktionszeiger auf andere Kernelfunktionen erstellen und diese aufrufen.PAGE_SIZE auf vm_page_size , nicht auf eine Konstante. Sie müssen PAN (auf A10+, was ich auch nicht empfehle) deaktivieren müssen, bevor Sie diese Variable lesen, oder Sie werden in Panik geraten.-fno-stack-protector und -D_FORTIFY_SOURCE=0 kompilieren. In einigen Fällen muss das Gerät ___stack_chk_guard durch Dereferenzierung eines anderen Benutzerspace -Zeigers lesen, der auf A10+gergt wird.Skimming https://developer.apple.com/library/archive/documentation/darwin/conceptual/kernelprogramming/style/style.html wird ebenfalls empfohlen.
Fehler sind beim Schreiben von Code unvermeidlich, daher verursachen Sie irgendwann eine Kernel -Panik. Eine Panik bedeutet nicht unbedingt, dass es einen Fehler mit Xnuspy gibt. Bevor Sie ein Problem öffnen, stellen Sie bitte sicher, dass Sie immer noch in Panik geraten, wenn Sie nur die ursprüngliche Funktion anrufen und ihren Wert zurückgeben (falls erforderlich). Wenn Sie immer noch in Panik geraten, ist es wahrscheinlich ein Xnuspy -Fehler (und bitte öffnen Sie ein Problem), aber wenn nicht, stimmt etwas mit Ihrem Ersatz nicht.
Da Xnuspy die Ausführung nicht auf EL0 -Seiten weiterleitet, ist das Debugieren einer Panik nicht so einfach. Öffnen Sie module/el1/xnuspy_ctl/xnuspy_ctl.c und kurz vor dem einzigen Anruf an kwrite_instr in xnuspy_install_hook fügen Sie IOSleep für ein paar Sekunden einen Anruf hinzu. Dies geschieht, um sicherzustellen, dass genügend Zeit vor dem Gerät in Panik gebracht wird, damit sich Protokolle ausbreiten können. Neukompilieren Sie Xnuspy mit XNUSPY_DEBUG=1 make -B und laden Sie das Modul erneut. Kompilieren Sie nach dem Laden des Moduls, wenn Sie es noch nicht getan haben, klog aus klog/ . Laden Sie es auf Ihr Gerät hoch und machen Sie stdbuf -o0 ./klog | grep shared_mapping_kva . Führen Sie Ihr Hook -Programm erneut aus und achten Sie nach einer Zeile von klog , die so aussieht:
shared_mapping_kva: dist 0x7af4 uaddr 0x104797af4 umh 0x104790000 kmh 0xfffffff00c90c000
Wenn Sie mehr als einen Haken installieren, wird es mehr als ein Ereignis geben. In diesem Fall werden dist und uaddr variieren, aber umh und kmh werden es nicht tun. kmh weist auf den Beginn des Kernels zu, das das __TEXT -Segment Ihres Programms zuzuordnen. Werfen Sie Ihr Hakenprogramm in Ihren Lieblings-Disassembler und neu, damit der Mach-O-Header an der Adresse von kmh liegt. Für IDA Pro, das ist Edit -> Segments -> Rebase program... mit Image base sprudelt. Nachdem Ihr Gerät erneut in Panik und Neustart gepanet und neu gestartet wird. Wenn es Adressen gibt, die dem Kernel -Zuordnen Ihres Ersatzes im Panikprotokoll entsprechen, stimmen sie mit der Demontage überein. Wenn es keine gibt, haben Sie wahrscheinlich eine subtile Korruption von Speicher in Ihrem Ersatz.
Xnuspy hat auch keine Möglichkeit zu wissen, ob ein Kernel -Thread immer noch auf dem Kernel -Zuordnen des __TEXT -Segments Ihres Programms ausführt (oder ausführt), nachdem Ihre Haken deinstalliert sind. Eines der Dinge, die Xnuspy tun, um damit umzugehen, besteht darin, diese Zuordnung nicht unmittelbar nach dem Tod Ihres Hook -Programms zu bearbeiten. Stattdessen wird es am Ende einer Warteschlange hinzugefügt. Sobald Xnuspys Garbage Collection -Thread eine festgelegte Begrenzung darüber überschritten wurde, wie viele Seiten Abbildungen in dieser Warteschlange gehalten werden, wird er von der Warteschlange von der Warteschlange begegnet und wird fortgesetzt, bis dieses Grenze nicht mehr überschritten wird. Standardmäßig beträgt diese Grenze 1 MB oder 64 Seiten.
Dies hilft zwar enorm, je größer die __TEXT und __DATA -Segmente Ihres Hook -Programms werden, desto weniger wahrscheinlich gewinnt Xnuspy dieses Rennen. Wenn Sie regelmäßig in Panik geraten und ein etwas großes Hakenprogramm haben, versuchen Sie, diese Grenze zu erhöhen, indem Sie XNUSPY_LEAKED_PAGE_LIMIT=n hinzufügen, bevor make . Dadurch wird diese Grenze eher auf n Seiten als auf 64 festgelegt.
Xnuspy reserviert eine Seite des statischen Kernel -Speichers vor Xnu -Stiefeln für seine xnuspy_tramp -Strukturen, sodass Sie gleichzeitig 225 Kernelfunktionen anschließen können. Wenn Sie mehr möchten, können Sie vor make XNUSPY_TRAMP_PAGES=n hinzufügen. Dies wird Xnuspy zeigen, dass er n Seiten statischer Speicher für xnuspy_tramp -Strukturen reservieren soll. Wenn Xnuspy jedoch auf einen nicht verwendeten Code zurückfallen muss, der bereits im Kernelcache ist, wird dies ignoriert. Wenn dies geschieht, wird in der Funktionsweise detailliert.
Aus irgendeinem Grund werden Protokolle von os_log_with_args im Stream nicht angezeigt, der aus dem Befehlszeilen -Tool oslog ausgegeben wurde. Protokolle von kprintf schaffen es auch nicht dort, aber sie sind mit dmesg zu sehen. dmesg ist jedoch kein Live -Feed, also habe ich klog geschrieben, ein Tool, das kprintf -Protokolle in Echtzeit zeigt. Finden Sie es in klog/ . Ich empfehle dringend, das zu verwenden, anstatt dmesg für Ihre kprintf -Nachrichten zu spammen.
Wenn Sie open: Resource busy nach dem Ausführen klog , führen Sie diesen Befehl launchctl unload /System/Library/LaunchDaemons/com.apple.syslogd.plist aus und versuchen Sie es erneut.
Leider können Sie keine NSLog 's If atm_diagnostic_config=0x20000000 in XNUs Bootargs festlegen. klog hängt davon ab, dass dieses Startargument vorhanden ist. Wenn Sie NSLog zurück haben möchten, entfernen Sie das Boot -Argument von pongo_send_command in loader.c .
Xnuspy wird dies für Sie verwalten. Sobald ein Prozess beendet ist, werden alle Kernel -Haken, die durch diesen Vorgang installiert wurden, innerhalb einer Sekunde oder so deinstalliert.
Die meisten Funktions -Hooking -Frameworks haben eine minimale Länge, die ein bestimmtes Funktionshaken ermöglicht. Xnuspy hat diese Grenze nur , wenn Sie planen, die ursprüngliche Funktion aufzurufen , und die erste Anweisung der Hakenfunktion nicht B . 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, sodass Kernelfunktionen, die erwarten, dass diese über Funktionsaufrufe hinweg bestehen, können nicht süchtig werden (es gibt nicht viele, die dies erwarten). Wenn die Funktion, die Sie anhängen möchten, mit BL beginnt, und Sie beabsichtigen, das Original aufzurufen, können Sie dies nur tun, wenn die Ausführung der ursprünglichen Funktion X17 nicht ändert.
xnuspy_ctl führt die einmalige Initialisierung durch, wenn sie zum ersten Mal nach einem frischen Stiefel aufgerufen wird. Dies ist der einzige Teil von Xnuspy, der rennreich ist, da ich die von mir verwendete Lese-/Schreibschloss nicht statisch initialisieren kann. Nach der Rückgabe des ersten Anrufs sind zukünftige Anrufe garantiert, um threadssicher zu sein.
Dies ist vereinfacht, aber es erfasst die Hauptidee gut. Ein Funktionshaken in Xnuspy ist eine Struktur, die sich auf dem schriftlichen, ausführbaren Kernel -Speicher befindet. In den meisten Fällen ist dieser Speicher, der von alloc_static innerhalb von Pongoos zurückgegeben wird. Es kann darauf gekocht werden:
struct {
uint64_t replacement;
uint32_t tramp[2];
uint32_t orig[10];
};
Wenn replacement die virtuelle Kerneladresse (auf später ausgearbeitet) der Ersatzfunktion ist, ist tramp ein kleines Trampolin, das die Ausführung in replacement neu stellt, und orig ist ein größeres, komplizierteres Trampolin, das die ursprüngliche Funktion darstellt.
Eines der ersten Dinge, die Xnuspy tut, ist zu bestimmen, wo sich der EL0 -Ersatz im Adressraum der Anrufprozesse befindet. Dies geschieht, sodass Kernelfunktionen aus dynamischen Bibliotheken begeistert werden können. Der Mach-O-Header, der der Adresse dieses Ersatzes entspricht, wird gespeichert.
Danach wird eine gemeinsame Benutzer-Kernel-Zuordnung des __TEXT und __DATA Segmente dieses Headers (sowie jedes Segment zwischen diesen, falls vorhanden) erstellt. __TEXT wird freigegeben, damit Sie andere Funktionen von Ihren Haken anrufen können. __DATA wird geteilt, sodass Änderungen an globalen Variablen sowohl von EL1 als auch von EL0 gesehen werden.
Da es sich bei dieser Zuordnung um eine Eins-zu-Eins-Kopie von __TEXT und __DATA handelt, ist es einfach, die Adresse der Ersatzfunktion des Benutzers herauszufinden. Angesichts der Adresse der Mach -O -Header u , der Adresse des Beginns der gemeinsam genutzten Zuordnung k und der Adresse der Ersatzfunktion des Benutzers wenden wir die folgende Formel an: replacement = k + (r - u) r
Danach ist replacement die virtuelle Kern -Adresse der Ersatzfunktion des Benutzers in der gemeinsam genutzten Zuordnung und wird in die Funktionshakenstruktur geschrieben. Xnuspy leitet die Ausführung nicht nur in die EL0-Adresse der Ersatzfunktion ein, da dies äußerst unsicher ist. Dies ist nicht nur der Gnade des Schedulers ausgeliefert, es gibt uns auch keine Kontrolle über das Szenario, in dem ein Prozess mit einem Kernelhaken stirbt, während ein Kernelfaden immer noch auf dem Ersatz ausführt.
Schließlich wird die gemeinsame Zuordnung als ausführbar markiert und eine bedingungslose unmittelbare Zweigstelle ( B ) montiert. Es lenkt die Ausführung auf den Beginn des tramp und ersetzt die erste Anweisung der inzwischen hoffnungsvollen Kernelfunktion. Leider beschränkt uns dies von der Verzweigung auf Hakenstrukturen mehr als 128 MB von einer bestimmten Kernelfunktion. Xnuspy überprüft dieses Szenario, bevor er bootet ist, und fällt zurück in den nicht verwendeten Code, der bereits im Kernelcache ist, damit die Hook -Strukturen stattdessen aufwenden können, wenn er feststellt, dass dies passieren könnte.
Ich tue mein Bestes, um sicherzustellen, dass die Patchfinders funktionieren. Wenn also etwas nicht funktioniert, öffnen Sie bitte ein Problem.