Cocoa アプリケーションのリバース エンジニアリングの概要

Directory structure of iTerm application bundle news

Windows マルウェアほど一般的ではありませんが、現在は macOS としてブランド変更された OS X オペレーティング システム上で動作するマルウェアが、長年にわたって絶え間なく発見されてきました。 2 月には、macOS マルウェアに関する 3 つの特に興味深い出版物が発表されました。キーチェーン データなどのシステム情報を攻撃者に送り返すトロイの木馬 Cocoa アプリケーション、APT28 の Xagent マルウェアの macOS バージョン新しいトロイの木馬ランサムウェアです。

このブログでは、FLARE チームは、macOS 用の Cocoa アプリケーションをリバース エンジニアリングする作業に役立つ 2 つの小さなツールを紹介したいと考えています。これらのツールを適切に紹介するために、最初に少し基礎を築き、Apple 固有のトピックを読者に紹介します。具体的には、Objective-C ランタイムが IDA Pro などのツールでのコード分析を複雑にする方法と、分析を開始できる Cocoa アプリケーションのコードへの有用なエントリ ポイントを見つける方法について説明します。

これらのトピックに興味を持った場合、または自分の環境で macOS マルウェアを調査する準備を整えたい場合は、 Black Hat AsiaBlack Hat USAでこのトピックに関する 2 日間の短期集中コースに参加してください。年。

Cocoa アプリケーションの構造

「Cocoa アプリケーション」という用語を使用する場合、Apple が Cocoa Application Layer と呼んでいるものに属する AppKit フレームワークを使用して構築されたアプリケーションを指しています。 macOS では、アプリケーションはアプリケーション バンドルで配布されます。このディレクトリ構造は、図 1 に示すように、実行可能コードとそれに関連するリソースを含む単一のファイルとして表示されます。

Directory structure of iTerm application bundle
図 1: iTerm アプリケーション バンドルのディレクトリ構造

これらのバンドルにはさまざまなファイルを含めることができますが、すべてのバンドルには少なくとも 2 つの重要なファイル (Info.plist と MacOS フォルダーにある実行可能ファイル) が含まれている必要があります。実行可能ファイルは、Python やシェル スクリプトなど、実行権限を持つ任意のファイルにすることができますが、通常はネイティブ実行可能ファイルです。 Mach-O は、macOS および iOS のネイティブ実行可能ファイル形式です。 Info.plist ファイルには、アプリケーション バンドルが記述されており、OS が適切にロードするために必要な重要な情報が含まれています。 plist ファイルは、XML、JSON、または bplist と呼ばれる独自のバイナリ形式の 3 つの形式のいずれかになります。 macOS では、plutil という名前の便利なユーティリティを使用して、形式を変換したり、形式に関係なく plist ファイルを単純にきれいに印刷したりできます。 Info.plist ファイルで最も注目すべきキーは CFBundleExecutable キーです。これは、実行される MacOS フォルダー内の実行可能ファイルの名前を指定します。図 2 は、iTerm アプリケーションの Info.plist ファイルの plutil からの整形済み出力のスニペットを示しています。

iTerm アプリケーションの Info.plist ファイルのスニペット
図 2: iTerm アプリケーションの Info.plist ファイルの抜粋
Objective-C

Cocoa アプリケーションは通常、Objective-C または Swift で作成されます。 2 つの言語のうち新しいほうの Swift は、Objective-C の人気に急速に追いつき、追い越したようです。それにもかかわらず、Objective-C は Swift よりも長い年月を費やしてきました。つまり、遭遇する悪意のある Cocoa アプリケーションの大部分は当面の間、Objective-C で作成されることになります。さらに、古い Objective-C API は、マルウェアの分析中に検出される傾向があります。これは、マルウェアの古さ、または下位互換性のためである可能性があります。 Objective-C は、動的でリフレクティブなプログラミング言語およびランタイムです。約 10 年前、Objective-C バージョン 2.0 がリリースされ、言語とランタイムの両方に大きな変更が加えられました。詳細については、このブログではバージョン 2.0 を参照しています。

