2 ポイント 投稿者 GN⁺ 2024-10-08 | 1件のコメント | WhatsAppで共有
  • Apple silicon MacのVirtualization.frameworkとvma2環境を使い、iOS 15.0.2 iPhone XRビルドをPreBoard.appまで起動した実験
  • アプローチの中核は、macOS 12.0.1のブートチェーンを再利用し、iOSシステムイメージ、mtreeroot_hashtrustcacheだけを置き換えて初期ブートチェーン改変の負担を減らすこと
  • 非公開の_setProductionModeEnabled(false)呼び出しでVMをCPFM 01状態に下げると、公開vma2デバイスに対してTSSが任意のファームウェアへ署名するため、従来のvma2pwn方式の必要性が薄れる
  • 実際の起動には、XNUプラットフォーム検査、system keybag、IOMFBサイズ検査、ramdisk、launchdmount、DYLD shared cache、lockdowndmobileactivationdに手を入れるカーネル・システムパッチが必要
  • 最大の未解決課題はsystem keybag互換性であり、タッチ入力もVirtualization.frameworkの非公開APIで可能かどうかはまだ確認されていない

実験の目標と出発点

  • Apple siliconへの移行とMac Catalyst以降、iOSとmacOSの距離は近づいており、macOSの仮想化が可能になったことで、iOSも同系統のブートチェーンを改変して仮想化できるのかが中核の問いとなった
  • 以前の作業であるvma2pwnは、macOSゲストVM向けに改変可能なvma2 macOSブートチェーンを作るプロジェクトで、今回の実験の土台になっている
  • 公開された完成度の高いiOS仮想化/エミュレーション事例としては、Corelliumのvirtual iPhone cloud製品がある
  • qemu-t8030、QEMUベースの作業、Zhuowei Zhangの記事も参考にしており、特にmacOSのIOSurfaceRootとiOSのIOCoreSurfaceRootの関係が、その後のカーネルパッチ探索に役立った
  • Zhuowei Zhangの記事では、GUIのmacOSアプリはiOS上で動かせないが、グラフィカルなiOSアプリはmacOS上で動かせるとされており、この性質はiOSのグラフィックシステムのかなりの部分にも当てはまる

Virtualization.frameworkの非公開機能

  • AppleのVirtualization.frameworkには、文書化されていないprivate関数_setProductionModeEnabled(false)がある
  • この呼び出しとVM設定の対応機能により、VMをChip Fuse ModeであるCPFM 01へ下げ、仮想デバイスを“secure”かつ“non-production”な状態に構成する
  • 物理デバイスでは、TSSはCPFM 0001のようなnon-production/non-secureデバイスに必要なSHSH blobへの署名を拒否する
  • 公開vma2デバイスに対しては、TSSが与えられた任意のファームウェアへ署名するため、改変ファームウェアチェーンを作っていたvma2pwn方式の必要性は大きく下がる

iOS VMの構成方法

  • 最も成功したアプローチは、macOS 12.0.1のブートチェーンをそのまま使い、システムOSイメージをiOS 15.0.2 iPhone XRビルドのイメージと関連ファイルに置き換える方法
    • 置き換え対象はシステムイメージ、mtreeroot_hashtrustcache
    • この方法により、iOS初期化以前の段階にあるブートチェーンや復旧ramdiskの改変必要性の大半を回避できる
  • iPhone XRビルドは、arm64e対応と低解像度の可能性を理由に選ばれた
  • 他のarm64eデバイス構成でも成功する可能性はあるが、vma2カーネルは一部のsysctlキーについて"iPad8,6"を返すようハードコードされている
  • arm64ビルドは追加の問題やバイナリ非互換を抱えるため、試す価値は低いと判断されている
  • VM実行には、Apple silicon向けVM管理サードパーティアプリtartのforkであるsuper-tartが使われている
    • super-tartはVirtualization.frameworkの必要な非公開機能を使えるようにする
    • _setProductionModeEnabled(false)の設定変更など、一部変更はまだすべてpushされていない
    • private APIを使うVirtualization.frameworkツールではSIPを無効化する必要があり、AMFIも無効化が必要な場合がある
  • 復元ツールとしてはidevicerestoreのforkが使われている

