IBの動的バイナリインスツルメンテーション

ソフトウェアの複雑さは増大しています。プログラムはより動的になり、その動作は実行プロセスでのみ評価できます。 そのようなアプリケーションのセキュリティ評価(脆弱性、文書化されていない機能などの検索)を実施することははるかに困難です。 動的に生成されたコードのため、分析中にコードの完全なカバレッジを保証することさえできないため、静的分析アプローチのみを使用することは不可能になります。 動的分析法が助けになります。



Dynamic Binary Instrumentation(DBI)のような素晴らしいテクノロジーがあります。これは、分析(一般的な場合)プロシージャをバイナリ実行可能コードに挿入することから成ります。 このアプローチの主な魅力は、分析されたアプリケーションのソースコードが不要であるという事実にあります-バイナリファイルを直接操作します。



インストルメンテーションは、分析のために調査中のプログラムを変更するプロセスです。 通常、インストルメンテーションプロシージャは、必要なイベントが発生してターゲットプログラムを変更するときに1回だけ呼び出される追加コードの挿入を担当します。 追加されたコードは分析手順です。 これらの手順は、調査された(ターゲット)プログラムの必要な分析、変更、監視を実行する責任があり、コードの特定の部分に到達するか、プログラムで特定のイベントが発生するたびに呼び出されます(プロセスの作成、例外の発生など)。 バイナリアプリケーションのインスツルメンテーションは、プログラムの粒度の異なるレベルで実行できます。

•指示。

•ベースユニット。

•トラック。

•手順

•バイナリファイルのセクション。

•バイナリイメージ。



その結果、動的バイナリインスツルメンテーション用の特別なフレームワークが開発され、プログラム実行中に機能するこのようなツールが作成されました。 ヘルプを使用して作成できるツールは、動的バイナリアナライザー(DBA、動的バイナリ分析)と呼ばれます。



最も一般的な4つのフレームワークであるPINDynamoRIODynInst、およびValgrindを区別できます。 ZeroNightsカンファレンスのプレゼンテーション「DBI:Intro」から各ライブラリの詳細をご覧いただけます。



この記事では、 DSecRGリサーチセンターのプロジェクトの一環として積極的に使用しているIntelのPINライブラリについて詳しく説明し、ソースコードなしでバイナリアプリケーションを操作する際に発生する問題を解決する方法を検討します。



このライブラリは積極的に開発および保守されています。 周辺にはかなり大きなコミュニティがあります-pinheadsで 、PINについての質問に答えることができます。



DBIは、Immunity、Zynamics、Rapid7、SourceFire VRT、Coseincなどのクールなセキュリティ研究センターを積極的に使用しています。 DBIの知識は、ソフトウェアセキュリティの評価とエクスプロイトの開発におけるスペシャリストの要件の1つとして既に遭遇しています。 Metasploit開発チームのエクスプロイトエンジニアの求人からの要件の例を次に示します。







PINを使用する例として、シャドウスタックの実装を検討してください。シャドウスタックの本質は、プログラムリターンアドレスの独自のスタックを作成することです。 これにより、リターンアドレスが上書きされるときに、スタック上のバ​​ッファオーバーフローを識別できます。 このようなツールのアルゴリズムは非常に単純です。各関数を呼び出す前に、シャドウスタックの戻りアドレスを記憶し、関数を終了する前に、戻りアドレスをシャドウスタックに格納されている値と比較します。



