4 Punkte von GN⁺ 2025-04-07 | 1 Kommentare | Auf WhatsApp teilen

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

 
GN⁺ 2025-04-07
Hacker-News-Kommentare