2 pontos por GN⁺ 2024-10-08 | 1 comentários | Compartilhar no WhatsApp
  • Este é um experimento que usou o Virtualization.framework do Apple silicon Mac e um ambiente vma2 para inicializar uma build do iPhone XR com iOS 15.0.2 até o PreBoard.app
  • O ponto central da abordagem é reutilizar a cadeia de boot do macOS 12.0.1 e trocar apenas a imagem do sistema iOS, mtree, root_hash e trustcache, reduzindo a necessidade de modificar a cadeia de boot inicial
  • Ao chamar a função privada _setProductionModeEnabled(false) para reduzir a VM ao estado CPFM 01, o TSS assina firmware arbitrário para dispositivos vma2 públicos, diminuindo a necessidade do método vma2pwn anterior
  • O boot real exige patches de kernel e sistema que mexem em verificação de plataforma do XNU, system keybag, verificação de tamanho do IOMFB, ramdisk, launchd, mount, DYLD shared cache, lockdownd e mobileactivationd
  • O maior desafio ainda não resolvido é a compatibilidade do system keybag, e ainda não foi confirmado se entrada por toque seria possível via APIs privadas do Virtualization.framework

Objetivo do experimento e ponto de partida

  • Com a transição para Apple silicon e o Mac Catalyst, iOS e macOS ficaram mais próximos, e a possibilidade de virtualizar o macOS levantou a questão central: seria possível virtualizar o iOS modificando uma cadeia de boot da mesma família?
  • O trabalho anterior vma2pwn era um projeto para criar uma cadeia de boot macOS vma2 modificável para VMs guest de macOS, e serviu de base para este experimento
  • Como caso público de virtualização/emulação de iOS concluída, existe o produto virtual iPhone cloud da Corellium
  • Também foram consultados qemu-t8030, trabalhos baseados em QEMU e textos de Zhuowei Zhang; em especial, a relação entre IOSurfaceRoot no macOS e IOCoreSurfaceRoot no iOS ajudou depois na busca por patches de kernel
  • Zhuowei Zhang considerou que apps gráficos de macOS não podem rodar no iOS, mas apps gráficos de iOS podem rodar no macOS, e essa propriedade também se aplica a boa parte do sistema gráfico do iOS

Recursos privados do Virtualization.framework

  • O Virtualization.framework da Apple tem uma função privada não documentada _setProductionModeEnabled(false)
  • Essa chamada e o recurso correspondente nas configurações da VM reduzem a VM para o Chip Fuse Mode CPFM 01, configurando o dispositivo virtual como “secure” e ao mesmo tempo “non-production”
  • Em dispositivos físicos, o TSS se recusa a assinar blobs SHSH exigidos por dispositivos non-production/non-secure como CPFM 00 ou 01
  • Para dispositivos vma2 públicos, o TSS assina o firmware arbitrário fornecido, o que reduz bastante a necessidade do método vma2pwn de construir uma cadeia de firmware modificada

Como a VM de iOS foi montada

  • A abordagem com mais sucesso foi usar a cadeia de boot do macOS 12.0.1 como está e substituir a imagem do sistema OS por imagens e arquivos relacionados da build do iPhone XR com iOS 15.0.2
    • Os itens substituídos são a imagem do sistema, mtree, root_hash e trustcache
    • Isso evita em grande parte a necessidade de modificar a cadeia de boot anterior à inicialização do iOS e o ramdisk de recuperação
  • A build do iPhone XR foi escolhida por causa do suporte a arm64e e da possibilidade de resolução mais baixa
  • Outras configurações de dispositivos arm64e também podem funcionar, mas o kernel vma2 está hardcoded para retornar "iPad8,6" em algumas chaves sysctl
  • Builds arm64 sofreram com problemas adicionais e incompatibilidade binária, então foram consideradas pouco interessantes para teste
  • Para executar a VM, foi usado o super-tart, um fork do app de terceiros tart para gerenciamento de VMs Apple silicon
    • O super-tart permite usar os recursos privados necessários do Virtualization.framework
    • Algumas mudanças, incluindo a alteração de _setProductionModeEnabled(false), ainda não foram totalmente publicadas
    • Ferramentas do Virtualization.framework que usam API privada exigem desativar o SIP, e talvez também o AMFI
  • Como ferramenta de restauração, foi usado um fork do idevicerestore

