はじめに
iOSのセキュリティ研究・脱獄界隈で、こんな短い投稿が流れることがある。
Dyld interposing PAC bypass on iOS 26.1.
たった一文に dyld / interposing / PAC / bypass と、専門用語が4つも詰まっている。分解するとこうなる。
| 用語 | 意味 |
|---|---|
| dyld | iOS/macOS の動的リンカ(実行時にライブラリを繋ぐ) |
| interposing | 関数を横取り(フック)する技術 |
| PAC | Apple のチップ(A12 以降)のポインタ改ざん防止機能 |
| bypass | その防御を回避する |
つまり「iOS 26.1 で、PACという防御をかいくぐって関数フックができた」という報告だ。
この界隈では dyld / fishhook / Frida / Substitute / ElleKit / PAC / AMFI といった単語が一緒に飛び交う。初見だと「結局これは何の話をしているのか」が掴めない。
👉 この記事のゴールは、これらを1枚の地図として理解することだ。個々のツールを覚える前に、まず「そもそも何の世界の話か」から入る。
❗ なお本記事は概念の整理が目的で、動作する回避コードや攻撃手順は扱わない。
全体像:iOSアプリが動く「層」を一枚で
個別ツールに入る前に、舞台を先に見せる。iOSのアプリは単体では動かない。
- アプリのコードは、Appleが用意したシステムのライブラリ(Foundation / UIKit など)の上に乗っている
- それらを実行時に繋ぎ合わせるのが dyld(動的リンカ)
- さらに下に カーネル(XNU) があり、全体を Appleのセキュリティ機構(PAC / AMFI など)が固めている
ここで対立の構図が見えてくる。
- 関数フック = この層のどこかに割り込む行為(攻める側)
- PAC / AMFI = その割り込みを防ぐ仕組み(守る側)
👉 この1枚で「dyld も フック も PAC も AMFI も、同じ土俵(アプリが動く層)の話」だと分かる。冒頭の投稿は、この土俵で「攻める側が守る側を一時的に上回った」という報告だった。
登場人物の地図:攻める側と守る側
6つの用語を、役割で2グループに仕分けると一気に整理できる。
- 攻める側(フックする道具):fishhook / dyld interposing / Frida / Substitute・ElleKit
- 守る側(防御機構):PAC / AMFI
この記事では、まず**攻める側(フックとは何か・どの層に刺すか)を見て、次に守る側(PAC / AMFI)**を見る。最後に両者がなぜ衝突するかを整理する。
フックとは何か
関数フックとは、ある関数の呼び出しを別の関数に差し替える技術だ。
例えば、ファイルを開く open() を、自作の my_open() に差し替える。
my_open() の中で「引数を記録する」「結果を書き換える」「本物の open() を呼ぶ」といったことができる。
これができると、こういうものが作れる。
- API監視(どの関数がどう呼ばれたか)
- ログ取得・デバッグ
- 動作の改変(チート、機能追加)
- Frida のような動的解析ツール
- 脱獄ツールの「tweak(改造)」
👉 フックは「アプリの外から挙動をのぞき見・書き換えする」ための基本テクニックだ。問題はどの層に、どうやって割り込むかで、ここが道具ごとに違う。
フックの3レイヤ:攻める側の住み分け
「フック」とひとくちに言っても、割り込む層が違う。攻める側の4つはここで住み分けている。
① シンボルテーブル書き換え(fishhook)
アプリが「外部の関数」を呼ぶとき、実際のアドレスは実行時に dyld が表(シンボルテーブル)へ書き込む。fishhook はこの表の中身を自分の関数に差し替える。自分のアプリ(や埋め込んだSDK)の中で完結し、脱獄は前提にしない。
② dyld interposing(__interpose)
「この関数を、この関数に差し替えてください」と dyld に正規の形で申告する仕組み。専用セクション(__DATA,__interpose)に差し替えペアを置くと、dyld が読み込み時に適用する。冒頭の投稿が指していたのはこの経路だ。
③ 実行中のメモリ書き換え / トランポリン(Frida / Substitute / ElleKit)
動作中の関数の本体(コード)を直接書き換える、あるいは入口に「飛び先」を仕込む(トランポリン)。任意のプロセスに広く効かせられて強力だが、その分ハードルが高く、実機では脱獄環境やそれ相当の権限を前提にすることが多い。
❌ 誤解:「フックは一種類」ではない。どの層に刺すかで道具も難易度も変わる。
👉 ①②はアプリ/ローダの仕組みに乗る比較的おだやかな方法、③は動いているコードそのものに手を入れる強い方法、というグラデーションで捉えるとよい。
守る側:PACとAMFI
ここまでが攻める側。ではAppleはどう守っているのか。今回の主役は PAC と AMFI だ。
PAC(Pointer Authentication)
Apple の A12 以降(Armv8.3-A の機能)に載っている、ポインタ改ざん防止の仕組み。関数ポインタや戻り先アドレスに、暗号的な署名を付ける。
例えば攻撃者が printf へのポインタを evil_function に書き換えても、署名が一致しないのでCPUが弾く。ポインタを差し替えるタイプの攻撃・フックにとって、これは大きな壁になる。
AMFI(AppleMobileFileIntegrity)
こちらはコードの素性を守る側。署名のないコードの実行そのものを拒否する。勝手に用意した dylib を読み込ませたり、コード領域を書き換えて実行させたりを止める。
👉 守る対象が違う。PAC はポインタの改ざん、AMFI はコード全体の署名(素性)。この2枚で「ポインタ書き換え」も「コード書き換え」も塞ぐ。
なぜフックはPACと衝突するのか
ここまで来ると、対立がはっきりする。
- フック = 関数ポインタやコードを書き換える行為
- PAC / AMFI = まさにその書き換えを拒否する仕組み
つまり両者は構造的に真っ向からぶつかる。だから「PACをかいくぐってフックできた」が成果として注目される。冒頭の投稿の "PAC bypass" とは、この壁を一時的に越えたという話だ。
回避を考えるうえで問われるのは、おおまかに次のような点になる(本記事は概念どまりで、手順そのものは扱わない)。
- 署名が付いたポインタを、どうすれば正当な署名のまま扱えるか
- そもそもどの権限が要るか(自アプリ内で済むのか、脱獄やカーネル権限が要るのか)
❗ ここが重要な注意点。冒頭の投稿の一文だけでは、次のことは分からない。
- 完全なPACバイパスなのか、特定条件だけなのか
- 脱獄が要るのか、要らないのか
- ユーザ権限で済むのか、カーネル権限が要るのか
👉 短い投稿は「壁を越えた」という結果だけを示していることが多い。どの層で・どの権限で・どこまでを確かめないと、すごさの度合いは測れない。
一枚のまとめ:関係図と比較表
最後に、攻める側が刺す層と、守る側の位置を1枚に統合する。
道具ごとの整理はこうなる。
| ツール | 動作レイヤ | 脱獄の要否 | PAC / AMFI との関係 |
|---|---|---|---|
| fishhook | シンボルテーブル(自プロセス内) | 不要(自アプリ内で完結) | 直接は衝突しにくい層 |
| dyld interposing | dyld への差し替え申告 | 場面による | 正規経路寄り |
| Frida | 実行中メモリの書き換え | 実機は基本要(or 埋め込み構成) | 衝突しうる・要対処 |
| Substitute / ElleKit | トランポリン注入 | 要(脱獄環境の基盤) | 衝突しうる・要対処 |
👉 「脱獄が要るか」「PACとぶつかるか」は、どの層に刺すかでほぼ決まる。上の行ほどおだやか、下の行ほど強力で権限が要る。
まとめ
- iOSのフック話は、すべて「アプリが動く層のどこに割り込むか」という1枚の地図に載る
- 攻める側(fishhook / dyld interposing / Frida / Substitute・ElleKit)は、刺す層で住み分けている
- 守る側の PAC はポインタの改ざんを、AMFI はコードの素性を検証して弾く
- フックは「書き換える」、PAC/AMFIは「書き換えを拒否する」。だから両者は正面衝突し、"PAC bypass" が成果として注目される
- ただし短い投稿だけでは どの層・どの権限・どこまで かは分からない。そこを確かめて初めて度合いが測れる
👉 攻防の本質は「署名 vs 書き換え」の綱引き。Appleは毎年 PAC / AMFI などを強化し、研究者はその隙を探す。次に深入りするなら、Frida や ElleKit の公式ドキュメントから、自分が触れる範囲(自アプリ・開発構成)で手を動かすのがおすすめだ。