カーネルパッチ

  • vma2pwnで使った署名検査パッチが必要な可能性はあるが、CPFM 01方式で必須かどうかははっきりしない
  • 署名関連パッチは、vma2カーネルで次の関数が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は1を返すようにパッチする必要がある
  • iOSバイナリはシミュレータ用プラットフォームバイナリではないため、当初はEXEC, [0xe] Binary with wrong platformで終了する
    • XNUのPLATFORM_IOS検査行をスキップするようパッチすれば解決する
    • vma2カーネルではB.NEBに変える形で適用される
  • system keybag非互換のため、Setup.appの代わりにPreBoard.appが“Swipe up to upgrade.”を表示する
    • ipc_make_system_keybagに2つのパッチを当てて関数がエラーを返さないよう強制すると、PreBoard.appまで到達できる
    • この制約はまだ完全には解決していない
  • iOSシステムフレームワークとmacOSカーネルの間にあるIOMFB構造体サイズ不一致は、CLCDTransaction size mismatch. Returning error 0x%X.という文字列とともにカーネルパニックを引き起こす
    • IOMobileFramebufferUserClient::swap_submitのサイズ検査を除去すると、パニックは止まる

システムファイルのパッチ準備

  • 復旧ramdiskとiOSシステムファイルは署名されているため、パッチ後に再署名しないと実行時に終了する
  • 改変環境ではProcursusのldid利用が推奨される
    • インストール例: brew install ldid-procursus
    • 再署名例: ldid_macosx_arm64 -S -M <binary>
    • -Sはバイナリをpseudo-signし、-Mは既存entitlementsを保持する
  • 多くのバイナリはidentity検査を行うため、再署名前に名前をidentityへ変更し、あとで元に戻す必要がある
    • keybagdcodesign -d -v keybagdIdentifier=com.apple.keybagdを確認する
    • mv keybagd com.apple.keybagd
    • ldid_macosx_arm64 -S -M com.apple.keybagd
    • mv com.apple.keybagd keybagd
  • 自動でシンボルが付かない関数は、文字列XRefを探し、ログ呼び出し参照から呼び出し元関数を追跡する方法で見つけられる
  • シリアルターミナルで対話可能なシェルを得るため、BashとLaunchDaemonを移植できる
    • Procursusのようなバージョン互換iOS jailbreak payloadから関連ファイルをコピーする方法が使われている

DMGとramdiskの改変

  • システムや復旧ramdiskにパッチを当てるには、DMGボリュームを直接改変する必要がある
  • iOS 15.0.2の例では、IPSWに含まれるiOS Systemボリュームを読み書き可能な形式へ変換する
    • hdiutil convert -format UDRW -o 018-66258-074-rw.dmg 018-66258-074.dmg
    • マウント後 sudo mount -uw /Volumes/Sky19A404.N104N841OS
    • 改変後 hdiutil convert -format ULFO -o 018-66258-074.dmg 018-66258-074-rw.dmg
    • その後 asr imagescan --source 018-66258-074.dmg
  • iOS起動前の復旧ramdiskにある/usr/local/bin/restored_externalの改変が必要
    • iOS版restored_externalMKBKeyBagCreateSystemでsystem keybagを作ろうとするが、macOSカーネルと互換性がない
    • エラー検査を取り除く形で回避する
    • ramrod_set_NVRAM_variable呼び出し条件をパッチしてallow-root-hash-mismatchをtrue、つまり1に設定し、root hash認証をスキップする
  • 復旧ramdiskの/usr/sbin/asrも改変が必要
    • iSuns9のasr64_patcherを利用できる
    • "Image failed signature verification."文字列を出力する関数を見つけ、それを参照する関数でARMv8-AのBL呼び出しを"Image passed signature verification"経路へ進むBジャンプに置き換える
    • iOS 15.0.2のasrバイナリでは、ファイルオフセット0x27A18b #0x7cを適用する