Patches de kernel

  • O patch de verificação de assinatura usado no vma2pwn pode ser necessário, mas no método com CPFM 01 não está claro se ele é obrigatório
  • Os patches ligados à assinatura no kernel vma2 fazem as funções abaixo retornarem 0
    • _apfs_extract_root_hash_arm
    • _authenticate_root_hash
    • __img4_firmware_property_callback
    • _is_root_hash_authentication_required
    • _img4_firmware_evaluate
  • lookup_in_static_trust_cache deve ser patchado para retornar 1
  • Como binários de iOS não são binários de plataforma simulador, no início eles encerravam com EXEC, [0xe] Binary with wrong platform
    • Isso é resolvido com um patch para pular a linha de verificação PLATFORM_IOS no XNU
    • No kernel vma2, isso é feito trocando B.NE por B
  • Por causa da incompatibilidade do system keybag, o PreBoard.app mostra “Swipe up to upgrade.” no lugar do Setup.app
    • Aplicando dois patches em ipc_make_system_keybag para forçar a função a não retornar erro, dá para chegar até o PreBoard.app
    • Essa limitação ainda não foi totalmente resolvida
  • A diferença no tamanho da struct IOMFB entre frameworks de sistema do iOS e o kernel do macOS provoca kernel panic com a string CLCDTransaction size mismatch. Returning error 0x%X.
    • Remover a verificação de tamanho em IOMobileFramebufferUserClient::swap_submit faz o panic parar

Preparação para patch de arquivos do sistema

  • O ramdisk de recuperação e os arquivos do sistema iOS são assinados, então eles encerram na execução se forem patchados sem reassinatura
  • No ambiente modificado, é sugerido usar o ldid do Procursus
    • Exemplo de instalação: brew install ldid-procursus
    • Exemplo de reassinatura: ldid_macosx_arm64 -S -M <binary>
    • -S aplica pseudo-sign ao binário, e -M preserva os entitlements existentes
  • Muitos binários fazem verificação de identity, então antes de reassiná-los é preciso renomeá-los para a identity e depois restaurar o nome original
    • Em keybagd, a verificação de Identifier=com.apple.keybagd é feita com codesign -d -v keybagd
    • mv keybagd com.apple.keybagd
    • ldid_macosx_arm64 -S -M com.apple.keybagd
    • mv com.apple.keybagd keybagd
  • Funções sem símbolos anexados automaticamente podem ser encontradas buscando XRefs de strings e rastreando a função chamadora por referências de logging
  • Para obter um shell interativo no terminal serial, é possível portar Bash e um LaunchDaemon
    • Foi usado o método de copiar os arquivos relevantes de payloads de jailbreak iOS compatíveis com a versão, como o Procursus

Modificação de DMG e ramdisk

  • Para patchar o sistema ou o ramdisk de recuperação, é preciso modificar diretamente o volume DMG
  • No exemplo do iOS 15.0.2, o volume iOS System incluído no IPSW é convertido para um formato com leitura e escrita
    • hdiutil convert -format UDRW -o 018-66258-074-rw.dmg 018-66258-074.dmg
    • Após montar: sudo mount -uw /Volumes/Sky19A404.N104N841OS
    • Depois das alterações: hdiutil convert -format ULFO -o 018-66258-074.dmg 018-66258-074-rw.dmg
    • Em seguida: asr imagescan --source 018-66258-074.dmg
  • Antes do boot do iOS, é necessário modificar /usr/local/bin/restored_external no ramdisk de recuperação
    • A versão de iOS de restored_external tenta criar um system keybag com MKBKeyBagCreateSystem, mas isso não é compatível com o kernel do macOS
    • O desvio é feito removendo a checagem de erro
    • A condição da chamada ramrod_set_NVRAM_variable é patchada para definir allow-root-hash-mismatch como true, isto é, 1, pulando a autenticação do root hash
  • Também é preciso modificar /usr/sbin/asr no ramdisk de recuperação
    • É possível usar o asr64_patcher do iSuns9
    • Encontra-se a função que imprime a string "Image failed signature verification." e, na função que a referencia, troca-se a chamada ARMv8-A BL por um salto B que segue para o caminho "Image passed signature verification"
    • No binário asr do iOS 15.0.2, isso aplica b #0x7c no offset de arquivo 0x27A18