Objective-C で記述されたプログラムは、コンパイル プロセスの一部として C に変換されるため、ほとんどのリバース エンジニアにとって、少なくとも多少快適な移行になります。このような移行の最大のハードルの 1 つは、Objective-C でのメソッドの呼び出し方法にあります。 Objective-C のメソッドは、概念的には C 関数に似ています。これらは、特定のタスクを実行するコードの単位であり、オプションでパラメーターを取り込んで値を返します。ただし、Objective-C の動的な性質により、通常、メソッドは直接呼び出されません。代わりに、メッセージがターゲット オブジェクトに送信されます。メソッドの名前はセレクターと呼ばれ、実行される実際の関数は実装と呼ばれます。このメッセージは、メソッド パラメータとともに呼び出されるセレクタへの参照を指定します。これにより、アプリケーションが特定のセレクターの実装を変更できる「メソッド スウィズリング」などの機能が可能になります。 Objective-C アプリケーション内でメッセージを送信する最も一般的な方法は、objc_msgSend 関数です。図 3 は、ブラウザーで URL を開く目的の C コードの小さなスニペットを示しています。図 4 は、これと同じコードを C で表したものです。

Objective-C コード スニペット
図 3: Objective-C のコード スニペット
Cで表現されたObjective-Cコード
図 4: C で表現された Objective-C コード

ご覧のとおり、括弧内の Objective-C コードは、objc_msgSend の呼び出しに相当します。

残念ながら、このメッセージ送信メカニズムは、IDA Pro でセレクターの相互参照に従おうとすると問題を引き起こします。特定のセレクターが参照されている任意の場所から、そのセレクターのすべての相互参照を簡単に確認できますが、実装自体は直接呼び出されたり参照されたりしないため、セレクター参照からその実装またはその逆にジャンプする簡単な方法はありません。逆。図 5 は、実装への唯一の相互参照が実行可能ファイルの __objc_const セクションにあり、ランタイムがクラス メンバー データを格納する場所であることを示すことで、この問題を説明しています。

実装への相互参照
図 5: 実装への相互参照

もちろん、これらのセレクタ参照を実装にリンクする情報は実行可能ファイルに格納されており、ありがたいことに、IDA Pro はこのデータを解析してくれます。 __objc_const セクションでは、IDA Pro によって __objc2_meth として識別される構造体の定義が図 6 に示されています。

__objc2_meth 構造体
図 6: __objc2_meth 構造体

この構造体の最初のフィールドは、メソッドのセレクターです。このフィールドへの相互参照の 1 つは、実行可能ファイルの __objc_selrefs セクションに移動し、そこでセレクター参照を見つけることができます。セレクタ参照の相互参照をたどると、セレクタが使用されているコード内の場所が明らかになります。構造体の 3 番目のフィールドは、分析したい関数であるセレクターの実装を指します。あとは、このデータを使用して相互参照を作成するだけです。今回紹介する 2 つのツールのうち最初のツールは、objc2_xrefs_helper.py という名前の IDAPython スクリプトで、Objective-C 2.0 を使用して x86_64 Mach-O 実行可能ファイルを処理します。このスクリプトは、Zynamics によってリリースされた古い IDAPython スクリプトに似ていますが、それらのスクリプトは x86_64 アーキテクチャをサポートしていません。私たちのスクリプトは、IDA Pro 用の他のすべてのスクリプトおよびプラグインとともに、こちらの Github リポジトリから入手できます。 objc2_xrefs_helper.py は、実行可能ファイルで定義されている各 Objective-C メソッドに対して、そのセレクターを相互参照して実装関数自体を参照する命令にパッチを適用し、参照命令から実装関数への相互参照を作成します。このスクリプトを使用すると、図 7 と図 8 に示すように、セレクターの実装からその参照へ、またはその逆に簡単に移行できます。

実装のために追加された相互参照
図 7: 実装のために追加された相互参照
参照からセレクターの実装を表示する
図 8: リファレンスからのビュー セレクターの実装