iOSシステムボリュームの改変

  • iOSシステム自体をmacOSカーネルに合わせるには、システムボリューム上でファイルシステムルートに置かれる大半のファイルを/System/Library/Templates/Dataへ移す必要がある
  • システムが起動すると、これらのファイルは/に存在することになる
  • 通常ルートの空フォルダは、実際に空でもシステムボリューム上に残しておく必要がある場合がある
    • 例として/Applicationsがある
    • この点は十分にはテストされていない

launchdとkeybagdのパッチ

  • iOS起動初期には/sbin/launchdが実行され、失敗なく初期ブートプロセスを始めるにはパッチが必要
  • 最初のパッチは、バイナリに埋め込まれた設定plistへ適用する
    • <key>SIGTERMTimeout</key>文字列を探し、約172バイト前を見ると該当設定が見つかる
    • mount-phase-2fipstzinitfinish-demo-restorefudxpcroleaccountdprng_seedctlMSUEarlyBootTaskセクションに<key>PerformAfterUserspaceReboot</key><true/>を追加する
    • data-protectionセクションのRequireSuccess<false/>へ変更する
  • data-protection変更は、/usr/libexec/init_data_protectionがVM上で実行されると失敗するため必要になる
    • このファイルは/usr/libexec/seputilへのシンボリックリンクになっている
  • 埋め込みplistの変更では既存の文字列領域を超える可能性があるため、XML minimizerで圧縮したXMLを貼り付け、残り領域をXML向きの空白文字で埋めることができる
  • launchd/usr/libexec/keybagdも初期化するが、このバイナリは前述のカーネル差異によって失敗する
    • 回避策の1つは、単に終了コード0で終わる実行ファイルをコンパイルしてkeybagdの代わりに置くこと
    • fixkeybagプロジェクトも検討されたが、このアプリのsystem keybag生成コードもMKBKeyBagCreateSystemを呼ぶため、起動済みiOS状態でも失敗する
  • launchdには、Userspace reboot changed system version: previous %s != current %sというパニック文字列を引き起こす条件分岐TBZNOP化する追加パッチも必要

mount、DYLD shared cache、グラフィック層の改変

  • macOSブートチェーンとmacOS ramdiskで復元処理を行うため、iOSの/sbin/mountでは作成されたAPFSボリュームを適切に扱えない
  • 解決策は、macOSシステムボリュームからmountバイナリを持ってきてMach-OメタデータをiOSで実行可能なように改変し、置き換えること
    • 手動でも可能で、macOSに含まれるvtoolも利用できる
  • より難しい改変はDYLD shared cacheのパッチ
    • macOSのIOSurfaceRootとiOSのIOCoreSurfaceRootは本質的に同じドライバだが、名前の違いのため互換性がない
    • iOS DYLD shared cacheにはより長い文字列"IOCoreSurfaceRoot"があるため、これを"IOSurfaceRoot"に置き換え、余ったバイトを0x00で埋める
  • DYLD shared cacheの解析と抽出にはblacktopのipswツールが使われる
    • ipsw dyld split <dsc file>で内蔵dylibを分離する
    • /System/Library/Frameworks/IOSurface.framework/IOSurfaceバイナリで__iosConnectInitalize関数の"IOCoreSurfaceRoot"参照を探す
    • ipsw dyld a2oで仮想アドレスをファイルオフセットへ変換する
    • 例ではdyld_shared_cache_arm64e0x28fde373オフセットをパッチする
  • DYLD shared cache改変後、別位置のcdhashが一致せず例外が発生する
    • Virtualization.frameworkフロントエンドが提供するGDB stubで、カーネルのcs_validate_hash関数にブレークポイントを置き、完全なcdhashを確認する
    • iOS 15.0.2の例では、ファイルオフセット0x5a9cffc0の既存バイトを新しいcdhashへパッチする