Modificação do volume de sistema do iOS

  • Para alinhar o próprio sistema iOS ao kernel do macOS, a maior parte dos arquivos que seriam instalados na raiz do sistema de arquivos deve ser movida para /System/Library/Templates/Data no volume do sistema
  • Quando o sistema inicializa, esses arquivos passam a existir em /
  • Pastas vazias da raiz normal podem precisar permanecer no volume do sistema mesmo estando de fato vazias
    • Um exemplo é /Applications
    • Essa parte ainda não foi suficientemente testada

Patches em launchd e keybagd

  • No boot inicial do iOS, /sbin/launchd é executado, e ele precisa de patches para iniciar o processo de boot inicial sem falhas
  • O primeiro patch é aplicado ao plist de configuração embutido no binário
    • Procure a string <key>SIGTERMTimeout</key>; cerca de 172 bytes antes dela está a configuração correspondente
    • Adicione <key>PerformAfterUserspaceReboot</key><true/> às seções mount-phase-2, fips, tzinit, finish-demo-restore, fud, xpcroleaccountd, prng_seedctl e MSUEarlyBootTask
    • Troque RequireSuccess da seção data-protection para <false/>
  • A mudança em data-protection é necessária porque /usr/libexec/init_data_protection falha ao rodar na VM
    • Esse arquivo é um link simbólico para /usr/libexec/seputil
  • Como a alteração do plist embutido pode ultrapassar o espaço original de string, dá para colar um XML comprimido com um XML minimizer e sobrescrever a área restante com caracteres de espaço compatíveis com XML
  • O launchd também inicializa /usr/libexec/keybagd, mas esse binário falha por causa das diferenças de kernel citadas antes
    • Uma forma de contornar isso é compilar um executável que simplesmente encerre com código 0 para substituir o keybagd
    • O projeto fixkeybag também foi analisado, mas o código de criação de system keybag desse app também chama MKBKeyBagCreateSystem, então ele falha mesmo em um iOS já inicializado
  • O launchd também precisa de um patch adicional que transforma em NOP a ramificação condicional TBZ que provoca a string de panic Userspace reboot changed system version: previous %s != current %s

Modificações em mount, DYLD shared cache e camada gráfica

  • Como o processo de restauração usa a cadeia de boot e o ramdisk do macOS, o /sbin/mount do iOS não consegue lidar corretamente com o volume APFS criado
  • A solução é pegar o binário mount do volume de sistema do macOS, ajustar os metadados Mach-O para que ele possa rodar no iOS e então substituí-lo
    • Isso pode ser feito manualmente, e também com o vtool incluído no macOS
  • A modificação mais difícil é o patch no DYLD shared cache
    • IOSurfaceRoot do macOS e IOCoreSurfaceRoot do iOS são basicamente o mesmo driver, mas a diferença de nome impede a compatibilidade
    • Como o DYLD shared cache do iOS contém a string mais longa "IOCoreSurfaceRoot", ela pode ser trocada por "IOSurfaceRoot" e os bytes restantes preenchidos com 0x00
  • Para analisar e extrair o DYLD shared cache, foi usada a ferramenta ipsw, de blacktop
    • ipsw dyld split <dsc file> separa as dylibs embutidas
    • No binário /System/Library/Frameworks/IOSurface.framework/IOSurface, encontra-se a referência a "IOCoreSurfaceRoot" na função __iosConnectInitalize
    • ipsw dyld a2o converte endereço virtual em offset de arquivo
    • No exemplo, foi patchado o offset 0x28fde373 de dyld_shared_cache_arm64e
  • Depois de modificar o DYLD shared cache, há exceções porque o cdhash em outra posição não bate mais
    • Com o GDB stub fornecido pelo frontend do Virtualization.framework, coloca-se um breakpoint em cs_validate_hash no kernel para verificar o cdhash completo
    • No exemplo do iOS 15.0.2, os bytes antigos no offset de arquivo 0x5a9cffc0 foram substituídos pelo novo cdhash