ただし、このツールには注目すべき欠点があります。複数のクラスで同じ名前のメソッドが定義されている場合、実行可能ファイルにはセレクターが 1 つだけ存在します。現時点では、ツールはこれらのあいまいなセレクターを無視します。

Cocoa アプリケーション – どこから探し始めますか?

Cocoa アプリケーション、またはアプリケーション フレームワークで構築されたアプリケーションのリバース エンジニアリングに関するもう 1 つの問題は、フレームワークのコードがどこで終わり、作成者のコードが始まるかを判断することです。 C/C++ で書かれたプログラムでは、作成者のコードは通常、プログラムのメイン関数内で始まります。この規則には多くの例外がありますが、一般的にはこれが当てはまります。 Apple の IDE である Xcode で Cocoa アプリケーション テンプレートを使用するプログラムの場合、メイン関数は、図 9 に示すように、NSApplicationMain という名前の AppKit フレームワークによってエクスポートされた関数へのテール ジャンプを実行するだけです。

Cocoa アプリケーションの主な機能
図 9: Cocoa アプリケーションの主な機能

では、実行されるアプリケーションの作成者によって記述されたコードの最初の行を見つけるには、どこを見ればよいのでしょうか?その質問に対する答えは、NSApplicationMain にあります。要約すると、NSApplicationMain は、NSApplication オブジェクトの構築、メイン ストーリーボードまたは nib ファイルのロード、およびイベント ループの開始という 3 つの重要な手順を実行します。 NSApplication オブジェクトは、実行中のアプリケーションのイベントおよび通知コーディネーターとして重要な役割を果たします。 NSApplicationMain は、アプリケーション バンドルの Info.plist ファイルの NSPrincipalClass キーでこのクラスの名前を探します。 Xcode は単にこのキーを NSApplication クラスに設定しますが、このクラスはサブクラス化または再実装され、キーが上書きされる場合があります。 NSApplication オブジェクトによって調整される注目すべき通知は、作成者が持つ可能性のあるアプリケーション固有の初期化コードを実行する適切な時間として指定された NS アプリケーションの起動完了通知です。この通知を処理するために、アプリケーションは NSApplicationDelegate プロトコルに準拠するデリゲート クラスを指定できます。 Objective-C では、プロトコルは、オブジェクト指向の用語で伝統的にインターフェイスと呼ばれる役割を果たします。初期化コードをカプセル化するためのこのプロトコルの関連メソッドは、アプリケーションのDidFinish Launchingメソッドです。デフォルトでは、Xcode によってこのデリゲート クラスが作成され、AppDelegate という名前が付けられます。アプリケーション開発者が必要に応じて変更できるように、空の applicationDidFinishLaunching メソッドも定義しています。これらすべての情報が手元にある場合、ほとんどの Cocoa アプリケーションの初期コードを探すのに最適な場所は、applicationDidFinishLaunching という名前のメソッドです (図 10 参照)。

applicationDidFinishLaunching メソッドの検索
図 10: applicationDidFinishLaunching メソッドの検索

何も役に立たない場合は、メイン関数の分析に戻ります。この情報はすべて、Xcode で Cocoa アプリケーション テンプレートを使用して作成されたアプリに固有のものであることに注意してください。 Cocoa アプリケーションは NSApplicationMain を使用する必要はありません。独自のバージョンの NSApplicationMain を実装して、独自の Cocoa アプリケーションをゼロから作成できます。

Interface Builder と Nib ファイル

