4 pontos por GN⁺ 2025-04-07 | 1 comentários | Compartilhar no WhatsApp

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

 
GN⁺ 2025-04-07
Comentários do Hacker News