Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
xnuspy — checkra1n'able デバイス向けの iOS カーネル関数フッキングフレームワーク | Kitploit
ツール/GitHubGitHub/jsherman212/xnuspy
iOSセキュリティエクスプロイトデバッガバイナリ解析
GitHubjsherman212/xnuspy

xnuspy

checkra1n'able デバイス向けの iOS カーネル関数フッキングフレームワーク

リポジトリを見る
595112534年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

xnuspy

代替テキスト

カーネルログからの出力:example/open1_hook.cをコンパイルして実行した後

xnuspyは、新しいシステムコールxnuspy_ctlをインストールするpongoOSモジュールであり、ユーザー空間からカーネル関数をフックすることができます。iOS 13.x、iOS 14.x、iOS 15.xを、checkra1n 0.12.2以降でサポートしています。4Kデバイスはサポートされていません。

このモジュールはKTRR/KPPを完全に無効化し、EL1内でRWXメモリを作成することを可能にします。メイン端末では使用しないでください。

libusbが必要:brew install libusb

Building

トップレベルディレクトリでmakeを実行します。ローダーとモジュールがビルドされます。

Build Options

makeの前にこれらを追加します。

  • XNUSPY_DEBUG=1
    • xnuspyからのデバッグ出力をカーネルログ(kprintf)に送信します。
  • XNUSPY_SERIAL=1
    • xnuspyからのデバッグ出力をIOLogに送信します。
  • XNUSPY_LEAKED_PAGE_LIMIT=n
    • xnuspyがリークを許可されるページ数を設定します。そのガベージコレクションスレッドがそれらを解放し始める前の値です。デフォルトは64です。詳細はカーネルパニックのデバッグを参照してください。
  • XNUSPY_TRAMP_PAGES=n
    • xnuspyがトランポリン構造のために予約するページ数を設定します。デフォルトは1です。詳細は制限を参照してください。

XNUSPY_DEBUGとXNUSPY_SERIALは互いに依存しません。

Usage

すべてをビルドした後、checkra1nでデバイスをpongoシェルで起動します:/Applications/checkra1n.app/Contents/MacOS/checkra1n -p

ローダーとモジュールをビルドしたのと同じディレクトリで、loader/loader module/xnuspyを実行します。その後、xnuspyが処理を行い、数秒でデバイスが起動します。loaderはxnuspy-getkernelvを発行した後、SEPROMのエクスプロイトが必要な場合に備えてさらに数秒待機します。

Known Issues

たまに、私のいくつかの端末では、checkra1nのKPFが実行された後で"起動中"で停止することがあります。原因はまだわかっていませんが、発生した場合は再試行してください。また、bootx後にデバイスがハングした場合も再試行してください。最後に、iOS 13.3.1を搭載したiPhone Xでコンパイルされたxnuspy_ctlコードを実行可能としてマークするのはやや不安定ですが、他の端末では100%成功します。フックプログラムを実行するときにカーネル命令フェッチアボートでパニックになった場合は、再試行してください。

xnuspy_ctl

xnuspyはenosysシステムコールをパッチして、xnuspy_ctl_trampを指すようにします。これは小さなトランポリンで、コンパイルされたxnuspy_ctlコードを実行可能としてマークし、そこに分岐します。xnuspy_ctlの実装はmodule/el1/xnuspy_ctl/xnuspy_ctl.cにあり、例はexampleディレクトリにあります。

include/xnuspy/内にはxnuspy_ctl.hがあり、xnuspy_ctlの定数を定義するヘッダーです。これはカーネル関数をフックするすべてのプログラムにインクルードされることを意図しています。

