FLARE スクリプト シリーズ:エミュレーションによる Objective-C コード分析の自動化

news

 

このブログ投稿は、FireEye Labs Advanced Reverse Engineering (FLARE) チーム スクリプト シリーズの次のエピソードです。本日、 IDA Proと、x86、x86_64、ARM、および ARM64 アーキテクチャ用のスクリプト可能なエミュレーション機能をリバース エンジニアに提供するUnicorn エミュレーション フレームワークを搭載した、新しい IDAPython ライブラリ、 flare-emuを共有します。このライブラリとともに、それを使用する Objective-C コード分析 IDAPython スクリプトも共有しています。エミュレーションがコード分析の問題を解決するのに役立ついくつかの創造的な方法と、プロセスで多くの時間を節約するために新しい IDAPython ライブラリを使用する方法を学びましょう。

なぜエミュレーション?

コード分析の問題を解決する手段としてエミュレーションを使用していない場合は、見逃していることになります。その利点といくつかのユースケースを紹介して、それがどれほど強力であるかを理解してもらいます。エミュレーションは柔軟で、Unicorn を含む現在利用可能な多くのエミュレーション フレームワークはクロスプラットフォームです。エミュレーションでは、エミュレートするコードを選択し、それが実行されるコンテキストを制御します。エミュレートされたコードは、それが実行されているオペレーティング システムのシステム サービスにアクセスできないため、損傷を引き起こすリスクはほとんどありません。これらすべての利点により、エミュレーションはアドホックな実験、問題解決、または自動化のための優れたオプションになります。

ユースケース

  • 復号化/復号化/難読化解除/圧縮解除 – 悪意のあるコードの分析中に、文字列、構成データ、または別のペイロードなどの有用なデータを復号化、圧縮解除、復号化、または難読化解除するために使用される関数に遭遇することがよくあります。一般的なアルゴリズムであれば、目で見たり、 signsrchなどのプラグインで識別できる場合があります。残念ながら、これはあまり当てはまりません。次に、デバッガーを開いてサンプルをインストルメント化してデコードするか、関数を手動でその時点のニーズに合ったプログラミング言語に置き換えます。これらのオプションは、コードの複雑さと分析しているサンプルによっては、時間がかかり、問題が発生する可能性があります。ここでは、エミュレーションが望ましい 3 番目のオプションを提供することがよくあります。関数をエミュレートするスクリプトを作成することは、関数を作成したか、ライブラリから呼び出しているかのように、関数を使用できるようにすることに似ています。これにより、デバッガーを開かなくても、さまざまな入力を使用して、必要な回数だけ関数を再利用できます。このケースは、コード自体を復号化できる自己復号化シェルコードにも適用されます。
  • データ追跡– エミュレーションを使用すると、命令フックを使用していつでもエミュレーション コンテキストを停止して調べることができます。逆アセンブラとエミュレータを組み合わせると、重要な命令でエミュレーションを一時停止し、レジスタとメモリの内容を検査できます。これにより、興味深いデータが関数を通過する際にそのデータを監視できます。これにはいくつかのアプリケーションがあります。 FLARE スクリプト シリーズの他のブログで以前に取り上げたように、 Automating Function Argument ExtractionおよびAutomating Obfuscated String Decodingのように、この手法を使用して、プログラム全体で特定の関数に渡される引数を追跡できます。関数引数の追跡は、この記事の後半で紹介する Objective-C コード分析ツールで採用されている手法の 1 つです。データ追跡技術は、オブジェクト メンバー参照をマークアップするために C++ コードのthisポインターを追跡するためにも使用できます。または、格納されている変数の名前を適切に変更するためにGetProcAddress/dlsymへの呼び出しからの戻り値を追跡することもできます。多くの可能性があります。

フレアエミューのご紹介

