0

長い投稿で申し訳ありません:)

例外で次のことを行う.exeファイル用のピンツールを作成したいと思います。

  1. 例外命令アドレスを出力します。
  2. この例外を処理するハンドラのアドレスを出力します。
  3. プログラムが戻る命令のアドレスを出力します。

私は Windows SEH メカニズムについてすべて読んでおり、Intel ピンツール自体にも精通しています。作業を開始するために、次のテスト プログラムを作成しました。

#include <stdio.h>
void bar(){
        throw 20;
}
void foo(char *s, int a){
    printf(s,a);
}
int main(){
    try{
        bar();
    }
    catch(int e){
        foo("%d\n",e );
    }
}

次に、pintool を使用して、すべてのルーチン (main が呼び出された後に呼び出されるルーチン) と、このプログラムの戻りアドレスを出力しました。リストは次のとおりです。

次に、これらのルーチンのドキュメントを読みました。これらのルーチンの引数には、必要な情報が含まれている可能性があると考えました。しかし、すべて無駄です。RtlRaiseException引数によってExceptionRecord構造体にアクセスできましたが、そのExceptionAddressフィールドには のアドレスではRaiseExceptionなく の開始アドレスが含まれていますbar

そして、を処理する例外ハンドラの場所を取得する方法を見つけることができませんでしたthrow

どんな助けでも大歓迎です。ありがとう :)

4

1 に答える 1

1

これは PIN ソリューションを直接目的とした回答ではなく、ハンドラーを見つける方法に関するヘルプです。

以下は、最適化なしでコンパイルされたコードの興味深い部分です (ハンドラーが に近すぎないようにprintf、関数の下にa を追加しました。VS 2015 でコンパイルされたコード)。barbar

