これは PIN ソリューションを直接目的とした回答ではなく、ハンドラーを見つける方法に関するヘルプです。
以下は、最適化なしでコンパイルされたコードの興味深い部分です (ハンドラーが に近すぎないようにprintf
、関数の下にa を追加しました。VS 2015 でコンパイルされたコード)。bar
bar
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