4 puntos por GN⁺ 2025-04-07 | 1 comentarios | Compartir por WhatsApp

El inicio del recorrido de emulación de iOS 14 con QEMU

  • Al principio se usó el proyecto open source existente alephsecurity/xnu-qemu-arm64, pero al ser de solo lectura (read-only) tenía problemas de falta de extensibilidad
  • Después se pasó a usar el proyecto TrungNguyen1909/qemu-t8030, con el que fue posible aprovechar las siguientes funciones:
    • función de restauración de iOS (con un QEMU acompañante para la conexión USB)
    • ejecución de iOS 14
    • basado en una versión reciente de QEMU
    • documentación detallada en la wiki
  • Al modificar launchd.plist se logró acceso por shell y SSH, lo que sirvió como un buen punto de partida
  • El objetivo era construir un entorno de emulación de iOS completo, capaz de ejecutar la UI y las apps

Parches del kernel e introducción de PongoOS

  • El proyecto t8030 tenía una estructura que aplicaba parches al kernel dentro de QEMU → esto generaba problemas de mantenimiento y extensibilidad
  • Con base en experiencia previa de jailbreak, se cambió a una estructura que aplica los parches de checkra1n mediante PongoOS
  • Se aumentó el tamaño de SRAM en QEMU para ejecutar PongoOS e inyectar el módulo checkra1n-KPF
  • Durante el arranque surgió un problema de FPU sin configurar por funciones faltantes de bootrom/iboot → se resolvió consultando la documentación de ARM
  • A partir del A13 se introdujo PAC (Pointer Authentication), lo que invalidó algunos parches
  • Se comparan binarios antes y después de la introducción de PAC usando como ejemplo task_for_pid0 (tfp0)

Desarrollo de una herramienta de automatización de parches del kernel

  • El método dinámico de parches de checkra1n existente era difícil de leer y de modificar → se introdujo un método declarativo de parches basado en texto
  • Se compararon dos binarios Mach-O para extraer diferencias en ensamblador y luego generar parches en texto
  • Después de arrancar con Pongo, se hizo un volcado de memoria para reconstruir el kernel → todos los parches se organizaron y comentaron en archivos de texto

Renderizado gráfico: Metal vs renderizado por software

  • iOS realiza todo el renderizado de la UI mediante la API Metal → requiere GPU
  • Como la emulación de GPU es compleja, se consideraron alternativas:
    • renderizado por software
    • enviar las llamadas a Metal por proxy hacia un dispositivo real
  • En iOS 14 se eliminó el bootarg gpu=0 → se analizó QuartzCore y se confirmó el comportamiento de fallback
  • Se parchó QuartzCore en un teléfono con jailbreak y se confirmó que el renderizado por software funcionaba, aunque lento
  • También se experimentó con la opción de proxy para Metal, pero se abandonó por la complejidad de Objective-C y de la API

Depuración del framebuffer e IOSurface

  • El QEMU de t8030 no tenía implementación de framebuffer → se usó el fork ChefKissInc/QEMUAppleSilicon
  • Durante el arranque inicial se mostraban el logo de Apple y el indicador de progreso, pero después aparecía una pantalla negra → comenzó la depuración
  • El análisis del kext IOMFB mostró la existencia de dos modos:
    • framebuffer de dirección fija (para la visualización inicial)
    • configuración multipla no, basada en DMA
  • Durante el arranque del sistema se usaba el modo basado en DMA → con el trazado de QEMU se verificó la configuración de registros del kernel
  • Aun así, seguía sin haber salida en pantalla

Desactivación de la aleatorización de direcciones

  • La aleatorización de direcciones del kernel podía desactivarse en el código de inicialización de la placa
  • La aleatorización en espacio de usuario se desactivó parchando _load_machfile
  • La caché de dyld es un binario grande que incluye todas las bibliotecas dinámicas → se carga en direcciones fijas durante el arranque
  • Se creó una herramienta en C para hacer dlopen y verificar direcciones con funciones _dyld_*
  • Esto permitió depurar la biblioteca dyld con GDB → con especial interés en IOMFB, SpringBoard y QuartzCore

Acceso a logs por USB y bypass de lockdownd

  • En un dispositivo real se pueden recopilar logs del sistema con idevicesyslog → requiere autenticación por USB
  • lockdownd usa un keybag que requiere el SEP para almacenar claves → el emulador no lo tiene
  • Se insertó shellcode en lugar de la función existente para cargar directamente desde un archivo de claves
  • Se logró saltar la autenticación de claves entre QEMU conectados por USB → ya fue posible recopilar logs
  • Se confirmó la inicialización correcta de QuartzCore y el uso de renderizado por software

Bypass de PAC (Pointer Authentication)

  • Al modificar backboardd apareció un error de PAC → es una función de seguridad introducida en ARMv8.3
  • Sustituir instrucciones PAC por NOP era demasiado intrusivo
  • Las instrucciones PAC pueden compilarse de forma compatible → si QEMU ignora PAC, la ejecución es posible
  • QEMU 7 no permitía el bypass de PAC → se migró a QEMU 8.2.1
  • Fue necesario portar gran cantidad de código personalizado de QEMU, como instrucciones exclusivas de Apple y niveles de excepción GL
  • Como resultado, se logró arrancar iOS en QEMU 8 y neutralizar PAC

Verificación de backboardd y de la salida gráfica

  • backboardd funcionaba, pero no había imagen en pantalla → había varias causas posibles
  • Incluso al volcar la memoria DMA no se observó una salida significativa
  • Se verificó la dirección en iosurface_lock y se hizo un volcado de frames, pero al parecer se entregaban comprimidos a la GPU
  • En un iPhone X (t8015) se confirmó salida sin compresión → se modificó el DTB de QEMU para cambiar chip-id de t8030 a t8015
  • Como resultado, tras el arranque apareció el logo de Apple

Barra de progreso y rastreo de errores del sistema

  • Después del logo apareció una barra de progreso blanca → se detenía en 90%
  • El análisis de logs reveló problemas en mobileactivationd y SpringBoardFoundation → tras aplicar parches la UI cambió
  • Para resolver el bloqueo del progreso fue necesario analizar numerosos logs del sistema

Caché de dyld y automatización de parches en espacio de usuario

  • Igual que con el kernel, en espacio de usuario también se usó un método de parches basado en texto
  • La caché de dyld tiene un tamaño de 2 GB, por lo que modificarla era ineficiente → se mejoraron herramientas internas para:
    • rastrear offsets dentro de dyld
    • parchear directamente posiciones específicas con el comando dd
  • También fue necesario aplicar en paralelo un parche para evadir la verificación de firma del kernel

Ejecución de PreBoard y verificación de la UI

  • La app PreBoard es una app del sistema que se muestra cuando ocurre un error → podía ejecutarse directamente
  • Se agregó un servidor VNC para intentar desbloquear la pantalla con teclado
  • Tras el desbloqueo, el framework vImage usaba instrucciones AMX (Apple Matrix Coprocessor) → QEMU no las soportaba
  • El problema se resolvió parchando vImage para usar una ruta de fallback por software
  • Después del parche se logró mostrar una pantalla donde ya era posible introducir texto

Conclusión

  • Se llegó hasta justo antes de ejecutar SpringBoard → lograr una UI completamente funcional ya es cuestión de tiempo
  • Se realizaron análisis y parches desde múltiples frentes: kernel, espacio de usuario, gráficos y funciones de seguridad como PAC
  • Se confirmó la posibilidad de un entorno práctico basado en QEMU para depuración y pruebas de apps de iOS

1 comentarios

 
GN⁺ 2025-04-07
Comentarios de Hacker News