CPU Disasm
Address    Command                                Comments
011D1071   CALL bar                               ; [bar
011D1076   PUSH OFFSET 011D2110                   ; ASCII "you wont see me..."
011D107B   CALL printf                            ; [printf
011D1080   ADD ESP,4
011D1083   JMP SHORT 011D109C
011D1085   MOV EAX,DWORD PTR SS:[EBP-14]          ; Handler start
011D1088   PUSH EAX
011D1089   PUSH OFFSET 011D2124                   ; ASCII "%d"
011D108E   CALL foo                               ; [foo

bar() 関数のコードは次のとおりです。

CPU Disasm
Address    Command                                Comments
011D1000 b PUSH EBP                               ; TestCppException.bar(void)
011D1001   MOV EBP,ESP
011D1003   PUSH ECX
011D1004   MOV DWORD PTR SS:[EBP-4],14
011D100B   PUSH OFFSET _TI1H                      ; /Arg2 = TestCppException._TI1H
011D1010   LEA EAX,[EBP-4]                        ; |
011D1013   PUSH EAX                               ; |Arg1
011D1014   CALL _CxxThrowException                ; \VCRUNTIME140._CxxThrowException
011D1019   MOV ESP,EBP
011D101B   POP EBP
011D101C   RETN

C++ 例外は、他の例外 (アクセス違反、0 による除算など) とは異なります。これは、ハンドラー (キャッチ部分) に到達する前に内部で多くのことが行われているためです。

ご覧のとおり、C++ 例外は次を使用して発生します。

RtlRaiseException呼び出されると、例外レコードは次のようになります。

CPU Stack
Address   Value                Comments
0018FAF8  |E06D7363    ; ExceptionCode=E06D7363
0018FAFC  |00000001    ; Flags=EXCEPTION_NONCONTINUABLE
0018FB00  |00000000    ; pExceptionRecord=NULL
0018FB04  |7736DAA0    ; ExceptionAddress=KERNELBASE.RaiseException
0018FB08  |00000003    ; Nparm=3
0018FB0C  |19930520    ; exception version identifier
0018FB10  |0018FBA4    ; pObject
0018FB14  |011D2638    ; _ThrowInfo*
  • 例外コード は、C++ 例外を識別します(0xE06D7363すべての C++ 例外に対して同じコード)。

  • C++ 例外は継続をサポートしていないため、フラグが設定されEXCEPTION_NONCONTINUABLEます (コードは例外が発生した時点で実行を継続できません)。

  • CPP 例外には例外チェーンがないため、pExceptionRecordは NULL です。

  • ExceptionAddressは、RaiseException例外を発生させるコードが "直接" ではなく、システムが代わりに実行するためです (つまり、非常に特定のアセンブリ命令を特定できる 0 による除算がある場合とは異なります。ここでは、システムが例外を発生させ、RaiseException でそれを発生させます)。

  • この例外レコードには 3 つのパラメーターがあります。

    • 最初のものは例外識別子です (これは文書化されていませんが、おそらくこれが VC6 スタイルの例外であることを意味します)。

    • pObject は、スローされたオブジェクトへのポインターです (この場合、これは整数 20 へのポインターです)

    • 最後に、システム (より正確には例外ディスパッチャー) がこの例外をキャッチできるハンドラーを確認するために使用する_ThrowInfo構造体 ( に渡される引数と同じ)。_CxxThrowException

この時点から、システムが例外をディスパッチし、適切な SEH ハンドラーを検索する「通常の」SEH 処理が行われます。この例では、「どの catch ブロックが int をキャッチできるか」です。

チェーンの最後の SEH がスローされた int を処理できる場合があります。

SEH のアセンブリ ビューを次に示します。

CPU Disasm
Address    Command                           Comments
00CA1D91   MOV EAX,OFFSET 011D2580        ; pointer to __ehfuncinfo
00CA1D96   JMP __CxxFrameHandler3            ; Jump to VCRUNTIME140.__CxxFrameHandler

eaxという名前の関数に(レジスタを介して)渡された構造体のみがあり__CxxFrameHandler3ます。

名前付きの構造は__ehfuncinfo次のようになります。

struct ehfuncinfo1200 //_s_ESTypeList
{
  /* 0x00 */  uint32_t        magic : 30;
  /* 0x04 */  ehstate_t       unwindtable_size;
  /* 0x08 */  unwindtable *   unwindtable;
  /* 0x0C */  size_t          tryblocktable_size;
  /* 0x10 */  tryblock *      tryblocktable;
  /* 0x14 */  size_t          _size;
  /* 0x18 */  void *          _;
/* … snip … */
};

私たちの場合、次のものがあります。

CPU 
Address    Value       Comments
011D2580   19930522    ; magic
011D2584   00000002    ; unwind table size 
011D2588   011D25A4    ; unwind table
011D258C   00000001    ; try block table size
011D2590   011D25B4    ; try block table
011D2594   00000000
011D2598   00000000

のエントリは次のtry block tableようになります。

struct tryblock
{
  ehstate_t   trylow;
  ehstate_t   tryhigh;
  ehstate_t   catchhigh;
  int         ncatches;
  ehandler *  catchsym;

  /* snip */
};

以下は、この例の値です。

CPU Stack
Address   Value      Comments
011D25B4   00000000  
011D25B8   00000000
011D25BC   00000001  
011D25C0   00000001 ; ncatches
011D25C4   011D25C8 ; catchsym

catchsymフィールドは構造ehandler体です:

/// This type represents the catch clause
struct ehandler
{
//  union { uint32_t  adjectives; void * ptr; };
  uint32_t isconst      : 1; /* + 00 */
  uint32_t isvolatile   : 1;
  uint32_t isunaligned  : 1;
  uint32_t isreference  : 1;

  const type_info *   typeinfo; /* + 04 */
  ptrdiff_t           eobject_bpoffset; // 0 = no object (catch by type)
  generic_function_t *  handler; /* + 0x0C */

  /* snip */
};

:handlerフィールドは catch ブロックのアドレスです!

そして値:

CPU Stack
Address   Value        Comments
011D25C8   00000000    ; 
011D25CC   011D3030    ; Typeinfo (OFFSET TestCppException.int `RTTI Type Descriptor')
011D25D0   FFFFFFEC    
011D25D4   011D1085    ; Handler entry point

この場合、handler フィールドは catch ブロックの正確な位置を指しています。

CPU Disasm
Address    Command                            Comments
011D1085   MOV EAX,DWORD PTR SS:[EBP-14]      ; handler start
011D1088   PUSH EAX
011D1089   PUSH OFFSET 011D2124               ; ASCII "%d"
011D108E   CALL foo                           ;

概要

例外が発生した場合 ( PIN マニュアルの例外 API を参照)

  • 例外 ocde を確認してください。そうであれば、それ0xE06D7363は C++ 例外です。
  • 呼び出されるのを待ち__CxxFrameHandler3ます (技術的には、CALL ではなく、この関数への JMP です)
  • エントリー時にeaxレジスターを確認してください。__CxxFrameHandler3
  • レジスタは構造体へのeaxポインタです。__ehfuncinfo
  • handler構造体のフィールドまで、すべての構造体をたどりますehandler
    • フィールドはのhandlerアドレスですcatch block

この件に関するいくつかの良い指針:


ピンツール コード

/* ===================================================================== */
/* This example demonstrates finding a function by name on Windows.      */
/* ===================================================================== */

#include "pin.H"
#include <iostream>
#include <fstream>

/*
* C++ exception structures
*/

// This type represents the catch clause
typedef struct _ehandler
{
    //  union { uint32_t  adjectives; void * ptr; };
    uint32_t            adjectives;         /* + 0x00 */
    const type_info *   typeinfo;           /* + 0x04 */
    ptrdiff_t           eobject_bpoffset;   /* + 0x08 */
    void*               handler;            /* + 0x0C */ 
    /* snip */
} ehandler;

typedef struct _tryblock
{
    uint32_t   trylow;
    uint32_t   tryhigh;
    uint32_t   catchhigh;
    int         ncatches;
    ehandler *  catchsym;
    /* snip */
} tryblock;

typedef struct ehfuncinfo1200 //_s_ESTypeList
{
    /* 0x00 */  uint32_t        magic : 30;
    /* 0x04 */  uint32_t       unwindtable_size;
    /* 0x08 */  void *   unwindtable;
    /* 0x0C */  size_t          tryblocktable_size;
    /* 0x10 */  tryblock *      tryblocktable;
    /* 0x14 */  size_t          _size;
    /* 0x18 */  void *          _;
    /* snip */
} ehfuncinfo;

/* ===================================================================== */
/* Global Variables */
/* ===================================================================== */

std::ofstream TraceFile;

/* ===================================================================== */
/* Commandline Switches */
/* ===================================================================== */

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
    "o", "check_handler.out", "specify trace file name");

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool produces a trace of calls to RtlAllocateHeap.";
    cerr << endl << endl;
    cerr << KNOB_BASE::StringKnobSummary();
    cerr << endl;
    return -1;
}

/* ===================================================================== */
/* Analysis routines                                                     */
/* ===================================================================== */

VOID Before(CHAR * name, ADDRINT RegValue)
{
    TraceFile << "At function entry (" << name << "); EAX: " << hex << RegValue << endl;

    // eax is a pointer to ehfuncinfo struct
    ehfuncinfo* funcinfo = reinterpret_cast<ehfuncinfo*>(RegValue);

    // get the tryblock table from ehfuncinfo
    tryblock* tryb = funcinfo->tryblocktable;

    // get ehandler struct from try block
    ehandler* ehand = tryb->catchsym;

    // from ehandler structure, get handler address
    void* handler = ehand->handler;

    // save it to file
    TraceFile << "Handler Address: " << hex << handler << endl;
}

/* ===================================================================== */
/* Instrumentation routines                                              */
/* ===================================================================== */

VOID Image(IMG img, VOID *v)
{
    // Walk through the symbols in the symbol table.
    //
    for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym))
    {
        string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY);

        //  Find function.
        if (undFuncName == "__CxxFrameHandler3")
        {
            std::cout << "OK! found __CxxFrameHandler3" << std::endl;

            RTN allocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));

            if (RTN_Valid(allocRtn))
            {
                // Instrument to print the input argument value and the return value.
                RTN_Open(allocRtn);

                RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)Before,
                    IARG_ADDRINT, "__CxxFrameHandler3",
                    IARG_REG_VALUE, REG::REG_EAX,
                    IARG_END);

                RTN_Close(allocRtn);
            }
        }
    }
}

/* ===================================================================== */

VOID Fini(INT32 code, VOID *v)
{
    TraceFile.close();
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char *argv[])
{
    // Initialize pin & symbol manager
    PIN_InitSymbols();
    if (PIN_Init(argc, argv))
    {
        return Usage();
    }

    // Write to a file since cout and cerr maybe closed by the application
    TraceFile.open(KnobOutputFile.Value().c_str());
    TraceFile << hex;
    TraceFile.setf(ios::showbase);

    // Register Image to be called to instrument functions.
    IMG_AddInstrumentFunction(Image, 0);
    PIN_AddFiniFunction(Fini, 0);

    // Never returns
    PIN_StartProgram();

    return 0;
}

/* ===================================================================== */
/* eof */
/* ===================================================================== */

出力

At function entry (__CxxFrameHandler3); EAX: 0xcc2580
Handler Address: 0x00cc1085

どちらも指定されたコードに一致します (ASLR によるオフセットを差し引いたもの):

 MOV EAX,OFFSET 011D2580        ; pointer to __ehfuncinfo

 ...

011D1085   MOV EAX,DWORD PTR SS:[EBP-14]  ; Handler start 

于 2016-06-10T10:20:43.183 に答える