
iOSカーネルキャッシュを解析するためのIDA Toolkit
このフォークはIDA 9.x / Python 3 / iOS 27.0 beta 1で動作するように更新されています。
ida_kernelcacheは、iOSカーネルキャッシュの操作を容易にするためのIDAPythonモジュール(IDA Pro用)です。 このモジュールは以下の機能を提供します:
__PRELINK_INFOセグメントを解析してPython辞書に変換します。__gotセクションのオフセットと__stubsセクションのスタブ関数にシンボルを付けます。メインの処理関数は、手動解析やリバースエンジニアリングの前に実行するように設計されています。デフォルト設定では、IDAはカーネルキャッシュ内の多くの有益な情報を見落としがちです。これらのスクリプトは、カーネルキャッシュの既知の構造を活用して有用な情報を自動的に伝播することでIDAを支援します。
モジュールの標準機能に加えて、ida_kernelcacheにはiOSカーネルキャッシュの解析を容易にするいくつかのスクリプトが含まれています。たとえば、関数で使用されるC構造体を自動生成するためにスクリプトを使用できます。
ida_kernelcacheで使用される手法の多くは、memctlのために開発され、そこから直接借用されています。
ida_kernelcacheは、iOSバージョン10.1.1、11.0、11.2、11.3.1、12.0ベータのカーネルキャッシュでIDA Pro 6.95を使用してテストされています。現在、iOS 10以降のArm64カーネルキャッシュのみをサポートしています。
すでに解凍されたカーネルキャッシュファイルがIDAにロードされている必要があります。Appleから特定のIPSWをダウンロードするためのURLはオンラインで入手でき、カーネルキャッシュを解凍できる公開ツール(memctlを含む)が多数あります。
IDAで、メニューバーから「File」→「Script file...」を選択し、メインディレクトリ内のida_kernelcache.pyスクリプトを選択します。これにより、ida_kernelcacheおよびkcという名前でida_kernelcacheモジュールがIDAPythonインタプリタにロードされます。IDAPythonプロンプトでkc.kernelcache_process()と入力し、Enterキーを押してカーネルキャッシュの解析を開始します。この関数は、ida_kernelcacheがサポートするすべての主要な解析を実行します。関数は数分間実行され、IDAが新しい関数を識別して解析します。
ida_kernelcacheは、ユーザーが設定したアドレス名を上書きしないようにします。つまり、kernelcache_processによる初期化の前にカーネルキャッシュが手動で解析されていた場合、ユーザー指定の名前が自動的な名前伝播を妨げるため、結果が十分に詳細でない可能性があります。ただし、ida_kernelcacheが以前の解析を台無しにしないという保証もないため、すでに解析済みのカーネルキャッシュファイルでkernelcache_processを実行する場合は、最初にバックアップを作成してください。
ida_kernelcacheはida_kernelcache.pyを介してロードされることを意図しています。ida_kernelcacheディレクトリ内のサブモジュールは直接ロードされることを想定していません。ただし、ida_kernelcacheはこれらのサブモジュールの機能の多くを公開しています。以下に各サブモジュールの説明を示します:
ida_utilities:
このモジュールは、IDAの一部の関数をラップして、より使いやすいAPIを提供します。特に便利なのは、is_mapped、read_word、read_struct、force_function、ReadWordsです。is_mappedはアドレスがマップされているかどうかをチェックし、オプションで既知の値を含むかどうかを確認します。read_wordはアドレスから可変サイズのワードを読み取ります。read_structは構造体型をPython辞書またはPythonアクセサオブジェクトに読み取り、データ構造の解析を容易にします。force_functionは、アドレスをIDAの関数の開始点に変換するいくつかのトリックを試みます。ReadWordsは、範囲内のデータワードとそのアドレスを反復処理するジェネレータです。
build_struct: この内部モジュールには、構造体への一連のアクセスに基づいてIDA構造体を自動的に設定するユーティリティが含まれています。
class_struct:
このモジュールは、C++仮想メソッドテーブルとクラスを表すIDA構造体を生成する関数を提供します。initialize_vtable_structsは(シンボル化された)仮想メソッドテーブルをスキャンし、仮想メソッドポインタを保持するIDA構造体を作成します。initialize_class_structsは、仮想メソッドに対するデータフロー解析を実行して各クラスのフィールドへのアクセスを識別し、クラスを表すIDA構造体を構築します。フィールドを参照していると思われる命令も、構造体オフセット参照に変換されます。詳細については、モジュールのdocstringを参照してください。
ida_kernelcache_reload.pyスクリプトは、ida_kernelcache.pyと同一ですが、ida_kernelcacheモジュールとすべてのサブモジュールを強制的にリロードします。主に開発に役立ちます。
scriptsディレクトリには、ida_kernelcacheを使用して何らかの解析を実行するスクリプトが含まれています。これらのスクリプトはメインのida_kernelcacheモジュールの一部にするには特化しすぎていますが、カーネルキャッシュのリバースエンジニアリングに役立ちます。内容は以下のとおりです:
find_virtual_method_overrides.py: 指定された文字列を含む仮想メソッドをオーバーライドするクラスの子孫を見つけるスクリプト。一致するオーバーライドがコンソールに出力されます。
populate_struct.py: 現在のアドレスからデータフロー解析を実行して、C++クラスまたはC構造体のフィールドを設定します。
process_external_methods.py:
IOExternalMethodまたはIOExternalMethodDispatch配列を、ファジングツールで使用するための標準形式に処理します。
Hex-Rays逆コンパイラを使用している場合、ida_kernelcacheのより興味深い機能の1つは、自動C++クラス再構築です。これは、OSMetaClass情報とデータフロー解析を使用して、カーネルキャッシュ内で見つかったクラスを表すIDA構造体を作成します。これらの表現により、疑似コード表現の可読性が大幅に向上します。詳細については、投稿Reconstructing C++ classes in the iOS kernelcache using IDA Proを参照してください。
iOS 12では、一部のデバイスで新しいカーネルキャッシュ形式が導入されました。変更点の中には、この新しいカーネルキャッシュのカーネルポインタがタグ付けされてリストにリンクされていることがあります。これはおそらく、iBootがプリリンク辞書内の_PrelinkLinkKASLROffsetsデータなしでカーネルをスライドできるようにするためです。この形式のストックカーネルキャッシュをIDAで解析しようとすると、クロスリファレンスが欠落しているため困難です。詳細については、記事Analyzing the iOS 12 kernelcache's tagged pointersを参照してください。
追加処理を行わずにカーネルキャッシュ内のポインタのタグを解除するだけの場合は、kc.tagged_pointers.untag_pointers()を実行してください。
この機能の一部は、おそらくAppleのカーネルキャッシュよりも広く適用できる可能性があります(たとえば、vtable解析とシンボル伝播、またはida_utilities.pyのほとんどの関数)。それでも、他の種類のバイナリでこれらをテストしていないため、インポート範囲をida_kernelcacheモジュールのみに制限しています。
ida_kernelcacheはMITライセンスの下でリリースされています。
ida_kernelcacheの機能の多くはmemctlから借用されており、これもMITライセンスの下でリリースされています。その他のソースは、対応するファイルのコメントに記載されています。
Brandon Azad
classes:
このモジュールは、カーネルキャッシュ内のC++クラスに関する情報を保持するClassInfo型を定義し、カーネルキャッシュをスキャンしてクラスを収集し、グローバルなclass_info辞書にクラス名からClassInfoオブジェクトへのマップを設定する関数collect_class_infoを提供します。ClassInfo型は、各C++クラスのクラス名、OSMetaClassインスタンス、仮想メソッドテーブル、スーパークラス名を記録します。さらに、各ClassInfoオブジェクトは、スーパークラスのClassInfoとすべての直接サブクラスのClassInfoへの参照を格納するため、クラス階層の調査と走査が容易になります。collect_class_infoはまた、すべての仮想メソッドテーブルのセットをグローバルなvtablesセットに保存します。
data_flow: この内部モジュールには、ida_kernelcacheの他の部分で使用されるデータフロー操作が含まれています。
kernel:
このモジュールは、baseとprelink_infoというグローバル変数を提供します。baseはカーネルイメージのベースアドレス(カーネルのMach-Oヘッダの先頭)です。prelink_infoは解析済みの__PRELINK_INFO辞書です。
kplist:
このモジュールは、カーネル形式のplistを解析するためのkplist_parse関数を提供します。
metaclass:
このモジュールは、既知の各OSMetaClassインスタンスにシンボルを追加する関数initialize_metaclass_symbolsを提供します。
offset:
このモジュールは、関数initialize_data_offsetsとinitialize_offset_symbolsを提供します。前者は、オフセットに変換できるポインタをセグメント内でスキャンします。後者は、オフセットのターゲットにシンボルがある場合、各kextの__gotセクションのオフセットにシンボルを付けます。
segment:
このモジュールは、IDAのセグメントをより有用に名前変更する関数initialize_segmentsを提供します。デフォルトでは、IDAはバンドル識別子の推測と領域を説明するMach-Oセクションを組み合わせてセグメント名を作成しているように見えます。initialize_segmentsは__PRELINK_INFO辞書から実際のバンドル識別子を抽出し、各セグメントをバンドル識別子、Mach-Oセグメント、Mach-Oセクションを含むように名前変更します。これにより、例えば__TEXT.__constと__DATA_CONST.__constを区別できるようになります。このモジュールはまた、(古いiOS 11の分割kextカーネルキャッシュ形式でのみ)指定されたアドレスを含むkextを決定する関数kernelcache_kext(トップレベルで再エクスポート)を提供します。
stub:
カーネルキャッシュ内の多くのkextには、カーネル本体の関数にジャンプする__stubsセクションにスタブ関数が含まれています。残念ながら、これらのスタブはクロスリファレンスや型情報の伝播に対する障壁となります。このモジュールはこれらの問題を解決するわけではありませんが、ターゲット関数名が表示されるようにスタブ関数を自動的に名前変更することで、スタブの確認を少し容易にします。スタブとそのターゲットは強制的にIDAの関数に変換され、IDA内の関数が元のソースコード内の関数と一致するのに役立ちます。
tagged_pointers: 新しいiOS 12の統合カーネルキャッシュ形式では、各ポインタの上位2バイトにオフセットがタグ付けされ、ポインタをリストにチェーンします。このモジュールには、これらのタグ付きポインタを処理および復元するための関数が含まれています。
vtable:
このモジュールは、仮想メソッドテーブルを操作するための多くの便利な関数を提供します。これには、vtable_length、convert_vtable_to_offsets、vtable_overrides、initialize_vtable_symbols、initialize_vtable_method_symbolsが含まれます。vtable_lengthは、指定されたアドレスがvtableである可能性があるかどうかをチェックし、vtableの長さを返します。ジェネレータvtable_overridesは、スーパークラスによって使用される仮想メソッドをオーバーライドするクラス内の仮想メソッドを列挙します。関数initialize_vtable_symbolsは、識別された各vtableの先頭にシンボルを追加します。initialize_vtable_method_symbolsは、各vtable内のオーバーライドされたメソッドを反復処理し、スーパークラスからサブクラスにシンボルを伝播します。これは、IOKit内のほとんどの基底クラスがXNUで定義されており、比較的完全なシンボル情報を持っているために可能です。サブクラスのvtable内の各メソッドオーバーライドは、スーパークラス内のメソッドと同じインターフェースに準拠する必要があるため、スーパークラスの仮想メソッドシンボル内のスーパークラス名をサブクラス名に置き換えることで、オーバーライドのシンボルを生成できます。たとえば、AppleKeyStoreクラスのインデックス7の仮想メソッドに名前がない場合、そのスーパークラスIOServiceのインデックス7の仮想メソッドが__ZNK9IOService12getMetaClassEvと呼ばれていることがわかれば、インデックス7はサブクラスでは__ZNK13AppleKeyStore12getMetaClassEvと呼ばれるべきだと推測できます。この手法は、ほとんどのクラスのほとんどの仮想メソッドにシンボルを付けるために使用できます。