Der Beginn der iOS-14-QEMU-Emulationsreise
- Zunächst wurde das bestehende Open-Source-Projekt
alephsecurity/xnu-qemu-arm64 verwendet, doch da es schreibgeschützt (read-only) war, gab es Einschränkungen bei der Erweiterbarkeit.
- Später wurde das Projekt
TrungNguyen1909/qemu-t8030 genutzt, womit sich folgende Funktionen verwenden ließen:
- iOS-Wiederherstellungsfunktion (mit begleitendem QEMU für die USB-Verbindung)
- Ausführung von iOS 14
- Basierend auf einer aktuellen QEMU-Version
- Bereitstellung einer ausführlichen Wiki-Dokumentation
- Durch die Anpassung von
launchd.plist gelang der Zugriff auf Shell und SSH, was als guter Ausgangspunkt diente.
- Ziel war der Aufbau einer vollständigen iOS-Emulationsumgebung, in der UI und Apps lauffähig sind.
Kernel-Patches und Einführung von PongoOS
- Das Projekt
t8030 hatte eine Struktur, in der Kernel-Patches innerhalb von QEMU angewendet wurden → das führte zu Problemen bei Wartbarkeit und Erweiterbarkeit.
- Auf Basis von Jailbreak-Erfahrung wurde die Struktur auf die Anwendung von
checkra1n-Patches über PongoOS umgestellt.
- In QEMU wurde die SRAM-Größe erhöht, um PongoOS auszuführen und das Modul checkra1n-KPF einzuschleusen.
- Beim Booten trat wegen fehlender Boot-ROM-/iboot-Funktionen ein Problem mit nicht konfigurierter FPU auf → mithilfe der ARM-Dokumentation gelöst.
- Seit A13 wurde PAC (Pointer Authentication) eingeführt, wodurch einige Patches wirkungslos wurden.
- Am Beispiel von
task_for_pid0 (tfp0) wurde ein Binärvergleich vor und nach der Einführung von PAC durchgeführt.
Entwicklung eines Automatisierungstools für Kernel-Patches
- Die bestehende dynamische Patch-Methode von checkra1n war schwer lesbar und unpraktisch zu ändern → daher wurde ein deklarativer, textbasierter Patch-Ansatz eingeführt.
- Durch den Vergleich zweier
Mach-O-Binärdateien wurden Unterschiede im Assembler extrahiert und daraus Text-Patches erzeugt.
- Nach dem Boot über Pongo wurde ein Speicherdump erstellt und der Kernel neu zusammengesetzt → sämtliche Patches wurden in Textdateien organisiert und kommentiert.
Grafik-Rendering: Metal vs. Software-Rendering
- iOS führt das gesamte UI-Rendering über die
Metal-API aus → daher ist eine GPU erforderlich.
- Wegen der Komplexität der GPU-Emulation wurden Alternativen betrachtet:
- Software-Rendering
- Weiterleitung der Metal-Aufrufe per Proxy an ein reales Gerät
- In iOS 14 wurde das Boot-Argument
gpu=0 entfernt → durch Analyse von QuartzCore wurde das Fallback-Verhalten bestätigt.
- Auf einem jailbroken iPhone wurde
QuartzCore gepatcht, um funktionierendes Software-Rendering zu verifizieren (langsam, aber möglich).
- Auch ein Metal-Proxy-Ansatz wurde erprobt, wegen der Komplexität von Objective-C und der API jedoch eingestellt.
Debugging von Framebuffer und IOSurface
- In
t8030 QEMU gab es keine Framebuffer-Implementierung → deshalb wurde der Fork ChefKissInc/QEMUAppleSilicon verwendet.
- Beim frühen Booten waren Apple-Logo und Fortschrittsanzeige sichtbar, danach jedoch nur ein schwarzer Bildschirm → Beginn des Debuggings.
- Die Analyse des IOMFB-kext ergab zwei Modi:
- Framebuffer mit fester Adresse (für die anfängliche Anzeige)
- DMA-basierte Mehrflächen-Konfiguration
- Während des Systemstarts wurde der DMA-basierte Modus verwendet → mithilfe von Tracing in QEMU wurden die Registereinstellungen des Kernels überprüft.
- Trotzdem erschien weiterhin keine Ausgabe auf dem Bildschirm.
Deaktivierung der Adressrandomisierung
- Die Kernel-Adressrandomisierung konnte im Board-Initialisierungscode deaktiviert werden.
- Die Randomisierung im User Space wurde durch Patchen von
_load_machfile deaktiviert.
- Der dyld-Cache ist eine große Binärdatei, die alle dynamischen Bibliotheken enthält → beim Booten wird sie an festen Adressen geladen.
- Es wurde ein C-Tool erstellt, das nach
dlopen mit _dyld_*-Funktionen Adressen ermittelt.
- Dadurch wurde Debugging der
dyld-Bibliotheken mit GDB möglich → besonderes Interesse galt IOMFB, SpringBoard und QuartzCore.
Zugriff auf USB-Logs und Umgehung von lockdownd
- Auf realen Geräten lassen sich mit
idevicesyslog Systemlogs sammeln → dafür ist USB-Authentifizierung erforderlich.
- lockdownd verwendet zur Schlüsselspeicherung einen Keybag, der SEP benötigt → im Emulator nicht vorhanden.
- Durch das Einfügen von Shellcode an Stelle einer bestehenden Funktion wurden die Schlüssel direkt aus einer Schlüsseldatei geladen.
- So gelang die Umgehung der Schlüsselauthentifizierung zwischen per USB verbundenen QEMU-Instanzen → Logs konnten gesammelt werden.
- Es wurde bestätigt, dass QuartzCore korrekt initialisiert wurde und Software-Rendering verwendet.
Umgehung von PAC (Pointer Authentication)
- Bei der Änderung von
backboardd trat ein PAC-Fehler auf → eine Sicherheitsfunktion, die mit ARMv8.3 eingeführt wurde.
- Das Ersetzen von PAC-Instruktionen durch NOPs war zu invasiv.
- PAC-Instruktionen können in einem kompatiblen Modus kompiliert werden → wenn QEMU PAC ignoriert, ist die Ausführung möglich.
- Mit QEMU 7 ließ sich PAC nicht umgehen → daher Migration auf QEMU 8.2.1.
- Dazu mussten zahlreiche benutzerdefinierte QEMU-Anpassungen portiert werden, darunter Apple-spezifische Instruktionen und GL-Exception-Levels.
- Im Ergebnis gelang unter QEMU 8 das Booten von iOS und die Deaktivierung von PAC.
backboardd und Bestätigung der Grafikausgabe
backboardd lief, aber es wurde nichts angezeigt → mehrere Ursachen waren möglich.
- Auch ein DMA-Speicherdump zeigte keine aussagekräftige Ausgabe.
- In
iosurface_lock wurde die Adresse geprüft und ein Frame-Dump erstellt, doch offenbar wurde die Ausgabe komprimiert an die GPU übergeben.
- Auf dem iPhone X (t8015) wurde unkomprimierte Ausgabe bestätigt → deshalb wurde der DTB in QEMU angepasst und
chip-id von t8030 auf t8015 geändert.
- Infolgedessen wurde nach dem Booten das Apple-Logo angezeigt.
Fortschrittsbalken und Nachverfolgung von Systemfehlern
- Nach dem Logo erschien ein weißer Fortschrittsbalken → bei 90 % blieb er stehen.
- Durch Log-Analyse wurden Probleme mit
mobileactivationd und SpringBoardFoundation gefunden → nach Patches änderte sich die UI.
- Um das Hängenbleiben zu beheben, mussten zahlreiche Systemlogs analysiert werden.
Automatisierung von dyld-Cache- und User-Space-Patches
- Wie beim Kernel wurde auch im User Space ein textbasierter Patch-Ansatz verwendet.
- Der dyld-Cache ist 2 GB groß, was Änderungen ineffizient machte → deshalb wurden interne Tools verbessert, um:
- Offsets innerhalb von dyld nachzuverfolgen
- mit dem Befehl
dd gezielt bestimmte Positionen direkt zu patchen
- Parallel dazu waren auch Patches zur Umgehung der Kernel-Signaturprüfung erforderlich.
Ausführung von PreBoard und Sichtprüfung der UI
- Die
PreBoard-App ist eine System-App, die bei Fehlern angezeigt wird → sie ließ sich direkt ausführen.
- Ein VNC-Server wurde hinzugefügt, um mit der Tastatur zu versuchen, den Bildschirm zu entsperren.
- Nach dem Entsperren verwendete das
vImage-Framework AMX-(Apple Matrix Coprocessor)-Instruktionen → von QEMU nicht unterstützt.
- Das Problem wurde gelöst, indem auf den Software-Fallback-Pfad von
vImage gepatcht wurde.
- Nach dem Patch gelang die Anzeige eines Bildschirms, auf dem Texteingabe möglich war.
Fazit
- Der Stand reichte bis unmittelbar vor dem Start von SpringBoard → eine vollständig lauffähige UI ist nun nur noch eine Frage der Zeit.
- Es wurden Analysen und Patches in mehreren Bereichen durchgeführt: Kernel, User Space, Grafik und Sicherheitsfunktionen (wie PAC).
- Das Potenzial einer praktischen QEMU-basierten Umgebung für Debugging und Tests von iOS-Apps wurde bestätigt.
1 Kommentare
Hacker-News-Kommentare