#include <stdio.h> #include "pin.H" #include <stack> typedef struct { ADDRINT address; ADDRINT value; } pAddr; // Shadow Stack stack<pAddr> protect; FILE * logfile; //-------------------------------------------------------------------------------------- VOID Fini(INT32 code, VOID *v) { fclose(logfile); } //-------------------------------------------------------------------------------------- VOID RtnEntry(ADDRINT esp, ADDRINT addr) { pAddr tmp; tmp.address = esp; tmp.value = *((ADDRINT *)esp); //     Shadow Stack protect.push(tmp); } //-------------------------------------------------------------------------------------- VOID RtnExit(ADDRINT esp, ADDRINT addr) { //    Shadow Stack if (protect.empty()) { fprintf(logfile, "WARNING! protection list empty\n"); return; } pAddr orig = protect.top(); ADDRINT cur_val = (*((ADDRINT *)orig.address)); //       Shadow Stack if (orig.value != cur_val) { fprintf(logfile, "Overwrite at: %x old value: %x, new value: %x\n", orig.address, orig.value, cur_val ); } //      Shadow Stack protect.pop(); } //-------------------------------------------------------------------------------------- //     VOID Routine(RTN rtn, VOID *v) { RTN_Open(rtn); SEC sec = RTN_Sec(rtn); IMG img = SEC_Img(sec); if ( IMG_IsMainExecutable(img) && (SEC_Name(sec) == ".text") ) { // ,     (RtnEntry) RTN_InsertCall(rtn, IPOINT_BEFORE,(AFUNPTR)RtnEntry, IARG_REG_VALUE, REG_ESP, IARG_INST_PTR, IARG_END); // ,     (RtnExit) RTN_InsertCall(rtn, IPOINT_AFTER ,(AFUNPTR)RtnExit, IARG_REG_VALUE, REG_ESP, IARG_INST_PTR, IARG_END); } RTN_Close(rtn); } //-------------------------------------------------------------------------------------- INT32 Usage() { PIN_ERROR( "This Pintool logs function return addresses in main module and reports modifications\n" + KNOB_BASE::StringKnobSummary() + "\n"); return -1; } //-------------------------------------------------------------------------------------- int main(int argc, char *argv[]) { //    PIN_InitSymbols(); //   PIN if (PIN_Init(argc, argv)) { return Usage(); } //  - logfile = fopen("protection.out", "w"); //  ,      RTN_AddInstrumentFunction(Routine, 0); //   ,       PIN_AddFiniFunction(Fini, 0); //  PIN_StartProgram(); return 0; }
      
      





グラフィカルに、このツールの作業は次のように表すことができます。





このツールは、返信先アドレスを書き換える場合にのみ機能し、返信先アドレスの前のデータを破壊する場合は何も気付かないことは当然です。 関数内のデータ破損に気付くには、/ RTCファミリーのフラグを使用してプログラムをコンパイルする必要がありますが、ここでは、まず、調査対象のアプリケーションのソースコードを用意する必要があります。プログラムのパフォーマンスに影響します。



/ GSフラグは同様のタスクを実行し、戻りアドレスの前に特別なCookie値を追加し、この値をチェックしてから関数を終了することもできます。 ただし、このフラグはすべてのプログラムで使用されるわけではなく、単純な操作からスタック内のバッファオーバーフローを単に保護できない場合もあります。



たとえば、func_bof()関数では、安全でないstrcpy()関数が呼び出され、その入力パラメーターはどのような方法でもフィルターされないため、スタックでバッファーオーバーフローが発生します。 その結果、関数パラメーター(変数b)、Cookie(存在する場合)、戻りアドレスは上書きされます。 ただし、プログラムはまだfunc_bof()関数の最後に到達していないため、Cookieチェックはすぐには機能しません。その結果、書き換えられた変数bはcritical_func()関数に入ります。さらに進んでください。その結果、プログラムは問題の実際の場所から大きく外れます。 上記のコードの最も簡単な変更を使用すると、子関数を呼び出す場合でも関数の戻りアドレスを確認できます(これを宿題として残しましょう)。これにより、critical_func()を呼び出す前にこの問題を特定できます。







最後に、PINを使用して実装された興味深い公共安全プロジェクトのリストを示します。

-Shellcode dumper-標準シェルコードのダンプ。その原理は、スタックまたはヒープへの制御の転送に基づいています。

-Moflow-mitigationsは、ROPシェルコードとJITシェルコードを識別するプロトタイプです。

- コードカバレッジ分析ツール -コードカバレッジアナライザー。

-RunTracer-プログラムの制御フローを追跡するためのツールのセット。

-Kerckhoffs-プログラム内の暗号プリミティブの半自動検出のためのツール。

-VERA-プログラムを視覚化するためのツール。

-Tripoux-マルウェアパッカーアナライザー。

- プライバシースコープ -重要な情報の漏洩を検出するためのツール。

-Tartetatintools-悪意のあるコードを分析するためのツールのセット。



PINツールの結果をIDA Pro逆アセンブラーと統合する非常に興味深い実装もあります。これはビジュアライザーの役割を果たします。

-DiffCov-プログラムの完成した基本ブロックを記録するためのツールのセット。

-runtime-tracer-レジスタ値と処理されたメモリセクションでプログラム実行トレースを作成します。







これは、PINを使用して実装されたセキュリティプロジェクトの完全なリストではありませんが、ピンツールツールの作成を開始するにはこれで十分だと思われます。



また、情報セキュリティのDBI / PINに関心のあるすべての人に、私の親友であるIntelのGal Diskinによるワークショップ「セキュリティ専門家向けのバイナリインスツルメンテーション」( スライド )に注意することを強くお勧めします。 私もこのコースに参加できて幸運でした。 BlackHat、DEFCON、HackLuなどのハッカー会議で読まれました。



All Articles