iOS 14 QEMUエミュレーションの旅の始まり
- 既存のオープンソースプロジェクト
alephsecurity/xnu-qemu-arm64 を使っていたが、read-only で拡張性に欠ける問題があった
- その後
TrungNguyen1909/qemu-t8030 プロジェクトを使うことで、次の機能を活用できた:
- iOSの復元機能(USB接続用QEMUを同伴)
- iOS 14の起動
- 最新のQEMUバージョンをベース
- 詳細なWikiドキュメントを提供
launchd.plist の修正によりシェルおよびSSHアクセスに成功し、良い出発点となった
- 目標は、UIとアプリを実行できる完全なiOSエミュレーション環境の構築である
カーネルパッチとPongoOSの導入
t8030 プロジェクトはQEMU内部でカーネルパッチを当てる構造だった → 保守性と拡張性の問題が発生
- 脱獄の経験をもとに、
PongoOS を通じて checkra1n パッチを適用する構成へ移行
- QEMUでSRAMサイズを増やしてPongoOSを実行し、checkra1n-KPFモジュールを注入
- ブート時にブートロム/iboot機能の欠落でFPU未設定の問題が発生 → ARMドキュメントを参考に解決
- A13以降でPAC(Pointer Authentication)が導入され、一部パッチが無効化された
task_for_pid0 (tfp0) の例として、PAC導入前後のバイナリを比較
カーネルパッチ自動化ツールの開発
- 既存のcheckra1n動的パッチ方式は読みづらく修正しにくい → 宣言的なテキストベースのパッチ方式を導入
- 2つの
Mach-O バイナリを比較してアセンブリ差分を抽出し、テキストパッチを生成
- Pongoで起動後にメモリをダンプしてカーネルを再構築 → 全パッチをテキストファイルに整理しコメント化
グラフィックレンダリング: Metal vs ソフトウェアレンダリング
- iOSはすべてのUIレンダリングを
Metal API経由で行う → GPUが必要
- GPUエミュレーションが複雑なため、代替案を検討:
- ソフトウェアレンダリング
- Metal呼び出しを実機へプロキシ転送
- iOS 14では
gpu=0 bootarg が削除された → QuartzCoreを解析してfallback動作を確認
- 脱獄端末で
QuartzCore をパッチし、ソフトウェアレンダリングが動作することを確認(遅いが可能)
- Metalプロキシ案も実験したが、Objective-CおよびAPIの複雑さにより中断
フレームバッファとIOSurfaceのデバッグ
t8030 QEMUにはフレームバッファ実装がない → ChefKissInc/QEMUAppleSilicon フォークを使用
- 初期ブート時にはAppleロゴと進行表示が見えたが、その後は黒画面 → デバッグ開始
- IOMFB kextの解析結果、2つのモードが存在:
- 固定アドレスのフレームバッファ(初期表示用)
- DMAベースのマルチプレーン構成
- システム起動中はDMAベースのモードが使われる → QEMUのトレースでカーネルレジスタ設定を確認
- しかし依然として画面には何も表示されなかった
アドレスランダム化の無効化
- カーネルのアドレスランダム化はボード初期化コードで無効化できる
- ユーザー空間のランダム化は
_load_machfile をパッチして無効化
- dyldキャッシュはすべての動的ライブラリを含む巨大なバイナリ → ブート時に固定アドレスへロードされる
- Cツールを作成してdlopen後に
_dyld_* 関数でアドレスを確認
- GDBで
dyld ライブラリをデバッグ可能にした → 特に IOMFB、SpringBoard、QuartzCore に注目
USBログへのアクセスとlockdowndの回避
- 実機では
idevicesyslog でシステムログを収集できる → USB認証が必要
- lockdowndは鍵の保存にSEPが必要なkeybagを使用 → エミュレータには存在しない
- 既存関数の位置にシェルコードを挿入し、鍵ファイルから直接ロードするようにした
- USB接続されたQEMU間で鍵認証の回避に成功 → ログ収集が可能に
- QuartzCoreが正常に初期化され、ソフトウェアレンダリングを使っていることを確認
PAC(Pointer Authentication)の回避
backboardd の修正中にPACエラーが発生 → ARMv8.3で導入されたセキュリティ機能
- PAC命令をNOPに置き換える方式は侵襲的すぎる
- PAC命令は互換モードでコンパイル可能 → QEMUでPACを無視すれば実行できる
- QEMU 7ではPAC回避不可 → QEMU 8.2.1へ移行
- Apple専用命令やGL例外レベルなど、多数のQEMUカスタムコードの移植が必要
- 結果としてQEMU 8でiOSのブートに成功し、PACの無力化も可能になった
backboardd とグラフィック出力の確認
backboardd は動作しているが画面表示がない → 複数の原因が考えられる
- DMAメモリをダンプしても有意な出力はなかった
iosurface_lock でアドレスを確認してフレームをダンプしたが、圧縮された形でGPUへ渡されているようだった
- iPhone X(t8015)では非圧縮出力を確認 → QEMUのDTBを修正し、
chip-id を t8030 → t8015 に変更
- 結果としてブート後にAppleロゴが表示された
進行バーとシステムエラーの追跡
- ロゴの後に白い進行バーが表示 → 90%で停止
- ログ解析により
mobileactivationd と SpringBoardFoundation の問題を発見 → パッチ後にUIが変化
- 進行停止問題を解決するため、多数のシステムログ解析が必要
dyldキャッシュとユーザー空間パッチの自動化
- カーネルと同じ方式で、ユーザー空間にもテキストベースのパッチ方式を使用
- dyldキャッシュは2GBと大きく、修正効率が悪い → 内部ツールを改善して:
- dyld内のオフセットを追跡
dd コマンドで特定位置を直接パッチ
- カーネル署名検査の回避パッチも並行して必要
PreBoard の起動とUIの確認
PreBoard アプリはエラー時に表示されるシステムアプリ → 直接起動可能
- VNCサーバーを追加し、キーボードで画面ロック解除を試行
- アンロック後、
vImage フレームワークがAMX(Apple Matrix Coprocessor)命令を使用 → QEMUは未対応
vImage のソフトウェアfallback経路へパッチして問題を解決
- パッチ後、テキスト入力可能な画面の表示に成功
結論
- SpringBoard起動直前まで到達 → 完全なUI実行はもはや時間の問題
- カーネル、ユーザー空間、グラフィック、セキュリティ機能(PACなど)を多角的に解析し、パッチを実施
- QEMUベースの実用的なiOSアプリのデバッグおよびテスト環境の可能性を確認
1件のコメント
Hacker Newsのコメント