Daemons do sistema e patches de ativação

  • O watchdogd entra em crash-loop porque verifica se está rodando em uma VM e não tem um caminho de encerramento limpo no macOS, mas esse crash é tratado como inofensivo
  • No backboardd, foram testados patches em chamadas ligadas à migração de dados que poderiam levar ao PreBoard.app, mas além de travar no logo da Apple não houve diferença observável
  • Na função get_device_type_internal_block_invoke do lockdownd, a chamada getMGInt referente a "ShouldHactivate" é patchada para mov x0, #1, forçando hactivation para contornar as restrições normais de ativação do iOS em ambiente de desenvolvimento
  • O mobileactivationd também pode ser patchado para permitir hactivation
    • A função shouldHactivate é substituída pelas instruções ARMv8-A mov x0, #0 e ret
  • Ajustes adicionais necessários na device tree vma2 ficam como tarefa para o leitor
  • Algumas ações podem exigir medidas anormais como chmod -R 777 /

Limitações restantes e entrada por toque

  • Para superar o problema do system keybag, será preciso entender melhor a estrutura correspondente existente no sistema iOS e no kernel, e criar patches adicionais
  • O projeto já consumiu pelo menos várias centenas de horas, e o estado atual publicado chega ao boot até o PreBoard.app
  • Ainda não foi confirmado se a função de toque funciona com o kernel e firmware públicos do vma2 Mac
  • O Virtualization.framework tem APIs privadas relacionadas a toque
    • _VZAppleTouchScreenConfiguration
    • _VZUSBTouchScreenConfiguration
    • _VZTouch
    • _VZMultiTouchEvent
  • Essas APIs podem enviar eventos de toque, mas o uso exato dos parâmetros ainda não foi totalmente entendido
    • O enum TouchPhase é um enum simples que implementa valores com os mesmos nomes de NSTouch.Phase
    • O código de exemplo às vezes pode gerar exceções
    • Ainda não foi verificado se os valores de coordenadas são mapeados da forma esperada pela VM, nem se a VM consegue processá-los
  • A demo mostra a sequência de boot, e o trecho de espera de cerca de 30 segundos no meio foi cortado

