O início da jornada de emulação do iOS 14 no QEMU
- O projeto open source existente
alephsecurity/xnu-qemu-arm64 foi usado inicialmente, mas como era somente leitura (read-only), havia limitações de extensibilidade
- Depois, ao usar o projeto
TrungNguyen1909/qemu-t8030, foi possível aproveitar os seguintes recursos:
- função de restauração do iOS (com um QEMU acompanhante para conexão USB)
- execução do iOS 14
- baseado em uma versão mais recente do QEMU
- documentação detalhada na wiki
- Ao modificar o
launchd.plist, foi possível obter acesso via shell e SSH, servindo como um bom ponto de partida
- O objetivo era construir um ambiente completo de emulação de iOS capaz de executar a UI e apps
Patches de kernel e adoção do PongoOS
- O projeto
t8030 aplicava patches no kernel dentro do próprio QEMU → isso causava problemas de manutenção e extensibilidade
- Com base em experiência com jailbreak, a estrutura foi alterada para aplicar os patches do
checkra1n via PongoOS
- No QEMU, o tamanho da SRAM foi aumentado para executar o PongoOS e injetar o módulo checkra1n-KPF
- Na inicialização, a ausência de funções do bootrom/iBoot causou um problema de FPU não configurada → resolvido com referência à documentação ARM
- Após o A13, a PAC (Pointer Authentication) foi introduzida, invalidando parte dos patches
- O
task_for_pid0 (tfp0) foi usado como exemplo para comparar binários antes e depois da introdução da PAC
Desenvolvimento de ferramenta de automação de patches de kernel
- O método existente de patch dinâmico do checkra1n era difícil de ler e inconveniente de modificar → foi introduzido um método declarativo de patches baseado em texto
- Ao comparar dois binários
Mach-O, foram extraídas diferenças de assembly para gerar patches em texto
- Após inicializar com Pongo, foi feito um dump de memória e o kernel foi remontado → todos os patches foram organizados e comentados em arquivos de texto
Renderização gráfica: Metal vs renderização por software
- No iOS, toda a renderização da UI é feita pela API
Metal → GPU necessária
- Como a emulação de GPU é complexa, foram consideradas alternativas:
- renderização por software
- encaminhar chamadas Metal por proxy para um dispositivo físico
- No iOS 14, o bootarg
gpu=0 foi removido → a análise do QuartzCore confirmou o comportamento de fallback
- Ao aplicar patch no
QuartzCore em um iPhone com jailbreak, foi confirmado que a renderização por software funcionava (lenta, mas possível)
- A abordagem de proxy para Metal também foi testada, mas abandonada devido à complexidade do Objective-C e da API
Depuração de framebuffer e IOSurface
- O QEMU
t8030 não tinha implementação de framebuffer → foi usado o fork ChefKissInc/QEMUAppleSilicon
- Na inicialização, o logotipo da Apple e a barra de progresso apareciam, mas depois a tela ficava preta → começou a depuração
- A análise da kext IOMFB mostrou a existência de dois modos:
- framebuffer em endereço fixo (para exibição inicial)
- configuração de múltiplos planos baseada em DMA
- Durante a inicialização do sistema, o modo baseado em DMA era usado → os registros configurados pelo kernel foram verificados com traces do QEMU
- Mesmo assim, ainda não havia saída na tela
Desativação da randomização de endereços
- A randomização de endereços do kernel podia ser desativada no código de inicialização da placa
- A randomização no espaço do usuário foi desativada com patch em
_load_machfile
- O cache do dyld é um grande binário que inclui todas as bibliotecas dinâmicas → carregado em endereço fixo durante o boot
- Foi criada uma ferramenta em C para usar
dlopen e verificar endereços com funções _dyld_*
- Isso permitiu depurar a biblioteca
dyld com GDB → com foco especial em IOMFB, SpringBoard e QuartzCore
Acesso a logs por USB e bypass do lockdownd
- Em dispositivos físicos, é possível coletar logs do sistema com
idevicesyslog → autenticação USB necessária
- O lockdownd usa um keybag que depende do SEP para armazenar chaves → isso não existe no emulador
- Shellcode foi inserido no lugar de uma função existente para carregar diretamente a partir de um arquivo de chaves
- O bypass da autenticação de chaves entre QEMUs conectados por USB foi bem-sucedido → foi possível coletar logs
- Foi confirmado que o QuartzCore inicializava corretamente e usava renderização por software
Bypass da PAC (Pointer Authentication)
- Ao modificar o
backboardd, ocorreu um erro de PAC → recurso de segurança introduzido no ARMv8.3
- Substituir instruções PAC por NOP era uma abordagem excessivamente intrusiva
- Instruções PAC podem ser compiladas de forma compatível → se o QEMU ignorasse a PAC, a execução seria possível
- O QEMU 7 não permitia bypass da PAC → migração para o QEMU 8.2.1
- Foi necessário portar muito código customizado do QEMU, incluindo instruções específicas da Apple e níveis de exceção GL
- Como resultado, foi possível inicializar o iOS no QEMU 8 e neutralizar a PAC
backboardd e verificação da saída gráfica
- O
backboardd funcionava, mas nada era exibido na tela → havia várias causas possíveis
- Mesmo com dumps de memória DMA, não havia saída significativa
- Endereços foram verificados em
iosurface_lock e foram feitos dumps de quadros, mas parecia que eles eram enviados comprimidos para a GPU
- No iPhone X (
t8015), foi confirmada saída não comprimida → o DTB do QEMU foi modificado para alterar o chip-id de t8030 para t8015
- Como resultado, o logotipo da Apple passou a ser exibido após o boot
Barra de progresso e rastreamento de erros do sistema
- Após o logotipo, uma barra de progresso branca era exibida → travava em 90%
- Pela análise de logs, foram encontrados problemas em
mobileactivationd e SpringBoardFoundation → após aplicar patches, a UI mudou
- Para resolver o bloqueio no progresso, foi necessária a análise de muitos logs do sistema
Cache do dyld e automação de patches no espaço do usuário
- Assim como no kernel, o espaço do usuário também passou a usar um método de patches baseado em texto
- O cache do dyld tem 2 GB e é ineficiente de modificar → a ferramenta interna foi aprimorada para:
- rastrear offsets dentro do dyld
- aplicar patches diretamente em posições específicas com o comando
dd
- Também foi necessário aplicar em paralelo patches para bypass da verificação de assinatura do kernel
Execução do PreBoard e confirmação da UI
- O app
PreBoard é um app do sistema exibido em caso de erro → podia ser executado diretamente
- Foi adicionado um servidor VNC para tentar desbloquear a tela com o teclado
- Após o desbloqueio, o framework
vImage usava instruções AMX (Apple Matrix Coprocessor) → não suportadas pelo QEMU
- O problema foi resolvido com patch para o caminho de fallback por software do
vImage
- Depois do patch, foi possível exibir uma tela onde era possível inserir texto
Conclusão
- O processo chegou até pouco antes da execução do SpringBoard → a execução completa da UI agora é uma questão de tempo
- Foram feitas análises e patches em várias frentes, incluindo kernel, espaço do usuário, gráficos e recursos de segurança (PAC etc.)
- Foi confirmada a possibilidade de um ambiente prático baseado em QEMU para depuração e testes de apps iOS
1 comentários
Comentários do Hacker News