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
Comentarios de Hacker News