1 comentários

 
GN⁺ 2024-10-08
Comentários do Hacker News
  • A Corellium venceu a disputa judicial e agora pode alugar VMs de iOS na nuvem para pesquisa de segurança https://hn.algolia.com/?query=corellium
    Se for possível virtualizar iOS em um MacBook Apple Silicon, a demanda por serviços comerciais de virtualização de iOS pode cair
    Para pessoas físicas custa cerca de US$ 400 por mês, e para empresas algo em torno de US$ 60.000 por ano https://support.corellium.com/subscriptions/pricing

    • Meu Deus, custa US$ 4~US$ 8 por hora, fico me perguntando quem paga por uma VM dessas
  • Ótimo. Na próxima, seria bom descobrirem também como instalar macOS em um iPad, para que enfim possamos usar aquele computador que queríamos que a Apple tivesse criado

    • Dá para começar com Windows XP
      https://www.theverge.com/2024/7/22/24200536/windows-xp-ipad-...
    • Pela parte de trabalhos anteriores, [Zhuowei Zhang] concluiu que apps macOS com GUI não podem rodar no iOS, mas apps gráficos de iOS podem rodar no macOS
      O Mac Catalyst também parece funcionar só em uma direção, como era de se esperar
    • Concordo 1000%. Nas últimas semanas eu estava procurando exatamente isso, ou seja, um tablet com sistema operacional para desenvolvimento
      Pelo tamanho, o iPad Mini seria ótimo, mas o iPadOS não serve para nada, e agora estou olhando o Surface Go. Só que ele é um pouco grande
      Sei que não existe nada com macOS, mas se alguém tiver recomendação de tablet pequeno que rode Win11, seria ótimo saber. Se precisar, penso até em encomendar da China
    • O iPad Pro é o dispositivo mais rápido com M4
    • Se você chamar isso de um MacBook Air com uma saliência invertida e um teclado destacável, já basta?
  • Pelo perfil do autor no GitHub, parece que ele acabou de se formar em ciência da computação; é um trabalho realmente impressionante

    • Aposto que uma proposta de emprego da Apple não deve demorar
  • Tenho a sensação de que a Apple nunca transformou o Simulator em Emulator porque não quer que as pessoas fuçem nas camadas internas do iOS

    • Outro motivo para ser Simulator e não Emulator desde o começo talvez seja que, na época, muitos componentes do iOS ou iPhone OS eram forks de bibliotecas do Mac OS X
    • Os desenvolvedores ainda usam Macs Intel, e neles não dá para virtualizar iOS ARM
    • Agora que iPhone, Mac e iPad são todos arm64, e ainda por cima baseados em Apple Silicon, fico realmente curioso sobre o quanto os bootloaders do iOS e do macOS diferem
      Depois de passar do bootloader, especialmente com a Apple controlando até o hardware, fica a dúvida de por que ainda seria necessário manter tantas diferenças entre os dois sistemas operacionais
  • A pessoa que criou o qemu-t8030 conseguiu executar o SpringBoard https://mastodon.social/@ntrung03/109712247237110967, mas não publicou o código
    Seria excelente se esse avanço pudesse ser combinado com este trabalho

  • Comentários anteriores: https://news.ycombinator.com/item?id=40219423

  • Artigo relacionado: https://worthdoingbadly.com/hv/
    Trata de máquinas virtuais com aceleração por hardware em um iPhone 12 com jailbreak / iOS 14.1

  • Fugindo um pouco do assunto, fico curioso se alguém já virtualizou macOS ARM em x86-64

    • Não é possível. Em geral, “virtualização” significa executar um sistema operacional com virtualização de hardware, em que a CPU do host executa o código nativamente e passa toda a entrada e saída para o hipervisor
      Por isso, só é possível virtualizar sistemas operacionais compilados para a mesma arquitetura de CPU do sistema host
      Em todos os demais casos, como rodar software ARM em x86 ou o contrário, é preciso usar emulação, que interpreta o código ou o recompila dinamicamente
      Pela definição, dá para emular qualquer coisa em cima de qualquer outra. Recentemente, houve até o caso de inicializar Linux para MIPS em um Intel 4004, o primeiro microprocessador, mas desempenho pode ser um problema
    • Se você tentar virtualizar ARM genérico no QEMU, vai ver que o desempenho fica abaixo até de um Raspberry Pi. Deve vir incluído por padrão nas versões recentes
      Virtualizar a CPU Mn parece ainda menos útil
    • Vale a pena dar uma olhada no projeto Hackintosh
  • A Apple já fornece o iOS Simulator no Xcode; então fico me perguntando em que este projeto é melhor do que a ferramenta da Apple

    • O Simulator não executa iOS real nem o build iOS real do app. Quando você roda um app no Simulator, ele é compilado para o conjunto de instruções nativo do Mac atual e executado com link para frameworks e bibliotecas do Mac que imitam, ou às vezes apenas simulam superficialmente, o comportamento esperado do iOS
      Por exemplo, você não pode pegar um binário iOS da App Store e simplesmente executá-lo no iOS Simulator, especialmente em Macs Intel
      Como o Simulator não executa um iOS completo, também não dá para investigar e aprender como as partes internas do iOS realmente funcionam. Se você cavar fundo o suficiente nos frameworks do Simulator, no fim acaba voltando ao macOS
      Já um emulador executa o build completo do iOS, igual ao de um dispositivo real. Em teoria, você pode executar qualquer binário iOS sem modificações e investigar como o sistema operacional de fato funciona
      É parecido com a diferença entre rodar um app no Wine e rodá-lo em uma VM do Windows. Só que, no caso do Simulator, é ainda mais próximo de uma situação em que você precisaria recompilar e relincar o app especificamente para o ambiente do Wine antes de executá-lo
      Se você quer estudar as partes internas do Windows, não aprende tanto apenas rodando coisas no Wine, mas investigando uma VM do Windows aprende muito mais
  • Vai ser um presente de Natal antecipado para as click farms