sysctlbynameを使用して、どのシステムコールがパッチされたかを確認できます:``` size_t oldlen = sizeof(long); long SYS_xnuspy_ctl = 0; sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, &oldlen, NULL, 0);

root@kitploit:~
このシステムコールは4つの引数`flavor`、`arg1`、`arg2`、`arg3`を取ります。
フレーバーは`XNUSPY_CHECK_IF_PATCHED`、`XNUSPY_INSTALL_HOOK`、
`XNUSPY_REGISTER_DEATH_CALLBACK`、`XNUSPY_CALL_HOOKME`、`XNUSPY_CACHE_READ`、
`XNUSPY_KREAD`、`XNUSPY_KWRITE`、または`XNUSPY_GET_CURRENT_THREAD`のいずれかです。
残りの3つの引数の意味はフレーバーに依存します。

## `XNUSPY_CHECK_IF_PATCHED`
これは`xnuspy_ctl`が存在するか確認するためのものです。このフレーバーで呼び出すと`999`が返されます。他の引数の値は無視されます。

## `XNUSPY_INSTALL_HOOK`
このフレーバーは[`MSHookFunction`](http://www.cydiasubstrate.com/api/c/MSHookFunction/)のAPIに合わせて設計しました。
`arg1`はフックしたいカーネル関数の*スライドされていない*アドレスです。スライドされたアドレスを指定すると、ほぼ確実にパニックが発生します。`arg2`はABI互換の置換関数へのポインタです。`arg3`は、`xnuspy_ctl`が元のカーネル関数を表すトランポリンのアドレスを`copyout`するためのポインタです。元の関数を呼び出すつもりがない場合は`NULL`にできます。

## `XNUSPY_REGISTER_DEATH_CALLBACK`
このフレーバーでは、オプションの「デスコールバック」、つまりフックプログラムが終了したときにxnuspyが呼び出す関数を登録できます。カーネルフックで作成したものをクリーンアップする機会を提供します。カーネルスレッドを作成した場合は、この関数で終了するように指示します。

コールバックは非同期で呼び出されるわけではないため、ブロックするとxnuspyのガベージコレクションスレッドの実行が妨げられます。

`arg1`はコールバック関数へのポインタです。他の引数の値は無視されます。

## `XNUSPY_CALL_HOOKME`
`hookme`は、xnuspyがxnuspyキャッシュを通じてエクスポートする小さなアセンブリスタブで、フックするためのものです。このフレーバーで`xnuspy_ctl`を呼び出すと`hookme`が呼び出され、実際のカーネル関数をフックしなくても簡単にカーネルコード実行を獲得する方法を提供します。

`arg1`は`hookme`が呼び出されたときに渡される引数です。`NULL`にできます。

## `XNUSPY_CACHE_READ`
このフレーバーでは、xnuspyキャッシュから読み取る方法を提供します。キャッシュには`kprintf`、`current_proc`、`kernel_thread_start`、いくつかのlibc関数、カーネルスライドなど、多くの便利なものが含まれているため、自分で見つける必要はありません。キャッシュIDの完全なリストについては、`example/xnuspy_ctl.h`を参照してください。

`arg1`は`xnuspy_ctl.h`で定義されたキャッシュIDの1つで、`arg2`は`xnuspy_ctl`が要求したアドレスまたは値を`copyout`するためのポインタです。他の引数の値は無視されます。

## `XNUSPY_KREAD`
このフレーバーでは、tfp0を使わずにユーザースペースからカーネルメモリを簡単に読み取る方法を提供します。

`arg1`はカーネル仮想アドレス、`arg2`はユーザースペースバッファのアドレス、`arg3`はそのユーザースペースバッファのサイズです。`arg1`から`arg2`へ`arg3`バイトが書き込まれます。

## `XNUSPY_KWRITE`
このフレーバーでは、tfp0を使わずにユーザースペースからカーネルメモリに簡単に書き込む方法を提供します。

`arg1`はカーネル仮想アドレス、`arg2`はユーザースペースバッファのアドレス、`arg3`はそのユーザースペースバッファのサイズです。`arg2`から`arg1`へ`arg3`バイトが書き込まれます。

## `XNUSPY_GET_CURRENT_THREAD`
このフレーバーは、呼び出し元スレッドのカーネルアドレスをユーザースペースに提供します。

`arg1`は`xnuspy_ctl`が`current_thread`の戻り値を`copyout`するためのポインタです。他の引数の値は無視されます。

### エラー
`XNUSPY_CHECK_IF_PATCHED`を除くすべてのフレーバーでは、成功時に`0`が返されます。エラー時には`-1`が返され、`errno`が設定されます。`XNUSPY_CHECK_IF_PATCHED`はエラーを返しません。XNUの`mach_to_bsd_errno`を使用して`kern_return_t`を適切な`errno`に変換します。

#### `XNUSPY_INSTALL_HOOK`に関するエラー
`errno`は次のように設定されます...
- `EEXIST`の場合:
  - `arg1`で示されるスライドされていないカーネル関数に既にフックが存在する。
- `ENOMEM`の場合:
  - `unified_kalloc`が`NULL`を返した。
- `ENOSPC`の場合:
  - 空き`xnuspy_tramp`構造体がない。これはxnuspy内部のデータ構造です。同時に何百ものカーネル関数をフックしているのでなければ発生しません。より多くの関数フックが必要な場合は、[制限](#limits)を参照してください。
- `ENOTSUP`の場合:
  - 呼び出し元がMach-O実行可能ファイルまたは動的ライブラリからのものでない。
- `ENOENT`の場合:
  - `mh_for_addr`が、呼び出し元のアドレス空間内で`arg2`に対応するMach-Oヘッダを特定できなかった。
- `EFAULT`の場合:
  - 特定されたMach-Oヘッダが実際にはMach-Oヘッダではない。これはおそらく発生しません。
- `EIO`の場合:
  - `mach_make_memory_entry_64`が、特定されたMach-Oヘッダの`__TEXT`セグメントと`__DATA`セグメント全体に対するメモリエントリを返さなかった。

`errno`は、`vm_map_wire_external`、`mach_vm_map_external`、`mach_make_memory_entry_64`、`copyin`、`copyout`、および該当する場合は1回限りの初期化関数の戻り値にも依存します。

このフレーバーがエラーを返した場合、対象のカーネル関数はフックされませんでした。`arg3`に非`NULL`ポインタを渡した場合、そのポインタは初期化されている場合とされていない場合があります。初期化されていない場合は使用すると危険です。

#### `XNUSPY_REGISTER_DEATH_CALLBACK`に関するエラー
`errno`は次のように設定されます...
- `ENOENT`の場合:
  - 呼び出し元のプロセスがカーネル関数を1つもフックしていない。

このフレーバーがエラーを返した場合、デスコールバックは登録されませんでした。

#### `XNUSPY_CALL_HOOKME`に関するエラー
`errno`は次のように設定されます...
- `ENOTSUP`の場合:
  - `hookme`が`xnuspy_tramp`構造体を含むメモリから遠すぎる。これはpongoOS内で判断され、xnuspyがカーネルキャッシュ内の未使用コードにフォールバックしなければならなかった場合にのみ発生します。この場合、`hookme`を呼び出すとほぼ確実にカーネルパニックが発生するため、別のカーネル関数をフックする方法を見つける必要があります。

このフレーバーがエラーを返した場合、`hookme`は呼び出されませんでした。

#### `XNUSPY_CACHE_READ`に関するエラー
`errno`は次のように設定されます...
- `EINVAL`の場合:
  - `arg1`で示される定数がキャッシュ内の何も表していない。
  - `arg1`が`IO_LOCK`だが、カーネルがiOS 14.4.2以下またはiOS 15.xである。
  - `arg1`が`IPC_OBJECT_LOCK`だが、カーネルがiOS 15.xである。
  - `arg1`が`IPC_PORT_RELEASE_SEND`だが、カーネルがiOS 14.5以上である。
  - `arg1`が`IPC_PORT_RELEASE_SEND_AND_UNLOCK`だが、カーネルがiOS 14.4.2以下である。
  - `arg1`が`KALLOC_CANBLOCK`だが、カーネルがiOS 14.x以上である。
  - `arg1`が`KALLOC_EXTERNAL`だが、カーネルがiOS 13.xである。
  - `arg1`が`KFREE_ADDR`だが、カーネルがiOS 14.x以上である。
  - `arg1`が`KFREE_EXT`だが、カーネルがiOS 13.xである。
  - `arg1`が`PROC_REF`だが、カーネルがiOS 14.8以下である。
  - `arg1`が`PROC_REF_LOCKED`だが、カーネルがiOS 15.xである。
  - `arg1`が`PROC_RELE`だが、カーネルがiOS 14.8以下である。
  - `arg1`が`PROC_RELE_LOCKED`だが、カーネルがiOS 15.xである。
  - `arg1`が`VM_MAP_UNWIRE`だが、カーネルがiOS 15.xである。
  - `arg1`が`VM_MAP_UNWIRE_NESTED`だが、カーネルがiOS 14.8以下である。

`errno`は、`copyout`の戻り値、および該当する場合は1回限りの初期化関数の戻り値にも依存します。

このフレーバーがエラーを返した場合、`arg2`に渡したポインタは初期化されませんでした。

#### `XNUSPY_KREAD`および`XNUSPY_KWRITE`に関するエラー
`errno`は次のように設定されます...
- `EFAULT`の場合:
  - `arg1`または`arg2`のアドレス変換に失敗した。`XNUSPY_DEBUG=1`でコンパイルした場合、そのメッセージがカーネルログに出力されます。

このフレーバーがエラーを返した場合、カーネルメモリは読み取り/書き込みされませんでした。

#### `XNUSPY_GET_CURRENT_THREAD`に関するエラー
`copyout`が失敗した場合、`errno`はその戻り値に設定されます。

# 重要な情報

### よくある落とし穴
置換関数を書いているとき、自分がカーネルコードを書いていることを忘れがちでした。フックを書く際に留意すべき点をいくつか挙げます:

- *プログラムの`__TEXT`セグメントの外にあるユーザースペースコードは実行できません*。たとえば、うっかり`kprintf`の代わりに`printf`を呼び出すとパニックになります。呼び出したいlibc関数が`XNUSPY_CACHE_READ`で既に利用可能でない場合は、その関数を再実装する必要があります。ただし、他のカーネル関数への関数ポインタを作成してそれらを呼び出すことはできます。
- *ユーザースペースコードでよく使われる多くのマクロはカーネルでは安全ではありません*。たとえば、`PAGE_SIZE`は定数ではなく`vm_page_size`に展開されます。この変数を読み取る前にPANを無効にする(A10+では推奨しません)必要があります。そうしないとパニックになります。
- *コードを`-fno-stack-protector`および`-D_FORTIFY_SOURCE=0`でコンパイルしてください*。場合によっては、デバイスが別のユーザースペースポインタを逆参照して`___stack_chk_guard`を読み取る必要があり、A10+ではパニックになります。
- *安全のため、フックプログラムをコンパイラ最適化なしでコンパイルしてください*。

https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/style/style.html にざっと目を通すこともお勧めします。

### カーネルパニックのデバッグ
コードを書くときにはバグは避けられないため、最終的にはカーネルパニックを引き起こすことになります。パニックは必ずしもxnuspyのバグを意味するわけではないため、問題を報告する前に、元の関数を呼び出してその値を返す以外何もしない場合でもパニックが発生することを確認してください。それでもパニックが発生する場合は、おそらくxnuspyのバグです(その場合は問題を報告してください)。しかし、パニックが発生しない場合は、置換関数に問題があります。

xnuspyは実際にEL0ページに実行をリダイレクトしないため、パニックのデバッグは簡単ではありません。`module/el1/xnuspy_ctl/xnuspy_ctl.c`を開き、`xnuspy_install_hook`内で`kwrite_instr`への唯一の呼び出しの直前に、`IOSleep`を数秒間追加します。これは、デバイスがパニックになる前にログが伝播する十分な時間を確保するためです。`XNUSPY_DEBUG=1 make -B`でxnuspyを再コンパイルし、モジュールを再度ロードします。モジュールのロード後、まだ行っていなければ、`klog/`から`klog`をコンパイルします。デバイスにアップロードし、`stdbuf -o0 ./klog | grep shared_mapping_kva`を実行します。フックプログラムをもう一度実行し、次のような行が`klog`から出力されるのを待ちます:

`shared_mapping_kva: dist 0x7af4 uaddr 0x104797af4 umh 0x104790000 kmh 0xfffffff00c90c000`

複数のフックをインストールしている場合、複数回出現します。その場合、`dist`と`uaddr`は異なりますが、`umh`と`kmh`は同じです。`kmh`はプログラムの`__TEXT`セグメントのカーネルマッピングの先頭を指します。お気に入りの逆アセンブラでフックプログラムを開き、Mach-Oヘッダが`kmh`のアドレスになるようにリベースします。IDA Proの場合は、`Edit -> Segments -> Rebase program...`で`Image base`を選択します。デバイスがパニックして再起動した後、パニックログに置換関数のカーネルマッピングに対応するアドレスがある場合、それらは逆アセンブリと一致します。ない場合は、置換関数内に何らかの微妙なメモリ破壊がある可能性があります。

また、xnuspyは、フックがアンインストールされた後も、プログラムの`__TEXT`セグメントのカーネルマッピング上でカーネルスレッドがまだ実行中である(または実行する予定である)かどうかを知る方法がありません。これに対処するために、xnuspyが行うことの1つは、フックプログラムが終了した後、このマッピングをすぐに解放しないことです。代わりに、キューに追加されます。xnuspyのガベージコレクションスレッドが、そのキューに保持されているマッピングのページ数に関して設定された制限を超えたことを検出すると、キューの先頭から解放を開始し、その制限を超えなくなるまで続けます。デフォルトでは、この制限は1 MB(64ページ)です。

これは非常に役立ちますが、フックプログラムの`__TEXT`セグメントと`__DATA`セグメントが大きくなるほど、xnuspyがこの競合に勝つ可能性は低くなります。定期的にパニックが発生し、フックプログラムがやや大きい場合は、`make`の前に`XNUSPY_LEAKED_PAGE_LIMIT=n`を追加してこの制限を増やしてみてください。これにより、制限が64ページではなく`n`ページに設定されます。

### 制限
xnuspyは、XNUが起動する前に静的カーネルメモリの1ページを`xnuspy_tramp`構造体用に予約し、一度に約225のカーネル関数を同時にフックできます。さらに必要な場合は、`make`の前に`XNUSPY_TRAMP_PAGES=n`を追加できます。これにより、xnuspyは`xnuspy_tramp`構造体用に`n`ページの静的メモリを予約します。ただし、xnuspyがカーネルキャッシュ内の未使用コードにフォールバックしなければならなかった場合、これは無視されます。これがいつ発生するかについては、[動作の仕組み](#how-it-works)で詳しく説明しています。

### ロギング
何らかの理由で、`os_log_with_args`からのログは、コマンドラインツール`oslog`から出力されるストリームに表示されません。`kprintf`からのログもそこには届きませんが、`dmesg`で確認できます。ただし、`dmesg`はライブフィードではないため、`kprintf`ログをリアルタイムで表示するツール`klog`を作成しました。`klog/`にあります。`kprintf`メッセージに`dmesg`を連打する代わりに、こちらを使用することを強くお勧めします。

`klog`の実行後に`open: Resource busy`が表示された場合は、`launchctl unload /System/Library/LaunchDaemons/com.apple.syslogd.plist`を実行してから再試行してください。

残念ながら、XNUのブート引数に`atm_diagnostic_config=0x20000000`が設定されている場合、`NSLog`は表示されません。`klog`はこのブート引数が存在することに依存しています。`NSLog`を戻したい場合は、`loader.c`内の`pongo_send_command`からそのブート引数を削除してください。

### フックのアンインストール
xnuspyがこれを管理します。プロセスが終了すると、そのプロセスによってインストールされたすべてのカーネルフックは約1秒以内にアンインストールされます。

### フック可能なカーネル関数
ほとんどの関数フックフレームワークには、特定の関数をフック可能にするための最小長があります。xnuspyには、元の関数を呼び出す予定があり、*かつ*フックされた関数の最初の命令が`B`でない場合に*のみ*この制限があります。この場合、最小長は8バイトです。それ以外の場合、最小長はありません。

xnuspyはトランポリンに`X16`と`X17`を使用するため、関数呼び出し間でそれらのレジスタが保持されることを期待するカーネル関数はフックできません(これを期待する関数は多くありません)。フックしたい関数が`BL`で始まり、元の関数を呼び出すつもりの場合、元の関数を実行しても`X17`が変更されない場合にのみ呼び出すことができます。

### スレッドセーフ
`xnuspy_ctl`は、新しく起動した後に初めて呼び出されたときに1回限りの初期化を実行します。使用する読み取り/書き込みロックを静的に初期化できないため、これがxnuspyの中で競合が発生する可能性がある唯一の部分です。最初の呼び出しが戻った後、それ以降の呼び出しはスレッドセーフであることが保証されます。

# 動作の仕組み
これは簡略化されていますが、主要なアイデアをうまく捉えています。xnuspyの関数フックは、書き込み可能で実行可能なカーネルメモリ上に存在する構造体です。ほとんどの場合、これはpongoOS内の`alloc_static`によって返されるメモリです。次のように簡略化できます:

struct { uint64_t replacement; uint32_t tramp[2]; uint32_t orig[10]; };

root@kitploit:~
ここで、`replacement` は(後述する)カーネル仮想アドレス、`tramp` は実行を `replacement` にリダイレクトする小さなトランポリン、`orig` は元の関数を表すより大きく複雑なトランポリンです。

xnuspy が最初に行うことの一つは、EL0 の置換関数が呼び出し元プロセスのアドレス空間内のどこにあるかを特定することです。これは、動的ライブラリからカーネル関数をフックできるようにするためです。その置換関数のアドレスに対応する Mach-O ヘッダが保存されます。

その後、そのヘッダの `__TEXT` セグメントと `__DATA` セグメント(およびそれらの間にあるセグメントがあればそれも)の共有ユーザー・カーネルマッピングが作成されます。`__TEXT` は、フックから他の関数を呼び出せるように共有されます。`__DATA` は、グローバル変数への変更が EL1 と EL0 の両方で認識されるように共有されます。

このマッピングは `__TEXT` と `__DATA` の一対一のコピーであるため、その上にあるユーザーの置換関数のアドレスを簡単に特定できます。呼び出し元プロセスの Mach-O ヘッダのアドレス `u`、共有マッピングの先頭アドレス `k`、ユーザーの置換関数のアドレス `r` が与えられた場合、次の式を適用します: `replacement = k + (r - u)`

その後、`replacement` は共有マッピング上のユーザーの置換関数のカーネル仮想アドレスとなり、関数フック構造体に書き込まれます。xnuspy は、置換関数の EL0 アドレスに実行をリダイレクトしません。これは極めて安全でないためです。スケジューラに依存するだけでなく、カーネルスレッドが置換関数上で実行中にカーネルフックを持つプロセスが終了するシナリオを制御できなくなります。

最後に、共有マッピングは実行可能としてマークされ、無条件即時分岐 (`B`) がアセンブルされます。これは `tramp` の先頭への実行を指示し、フックされたカーネル関数の最初の命令を置き換えます。残念ながら、これにより、特定のカーネル関数から 128 MB 以上離れたフック構造体への分岐に制限が生じます。xnuspy は起動前にこのシナリオをチェックし、発生する可能性がある場合には、代わりにフック構造体が常駐するためにカーネルキャッシュ内の未使用コードにフォールバックします。

# その他の注意事項
パッチファインダーが動作するように最善を尽くしていますので、何か動作しない場合は issue を開いてください。
ツールをダウンロード