システムデーモンとアクティベーションのパッチ

  • watchdogdはVM上で動作中かを確認し、正常終了するmacOSコード経路がないためcrash-loopを起こすが、このクラッシュは無害と見なされている
  • backboarddでは、PreBoard.appにつながり得るデータ移行関連呼び出しのパッチを試したが、Appleロゴで止まる以外の違いは確認できなかった
  • lockdowndget_device_type_internal_block_invoke関数では、"ShouldHactivate"に対するgetMGInt呼び出しをmov x0, #1へパッチし、開発環境で通常のiOSアクティベーション制限を回避するhactivationを強制する
  • mobileactivationdもhactivationを許可するようパッチできる
    • shouldHactivate関数をARMv8-A命令のmov x0, #0retへ変更する
  • vma2 device treeに必要な追加修正は、読者が自分で対処する課題として残されている
  • 一部動作のためにchmod -R 777 /のような異常な措置が必要になる場合がある

残る制約とタッチ入力

  • system keybag問題を越えるには、iOSシステムとカーネル内にある該当構造をさらに理解し、追加パッチを作る必要がある
  • このプロジェクトにはすでに少なくとも数百時間が投入されており、現在公開されている進捗はPreBoard.appまで起動するレベル
  • 公開vma2 Macカーネルとファームウェアでタッチ機能が動くかどうかは、まだ確認されていない
  • Virtualization.frameworkにはタッチ関連の非公開APIがある
    • _VZAppleTouchScreenConfiguration
    • _VZUSBTouchScreenConfiguration
    • _VZTouch
    • _VZMultiTouchEvent
  • これらのAPIでタッチイベントを送れるが、正確なパラメータの使い方は完全には把握されていない
    • TouchPhase enumはNSTouch.Phaseと同名の値を実装した単純なenum
    • サンプルコードはときどき例外を発生させることがある
    • 座標値がVMの期待する方式でマップされるか、VMがそれを処理できるかも確認されていない
  • デモでは起動シーケンスが示されており、途中の約30秒の待機区間はカットされている

