
example/open1_hook.cをコンパイルして実行した後のカーネルログからの出力
Xnuspyは、新しいシステムコールxnuspy_ctlをインストールするPongoosモジュールで、ユーザースペースからカーネル関数をフックできるようにします。 checkra1n 0.12.2以上でiOS 13.x、iOS 14.x、およびiOS 15.xをサポートします。 4Kデバイスはサポートされていません。
このモジュールは、KTRR/KPPを完全に去勢し、EL1内にRWXメモリを作成することを可能にします。毎日のドライバーにこれを使用しないでください。
libusbが必要: brew install libusb
トップレベルのディレクトリでmakeます。ローダーとモジュールを構築します。
make前にこれらを追加します。
XNUSPY_DEBUG=1kprintf )にデバッグ出力を送信します。XNUSPY_SERIAL=1IOLogにデバッグ出力を送信します。XNUSPY_LEAKED_PAGE_LIMIT=n64です。詳細については、カーネルパニックのデバッグの下にあります。XNUSPY_TRAMP_PAGES=n XNUSPY_DEBUGとXNUSPY_SERIAL互いに依存しません。
すべてを作成したら、checkra1nをポンゴシェルにブートしてください: /Applications/checkra1n.app/Contents/MacOS/checkra1n -p
同じディレクトリで、ローダーとモジュールを構築しました。 loader/loader module/xnuspyを実行します。その後、Xnuspyはそのことをし、数秒でデバイスが起動します。 loader 、SEPROMを悪用する必要がある場合に備えてxnuspy-getkernelvを発行してからさらに数秒待ちます。
Checkra1nのKPFが実行された後、私の携帯電話のいくつかが「ブート」で立ち往生することがあります。何がこれを引き起こすのかはまだわかりませんが、それが起こった場合は、もう一度やり直してください。また、 bootxの後にデバイスがぶら下がっている場合は、再試行してください。最後に、iOS 13.3.1を実行しているiPhone Xで実行可能なxnuspy_ctlコードをマークするのは少しむらがありますが、他の携帯電話で100%成功します。フックプログラムを実行したときにカーネル命令フェッチフェッチをパニックに陥れた場合は、再試行してください。
Xnuspyは、 xnuspy_ctl_trampを指すためにenosysシステムコールにパッチを当てます。これは、コンパイルされたxnuspy_ctlコードを実行可能ファイルとしてマークする小さなトランポリンです。 module/el1/xnuspy_ctl/xnuspy_ctl.cでxnuspy_ctlの実装と、 exampleの例で例を見つけることができます。
内部include/xnuspy/ is 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);
このシステム呼び出しは、 flavor 、 arg1 、 arg2 、およびarg3 4つの引数が必要です。フレーバーは、 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の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そのユーザースペースバッファのサイズです。 arg3バイトは、 arg1からarg2に記述されます。
XNUSPY_KWRITEこのフレーバーは、TFP0なしでユーザースペースからカーネルメモリに簡単に書き込む方法を提供します。
arg1はカーネル仮想アドレス、 arg2ユーザースペースバッファのアドレス、 arg3そのユーザースペースバッファのサイズです。 arg3バイトは、 arg2からarg1に記述されます。
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 if:unified_kalloc NULLを返しました。ENOSPC if:xnuspy_tramp構造体はありません。何百ものカーネル関数を同時にフックしていない限り、これは起こりません。より多くの機能フックが必要な場合は、制限をチェックしてください。ENOTSUP場合:ENOENT場合:mh_for_addr 、発信者のアドレス空間内のarg2に対応するMACH-Oヘッダーを決定することができませんでした。EFAULTの場合:EIO if: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場合:このフレーバーがエラーを返した場合、デスコールバックは登録されていません。
XNUSPY_CALL_HOOKMEに関連するエラーerrnoは...
ENOTSUP場合:hookme 、 xnuspy_tramp構造を含むメモリから遠すぎます。これはポングー内で決定され、Xnuspyがkernelcacheの内側にすでに使用されていないコードにフォールバックする必要がある場合にのみ発生します。この場合、 hookmeを呼び出すことはほぼ間違いなくカーネルパニックを引き起こし、フックするために別のカーネル関数を把握する必要があります。このフレーバーがエラーを返した場合、 hookme呼び出されませんでした。
XNUSPY_CACHE_READに関連するエラーerrnoは...
EINVAL if: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を呼び出すとパニックになります。その関数がXNUSPY_CACHE_READを介してまだ利用できない場合、呼び出すlibc関数を再実装する必要があります。ただし、他のカーネル関数への関数ポインターを作成して、それらを呼び出すことができます。PAGE_SIZE定数ではなくvm_page_sizeに展開します。この変数を読む前に、PANを無効にする必要があります(A10+も、私も行うことをお勧めしません)。パニックになります。-fno-stack-protectorと-D_FORTIFY_SOURCE=0でコードをコンパイルするようにしてください。場合によっては、デバイスは別のユーザースペースポインターを参照して___stack_chk_guardを読み取る必要があります。スキミング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 、モジュールを再度ロードします。モジュールを読み込んだ後、まだお届けしていない場合は、 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は、 xnuspy_tramp構造体のXNUブーツの前に静的カーネルメモリの1ページを予約し、225カーネル関数を同時にフックすることができます。もっと必要な場合は、 make前にXNUSPY_TRAMP_PAGES=n追加できます。これにより、 xnuspy_tramp構造の静的メモリのnページを予約するようにxnuspyに指示されます。ただし、Xnuspyがkernelcache内の未使用のコードに戻る必要がある場合、これは無視されます。これが起こるとき、それがどのように機能するかについて詳しく説明されています。
何らかの理由で、 os_log_with_argsのログは、コマンドラインツールoslogから出力されたストリームに表示されません。 kprintfからのログもそこにはなりませんが、 dmesgで見ることができます。ただし、 dmesgライブフィードではないため、 kprintfログをリアルタイムで表示するツールであるklog書きました。 klog/で見つけてください。 kprintfメッセージにdmesgをスパムする代わりに、それを使用することを強くお勧めします。
open: Resource busy klogを実行した後に忙しい場合は、このコマンドlaunchctl unload /System/Library/LaunchDaemons/com.apple.syslogd.plistを実行して、再試行してください。
残念ながら、 atm_diagnostic_config=0x20000000 XNUのBootargsに設定されている場合、 NSLogのnslogを見ることができません。 klog 、このブート引数が存在することに依存しています。 NSLogを取り戻したい場合は、 loader.c内のpongo_send_commandからそのブート引数を削除します。
Xnuspyはあなたのためにこれを管理します。プロセスが終了すると、そのプロセスによってインストールされたすべてのカーネルフックは、1秒ほど以内にアンインストールされます。
ほとんどの関数フックフレームワークには、特定の関数をフック可能にする最小の長さがあります。 Xnuspyには、元の関数を呼び出すことを計画している場合にのみこの制限があり、フック機能の最初の命令はBはありません。この場合、最小長は8バイトです。それ以外の場合、最小長はありません。
XnuspyはトランポリンにX16とX17使用するため、関数呼び出し全体で持続すると予想されるカーネル関数はフックできません(これを期待する人は多くありません)。フックしたい関数がBLで始まり、元の関数を呼び出すつもりの場合、元の関数を実行してもX17変更しない場合にのみ行うことができます。
xnuspy_ctl 、新鮮なブーツの後に呼び出されたときに1回限りの初期化を実行します。これは、使用している読み取り/書き込みロックを静的に初期化できないため、レース可能なXnuspyの唯一の部分です。最初のコールが戻った後、将来の呼び出しはスレッドセーフになるためにguarenteedです。
これは単純化されていますが、主なアイデアをよく捉えています。 Xnuspyの関数フックは、書き込み可能な実行可能なカーネルメモリにある構造です。ほとんどの場合、これはポングー内のalloc_staticによって返されるメモリです。これに煮詰めることができます:
struct {
uint64_t replacement;
uint32_t tramp[2];
uint32_t orig[10];
};
replacement交換関数のカーネル仮想アドレス(後の詳細)である場合、 tramp実行をreplacementために再ダイレクトする小さなトランポリンであり、 orig元の関数を表すより大きく、より複雑なトランポリンです。
Xnuspyが最初に行うことの1つは、呼び出しプロセスのアドレス空間内のEL0置換がどこに存在するかを決定することです。これは、カーネル関数を動的ライブラリからフックできるように行われます。その交換のアドレスに対応するMACH-Oヘッダーが保存されます。
その後、そのヘッダーの__TEXTおよび__DATAセグメントの共有ユーザーカーネルマッピング(およびそれらの間の任意のセグメントが作成されます)が作成されます。 __TEXTが共有されているため、フックから他の機能を呼び出すことができます。 __DATAが共有されるため、グローバル変数の変更はEL1とEL0の両方で見られます。
このマッピングは__TEXTと__DATAの1対1のコピーであるため、ユーザーの交換機能のアドレスを簡単に把握するのは簡単です。呼び出しプロセスのMACH -Oヘッダーuのアドレス、共有マッピングkの開始のアドレス、およびユーザーの交換機能rのアドレスを考えると、次の式を適用しますreplacement = k + (r - u)
その後、 replacement共有マッピング上のユーザーの交換機能のカーネル仮想アドレスであり、関数フック構造に書き込まれます。 Xnuspyは、交換関数のEL0アドレスに実行を再ダイレクトすることはありません。これは非常に安全ではないため、スケジューラの慈悲に陥るだけでなく、カーネルフックを使用したプロセスが死亡し、カーネルスレッドがまだ交換用に実行されているシナリオを制御できません。
最後に、共有マッピングは実行可能ファイルとしてマークされ、無条件の即時ブランチ( B )が組み立てられます。実行をtrampの開始に向け、現在にフックされているカーネル関数の最初の命令に取って代わるものです。残念ながら、これにより、特定のカーネル関数から128 MB以上離れた分岐構造からフック構造に制限されます。 Xnuspyは、起動する前にこのシナリオをチェックし、フック構造が発生する可能性があることがわかった場合、フック構造の代わりに存在するためにkernelcacheに既に使用されていないコードに戻ります。
パッチファインダーが機能することを確認するために最善を尽くしているので、何かが機能していない場合は、問題を開いてください。