NSApplicationMain の主な役割の 1 つは、メインのストーリーボードまたは nib ファイルをロードすることであると前述しました。 「Nib」は NeXTSTEP Interface Builder の略で、Xcode の一部である Interface Builder アプリケーションを指します。 Interface Builder を使用すると、開発者はグラフィカル ユーザー インターフェイスを簡単に構築でき、グラフィカル インターフェイスを使用してコード内の変数やメソッドにコントロールを配線することもできます。開発者が Interface Builder で GUI を構築すると、オブジェクトのグラフが形成されます。オブジェクト グラフは、プロジェクト フォルダー内の .xib ファイルに XML 形式で保存されます。プロジェクトがビルドされると、各オブジェクト グラフは NSKeyedArchiver クラスを使用してシリアル化され、アプリケーション バンドル内の .nib ファイルに Apple の bplist 形式で保存されます。通常は Resources フォルダーの下にあります。 Xcode は、メイン nib ファイルの名前を、キー NSMainNibFile の下のアプリケーションの Info.plist ファイルに書き込みます。アプリケーションが nib ファイルをロードすると、このオブジェクト階層がメモリにアンパックされ、さまざまな GUI ウィンドウ、メニュー、コントロール、変数、およびメソッド間のすべての接続が確立されます。この接続のリストには、アプリケーション デリゲートと NSApplication クラスの間の接続が含まれます。 Yosemite の macOS にストーリーボードが追加されました。これらにより、開発者は、ユーザーに表示されるアプリケーションのさまざまなビューをすべてレイアウトし、それらの関係を指定できます。内部的には、ストーリーボードは、nib ファイルと付随する Info.plist ファイルを含むディレクトリです。メイン ストーリーボード ディレクトリは、アプリケーションの Info.plist ファイルのキー NSMainStoryboardFile の下に指定されています。

これにより、共有したいもう 1 つのツール nib_parse.py が表示されます。これは、こちらの Github リポジトリから入手できます。 nib_parse.py はccl_bplistを使用して nib ファイルをデコードおよびデシリアライズし、その中で定義されている接続のリストを出力します。接続ごとに、接続のラベル (通常はメソッドまたは変数名) とソースおよび宛先オブジェクトのクラスを出力します。 NSKeyedArchiver によってエンコードされた各オブジェクトには、括弧で囲まれた出力に含まれる一意の数値識別子の値が割り当てられます。ボタン ラベルなど、テキスト データが関連付けられている適切な GUI 要素の場合、テキストは括弧で囲まれたスクリプト出力に含まれます。この情報を使用して、コードと GUI 要素の間の関係を判断できます。さまざまな GUI イベントを処理する関数を変更して、アプリケーションを再配線することも可能です。 nib がフラット化されていない場合、nib ファイルを含むディレクトリとして表され、代わりにその中にある keyedobjects.nib ファイルに対してこのツールを実行できることに注意してください。ストーリーボードの場合、ストーリーボード ディレクトリにあるさまざまな nib ファイルに対してこのツールを実行できます。図 11 は、最近発見された図 12 のMacDownloader 脅威の MainMenu.nib ファイルで nib_parse.py を使用した場合の出力を示しています。ツール出力の GUI テキストが、スクリーンショットの GUI テキストと一致していないことに気付くかもしれません。 .この場合、GUI 要素の多くは、図 13 に示すコードで実行時に変更されます。

MacDownloader 脅威の nib_parse.py 出力
図 11: MacDownloader 脅威の nib_parse.py 出力
MacDownloader の初期ウィンドウ
図 12: MacDownloader の初期ウィンドウ
ボタンのテキストを更新するコード
図 13: ボタンのテキストを更新するコード

nib_parse.py からの出力は、作成者が Xcode によって提供されるデフォルトのデリゲート クラス AppDelegate を使用したことを示しています。 AppDelegate クラスには、NSButton オブジェクト用の 2 つのインスタンス変数と、NSTextField オブジェクト用の 4 つのインスタンス変数があります。 btnSearchAdware という名前のセレクターが、インスタンス変数 btnAction と同じ ID (49) のボタンに接続されています。これは、分析を開始するための興味深い機能である可能性があります。

概要

Cocoa アプリケーションのリバース エンジニアリングに関するこの慌ただしいツアーをお楽しみいただけたでしょうか。 macOS の内部構造と分析ツール、リバース エンジニアリングとデバッグ手法、実際に出回っている macOS マルウェアにもっと触れてみたいと思っている方は、今年の Black Hat に参加して詳細を学んでください!

参考: https ://www.mandiant.com/resources/blog/introduction-to-reve

Comments

Copied title and URL