FLARE チームは、IDA Pro のバイナリ解析機能と Unicorn のエミュレーション フレームワークを組み合わせて、エミュレーション タスクをスクリプト化するための使いやすく柔軟なインターフェイスをユーザーに提供する IDAPython ライブラリ、 flare-emuを導入しています。 Flare-emuは、サポートされているアーキテクチャ用に柔軟で堅牢なエミュレーターをセットアップするためのすべてのハウスキーピングを処理するように設計されているため、コード分析の問題を解決することに集中できます。現在、エミュレーションのニーズを満たす 3 つの異なるインターフェイスと、関連する多数のヘルパーおよびユーティリティ関数が提供されています。

  1. エミュレート範囲– この API は、ユーザー指定のコンテキスト内で一連の命令または関数をエミュレートするために使用されます。個々の命令と「呼び出し」命令が検出されたときの両方に対して、ユーザー定義のフックのオプションを提供します。ユーザーは、エミュレーターがスキップするか、関数呼び出しを呼び出すかを決定できます。図 1 に示すエミュレート範囲の戻り値を追跡するために、命令と呼び出しフックの両方で使用されます。GetProcAddress呼び出し、グローバル変数の名前を、それらが指す Windows API の名前に変更します。この例では、エミュレートするようにのみ設定されていました。0x4015140x40153D.このインターフェイスは、ユーザーが特定のレジスタとスタック引数の値を簡単に指定できる方法を提供します。バイト文字列が指定されている場合、それはエミュレータのメモリに書き込まれ、ポインタはレジスタまたはスタック変数に書き込まれます。エミュレーション後、ユーザーはflare-emuのユーティリティ関数を使用して、エミュレートされたメモリまたはレジスタからデータを読み取るか、返された Unicorn エミュレーション オブジェクトを使用して直接プローブすることができます。flare-emu必要な機能を公開していません。

    の小さなラッパー関数エミュレート範囲、名前付きエミュレート選択、IDA Pro で現在強調表示されている命令の範囲をエミュレートするために使用できます。
     

    GetProcAddress の戻り値を追跡するために使用されている emulateRange
    GetProcAddress の戻り値を追跡するために使用されている emulateRange

     

  2. iterate – この API は、特定のターゲットに到達するために、関数内の特定のブランチでエミュレーションを強制するために使用されます。ユーザーは、ターゲット アドレスのリスト、またはターゲットに到達したときのコールバックと共に、関数への相互参照のリストがターゲットとして使用される関数のアドレスを指定できます。エミュレーション中に別の分岐が発生した可能性がある条件に関係なく、ターゲットに到達します。図 2 は、反復がターゲットに到達するために強制的に取られた一連のコード分岐を示しています。 cmp命令によって設定されたフラグは無関係です。 emulateRange API と同様に、個々の命令と「call」命令が検出されたときの両方のユーザー定義フックのオプションが提供されます。反復API の使用例は、この投稿で前述した関数引数追跡手法です。
    ターゲット アドレスに到達するために反復 API によって決定されるエミュレーションのパス
    ターゲット アドレスに到達するために反復 API によって決定されるエミュレーションのパス

  3. emulateBytes – この API は、余分なシェルコードの塊を単純にエミュレートする方法を提供します。提供されたバイトは IDB に追加されず、そのままエミュレートされます。これは、エミュレーション環境の準備に役立ちます。たとえば、 flare-emu自体はこの API を使用して、Unicorn によって公開されていない ARM64 CPU のモデル固有レジスタ (MSR) を操作し、ベクトル浮動小数点 (VFP) 命令とレジスタ アクセスを有効にします。図 3 は、これを実現するコード スニペットを示しています。 emulateRangeと同様に、 flare-emuがユーザーが必要とする機能を公開していない場合に備えて、Unicorn エミュレーション オブジェクトが返されます。
    emulateBytes を使用して ARM64 の VFP を有効にするflare-emu
    emulateBytes を使用して ARM64 の VFP を有効にするflare-emu

API フック

前述のように、 flare-emuは、エミュレーションを使用してコード分析のニーズを簡単に解決できるように設計されています。エミュレーションの苦労の 1 つは、ライブラリ関数への呼び出しを処理することです。 Flare-emuには、呼び出し命令を単純にスキップするオプション、または呼び出しフック ルーチン内の特定の関数を処理するための独自のフックを定義するオプションが用意されていますが、80 を超える関数用の定義済みフックも付属しています!これらの関数には、遭遇する文字列およびメモリ操作のための一般的な C ランタイム関数の多くと、対応する Windows API の一部が含まれています。

図 4 は、タイムスタンプ値を取得して文字列に変換する関数を呼び出すコード ブロックをいくつか示しています。図 5 は、 flare-emuiterate API を使用して、この関数が呼び出された場所ごとにこの関数に渡された引数を出力する単純なスクリプトを示しています。このスクリプトは、単純な XOR デコード関数もエミュレートし、結果のデコードされた文字列を出力します。図 6 は、スクリプトの出力結果を示しています。

タイムスタンプ変換関数の呼び出し
タイムスタンプ変換関数の呼び出し
フレアエミュの簡単な使用例
フレアエミュの簡単な使用例
図 5 に示すスクリプトの出力
図 5 に示すスクリプトの出力

