Sorry for the long POST :)
I would like to write a pintool for .exe files which on a exception does the following:
I have read all about windows SEH mechanism and I have good familiarity with intel pin-tool itself. To get going, I wrote the following test program:
#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 );
}
}
Then I printed all the routines (that are called after main has been called) with their return addresses for this program using pintool. Here is the list:
Then I read up the documentation of these routines. I thought that the arguments of these routines might contain the information I need. But all in vain. RtlRaiseException
arguments did gave me access to the ExceptionRecord
structure but its ExceptionAddress
field contains the starting address of RaiseException
instead of an address of bar
.
And I was unable to find any way to get the Exception Handler location which will handle the throw
.
Any help is appreciated; thanks :)
This is not an answer directly aimed at a PIN solution but rather a help on how to find the handler.
Below is the interesting part of the code compiled without optimization (I added a printf
below the bar
function so the handler was not too close to bar
; code compiled with VS 2015):
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
Here's the code for the bar() function:
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++ exception are different beasts than other exceptions (access violation, divide by 0, etc.) as there are a lot of things going on under the hood before reaching the handler (the catch part).
As you have seen, a C++ exception is raised using:
When RtlRaiseException
is called, the Exception record looks like this:
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*
The exception code 0xE06D7363
identifies a C++ exception (same code for all C++ exception).
Flags is set to EXCEPTION_NONCONTINUABLE
because C++ exceptions do not support continuation (the code can’t continue execution at the point where the exception occurred).
pExceptionRecord is NULL because there’s no exception chaining in CPP exception.
The ExceptionAddress is RaiseException
because it is not “directly” your code that raises the exception, but the system does it for you (i.e: it’s not like if you had a divide by 0 which can be pinpointed to a very specific assembly instruction; here the system raises the exception for you, and it raises it at RaiseException).
There are 3 parameters to this exception record :
The first one is an exception identifier (this is undocumented, but probably means that this is a VC6 style exception).
pObject is a pointer to the thrown object (in our case this is a pointer to the integer 20)
Finally a _ThrowInfo
structure (the same as the argument passed to _CxxThrowException
) which is used by the system, more precisely the exception dispatcher, to see which handler can catch this exception.
From this point, we have the “usual” SEH stuff where the system dispatches the exception and search for the right SEH handler. In our example, “which catch block can catch an int”.
It happens that the last SEH in the chain can handle the thrown int.
Here’s an assembly view of the SEH:
CPU Disasm
Address Command Comments
00CA1D91 MOV EAX,OFFSET 011D2580 ; pointer to __ehfuncinfo
00CA1D96 JMP __CxxFrameHandler3 ; Jump to VCRUNTIME140.__CxxFrameHandler
We only have a structure passed (through eax
register) to a function named __CxxFrameHandler3
.
The structure, named __ehfuncinfo
looks like this:
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 … */
};
In our case we have:
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
An entry in the try block table
looks like this:
struct tryblock
{
ehstate_t trylow;
ehstate_t tryhigh;
ehstate_t catchhigh;
int ncatches;
ehandler * catchsym;
/* snip */
};
Below are the value from our example:
CPU Stack
Address Value Comments
011D25B4 00000000
011D25B8 00000000
011D25BC 00000001
011D25C0 00000001 ; ncatches
011D25C4 011D25C8 ; catchsym
The catchsym
field is a ehandler
structure:
/// 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 */
};
Note: the handler
field is the address of the catch block!
And the value:
CPU Stack
Address Value Comments
011D25C8 00000000 ;
011D25CC 011D3030 ; Typeinfo (OFFSET TestCppException.int `RTTI Type Descriptor')
011D25D0 FFFFFFEC
011D25D4 011D1085 ; Handler entry point
And in our case the handler field points to the precise location of the catch block:
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 ;
Summary
If an exception occurs (see Exception API in PIN Manual)
0xE06D7363
then it's a C++ exception__CxxFrameHandler3
to be called (technically it is a JMP to this function, not a CALL)eax
register on __CxxFrameHandler3
entry.eax
register is a pointer to a __ehfuncinfo
structure.handler
field of the ehandler
structure.
handler
field is the address of the catch block
Some good pointers on the subject:
Pintool code
/* ===================================================================== */
/* 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 */
/* ===================================================================== */
Output
At function entry (__CxxFrameHandler3); EAX: 0xcc2580
Handler Address: 0x00cc1085
Both match the given code (minus offsets due to ASLR):
MOV EAX,OFFSET 011D2580 ; pointer to __ehfuncinfo
...
011D1085 MOV EAX,DWORD PTR SS:[EBP-14] ; Handler start