1件のコメント

 
GN⁺ 2024-10-08
Hacker News の意見
  • Corellium は法的紛争に勝ったことで、セキュリティ研究向けに iOS クラウド VM を貸し出せるようになった https://hn.algolia.com/?query=corellium
    iOS を Apple Silicon MacBook 上で仮想化できるなら、商用 iOS 仮想化サービスの需要は減るかもしれない
    個人は月額 $400、企業は年額 $60,000 程度 https://support.corellium.com/subscriptions/pricing

    • なんてことだ、時間あたり $4〜$8 なのに、誰がこんな VM にお金を払うのか気になる
  • いいね。次は iPad に macOS をインストールする方法も見つけて、Apple が作ってくれるのを待っていた あのコンピュータ をついに使えるようにしてほしい

    • Windows XP から始められる
      https://www.theverge.com/2024/7/22/24200536/windows-xp-ipad-...
    • 先行研究の部分によると、[Zhuowei Zhang] は GUI macOS アプリは iOS 上では実行できないが、グラフィカルな iOS アプリは macOS 上で実行できると結論づけた
      Mac Catalyst も予想どおり一方向にしか動作しないようだ
    • 本当に1000回同意する。ここ数週間、まさにこのテーマ、つまり 開発用 OS が動くタブレット を探していた
      サイズだけなら iPad Mini がいいが、iPadOS は使いものにならず、今は Surface Go を見ている。ただ少し大きい
      MacOS 搭載製品がないのは分かっているが、Win11 が動く小型タブレットのおすすめがあれば教えてほしい。必要なら中国から注文するつもりもある
    • iPad Pro は M4 を搭載した 最速のデバイス
    • 上下逆さまの出っ張りと着脱式キーボードが付いた MacBook Air と呼べば、それで十分なのだろうか?
  • 作者の GitHub プロフィールを見ると、どうやら コンピュータサイエンスを卒業したばかり のようだが、本当に印象的な仕事だ

    • Apple から採用オファーがすぐ来るのではないかと思う
  • Apple が Simulator を Emulator にしなかった理由は、人々に iOS の内部基盤層 を掘り下げてほしくなかったから、という気がする

    • そもそも Simulator であって Emulator ではなかった別の理由として、当時の iOS または iPhone OS の多くのコンポーネントが既存の Mac OS X ライブラリ のフォークだったからかもしれない
    • 開発者はまだ Intel Mac も使っており、そこでは ARM iOS を仮想化できない
    • iPhone、Mac、iPad がすべて arm64、それも Apple Silicon ベースになった今、iOS と macOS の ブートローダ がどれほど違うのか本当に気になる
      ブートローダを越えた後、とくに Apple がハードウェアまで管理している状況で、2つの OS と多くの差異を維持し続ける理由があるのか疑問だ
  • qemu-t8030 を作った人が SpringBoard の実行に成功しているが https://mastodon.social/@ntrung03/109712247237110967、コードは公開していない
    その進展が今回の作業と統合できれば素晴らしいと思う

  • 過去のコメント: https://news.ycombinator.com/item?id=40219423

  • 関連記事: https://worthdoingbadly.com/hv/
    脱獄済みの iPhone 12 / iOS 14.1 で ハードウェアアクセラレーション付き仮想マシン を扱っている

  • 少し脇道にそれるが、誰か ARM macOS を x86-64 上で仮想化 したことがあるのか気になる

    • 不可能。一般に「仮想化」とは、ハードウェア仮想化を通じて OS を実行することを意味し、ホスト CPU がコードをネイティブに実行しつつ、すべての入出力をハイパーバイザに渡す方式だ
      そのため、ホストシステムと同じ CPU アーキテクチャ 向けにビルドされた OS だけを仮想化できる
      ARM ソフトウェアを x86 で実行したり、その逆を行うような残りのケースではエミュレーションを使う必要があり、これはコードを解釈するか動的に再コンパイルする方式だ
      定義上、どんなものでも別のどんなものの上でもエミュレーションは可能ではある。最近では、最初のマイクロプロセッサである Intel 4004 上で MIPS 向け Linux を起動した例もあるが、性能が問題になり得る
    • QEMU で一般的な ARM を仮想化してみれば、Raspberry Pi の性能にも及ばないことが分かる。最近のバージョンには標準で入っているはず
      Mn CPU を仮想化するのはさらに使い道が少なそうだ
    • Hackintosh プロジェクトを調べてみるとよい
  • Apple はすでに Xcode に iOS Simulator を提供しているが、このプロジェクトが Apple 提供ツールより優れている点は何なのか気になる

    • Simulator は実際の iOS やアプリの iOS ビルドを実行しているわけではない。Simulator でアプリを実行すると、アプリは現在の Mac のネイティブ命令セット向けにコンパイルされ、期待される iOS の動作をまねたり一部はスタブだけを提供したりする Mac のフレームワークやライブラリにリンクされて実行される
      たとえば App Store から iOS バイナリを取得して iOS Simulator でそのまま実行することはできず、Intel Mac ではなおさらできない
      Simulator は完全な iOS を実行しないため、実際の iOS 内部がどう動作するのかを調査して学ぶこともできない。Simulator のフレームワークを十分深く掘っていくと、結局 macOS に戻ってくる
      一方、エミュレータは実機と同じ完全な iOS ビルドを実行する方式だ。理論上はどんな iOS バイナリでも変更なしに実行し、実際の OS がどう動作するのかを調査できる
      Wine でアプリを実行することと Windows VM でアプリを実行することの違いに似ている。ただし Simulator の場合は、Windows アプリを実行する前に Wine 環境に合わせて別途再コンパイルしてリンクしなければならない状況に近い
      Windows 内部を研究したいなら、Wine で実行するだけで学べることは多くないが、Windows VM を調べればはるかに多くを学べる
  • クリックファームにとっては少し早い クリスマスプレゼント になりそうだ