以下は、 flare-emuを使用してGetProcAddressの戻り値を追跡し、それに応じて格納されている変数の名前を変更するサンプル スクリプトです。その他の例については、 READMEを確認し、 flare-emu のヘルプを参照してください。

objc2_analyzer の紹介

昨年、macOS 用の Cocoa アプリケーションのリバース エンジニアリングを紹介するブログ記事を書きました。その投稿には、Objective-C メソッドが内部でどのように呼び出されるか、およびこれが IDA Pro や他の逆アセンブラーの相互参照にどのように悪影響を与えるかについての短い入門書が含まれていました。これらの相互参照の問題を解決するために、objc2_xrefs_helper という名前のIDAPythonスクリプトもこの投稿で紹介されました。このブログ記事をまだ読んでいない場合は、 objc2_analyzerが特に役立つ理由についてのコンテキストが提供されているため、この記事を読み進める前に読むことをお勧めします。 objc2_xrefs_helperの主な欠点は、セレクター名があいまいな場合、つまり、2 つ以上のクラスが同じ名前のメソッドを実装している場合、スクリプトは、参照されたセレクターがバイナリ内の特定の場所に属しているクラスを判別できず、相互参照を修正するときにそのようなケースを無視します。

現在、エミュレーションのサポートにより、これは当てはまりません。 objc2_analyzerは、バイナリ内のobjc_msgSendバリアントへのすべての呼び出しに対して渡されるIDセレクターを決定するために、Objective-C 逆アセンブリ分析を実行する命令および呼び出しフックと共に、 flare-emuの反復 API を使用します。追加のボーナスとして、関数ポインターがレジスターに格納されている場合にobjc_msgSendバリアントへの呼び出しをキャッチすることもできます。これは、Clang (最新バージョンの Xcode で使用されるコンパイラー) で非常に一般的なパターンです。 IDA Pro は、これら自体を捕捉しようとし、非常にうまく機能しますが、すべてを捕捉するわけではありません。 x86_64 に加えて、iOS アプリケーションのリバース エンジニアリングをサポートするために、ARM および ARM64 アーキテクチャのサポートも追加されました。このスクリプトは、リポジトリから削除された古いobjc2_xrefs_helperスクリプトに取って代わります。また、スクリプトはエミュレーションを使用して Objective-C コードでこのようなデータ追跡を実行できるため、 idがクラス インスタンスなのかクラス オブジェクト自体なのかを判断することもできます。 IDとして渡されるivarも追跡するためのサポートが追加されました。このすべての情報を使用して、各場所で行われるメソッド呼び出しを表すobjc_msgSendバリアントへの各呼び出しに、Objective-C スタイルの疑似コード コメントが追加されます。スクリプトの機能の例を図 7 と図 8 に示します。

objc2_analyzer を実行する前の Objective-C IDB スニペット
objc2_analyzer を実行する前の Objective-C IDB スニペット
objc2_analyzer 実行後の Objective-C IDB スニペット
objc2_analyzer 実行後の Objective-C IDB スニペット

簡単に移行できるように、代わりに実装関数自体を参照するように、セレクターを参照する命令にパッチが適用されていることを確認してください。各呼び出しに追加されたコメントにより、分析がはるかに簡単になります。図 9 に示すように、実装関数からの相互参照も作成され、それらを参照するobjc_msgSend呼び出しを指し示します。

実装機能の IDB に追加された相互参照
実装機能の IDB に追加された相互参照

7.0 以降の IDA Pro のすべてのリリースで、Objective-C コードの分析と処理が改善されていることに注意してください。ただし、執筆時点では、IDA Pro の最新バージョンは 7.2 であり、このツールを使用して緩和される欠点と、非常に役立つコメントが追加されています。 objc2_analyzerは、他の IDA Pro プラグインおよびスクリプトとともに、GitHub ページで入手できます。

結論

Flare-emuは、さまざまなコード分析の問題に適用できる武器庫に組み込むための柔軟なツールです。このブログ投稿では、いくつかの問題の例を提示し、それを使用して解決しましたが、これは考えられるアプリケーションのほんの一部です。コード分析の問題を解決するためにエミュレーションを試したことがない場合は、エミュレーションを選択肢の 1 つとして検討していただければ幸いです。そして、皆さんがこれらの新しいツールを使用することに価値を見出せることを願っています!

参照: https://www.mandiant.com/resources/blog/automating-objective-c-code-analysis-with-emulation

Comments

Copied title and URL