diff --git a/pykd/dbgcmd.h b/pykd/dbgcmd.h index 5aa8817..d59ad02 100644 --- a/pykd/dbgcmd.h +++ b/pykd/dbgcmd.h @@ -30,8 +30,13 @@ setExecutionStatus() } if ( FAILED( hres ) ) - throw DbgException( "IDebugControl::WaitForEvent failed" ); - + { + if (E_UNEXPECTED == hres) + throw WaitEventException(); + + throw DbgException( "IDebugControl::WaitForEvent failed" ); + } + hres = dbgExt->control->GetExecutionStatus( ¤tStatus ); if ( FAILED( hres ) ) diff --git a/pykd/dbgevent.cpp b/pykd/dbgevent.cpp index 4405485..3d47ab9 100644 --- a/pykd/dbgevent.cpp +++ b/pykd/dbgevent.cpp @@ -43,12 +43,110 @@ HRESULT debugEvent::GetInterestMask( *Mask |= DEBUG_EVENT_LOAD_MODULE; *Mask |= DEBUG_EVENT_UNLOAD_MODULE; *Mask |= DEBUG_EVENT_SESSION_STATUS; + *Mask |= DEBUG_EVENT_EXCEPTION; + *Mask |= DEBUG_EVENT_BREAKPOINT; return S_OK; } /////////////////////////////////////////////////////////////////////////////////// +HRESULT debugEvent::Breakpoint( + __in PDEBUG_BREAKPOINT Bp +) +{ + boost::python::dict bpParameters; + + HRESULT hres; + ULONG Value = 0; + ULONG Value2 = 0; + ULONG64 Value64 = 0; + std::string str; + +#define _ADD_BP_ULONG(x) \ + hres = Bp->Get##x(&Value); \ + BOOST_ASSERT( SUCCEEDED( hres ) || hres == E_NOINTERFACE ); \ + if (SUCCEEDED( hres )) \ + bpParameters[#x] = Value; + +#define _ADD_BP_ULONG2(x, n1, n2) \ + hres = Bp->Get##x(&Value, &Value2); \ + BOOST_ASSERT( SUCCEEDED( hres ) || hres == E_NOINTERFACE ); \ + if (SUCCEEDED( hres )) \ + { \ + bpParameters[n1] = Value; bpParameters[n2] = Value2; \ + } + +#define _ADD_BP_ULONG64(x) \ + hres = Bp->Get##x(&Value64); \ + BOOST_ASSERT( SUCCEEDED( hres ) || hres == E_NOINTERFACE ); \ + if (SUCCEEDED( hres )) \ + bpParameters[#x] = Value64; + +#define _ADD_BP_STR(x) \ + Value = 0; \ + Bp->Get##x(NULL, 0, &Value); \ + if (Value) \ + { \ + str.resize(Value + 1); \ + BOOST_VERIFY( SUCCEEDED( \ + Bp->Get##x(&str[0], (ULONG)str.size(), NULL) \ + ) ); \ + if (!str.empty()) bpParameters[#x] = str.c_str(); \ + } + + _ADD_BP_ULONG(Id); + _ADD_BP_ULONG2(Type, "BreakType", "ProcType"); + _ADD_BP_ULONG(Flags); + _ADD_BP_ULONG64(Offset); + _ADD_BP_ULONG2(DataParameters, "Size", "AccessType"); + _ADD_BP_ULONG(PassCount); + _ADD_BP_ULONG(CurrentPassCount); + _ADD_BP_ULONG(MatchThreadId); + _ADD_BP_STR(Command); + _ADD_BP_STR(OffsetExpression); + +#undef _ADD_BP_ULONG +#undef _ADD_BP_ULONG2 +#undef _ADD_BP_ULONG64 +#undef _ADD_BP_STR + + PyThread_StateSave pyThreadSave; + return onException(bpParameters); +} + +/////////////////////////////////////////////////////////////////////////////////// + +HRESULT debugEvent::Exception( + __in PEXCEPTION_RECORD64 Exception, + __in ULONG FirstChance +) +{ + boost::python::list exceptParams; + boost::python::dict exceptData; + + // build list of parameters + for (ULONG i = 0; i < Exception->NumberParameters; ++i) + exceptParams.append(Exception->ExceptionInformation[i]); + + // build dict of exception data +#define _ADD_EXCEPTION_ENTRY(x) exceptData[#x] = Exception->Exception##x + _ADD_EXCEPTION_ENTRY(Code); + _ADD_EXCEPTION_ENTRY(Flags); + _ADD_EXCEPTION_ENTRY(Record); + _ADD_EXCEPTION_ENTRY(Address); +#undef _ADD_EXCEPTION_ENTRY + + exceptData["Parameters"] = exceptParams; + + exceptData["FirstChance"] = (0 != FirstChance); + + PyThread_StateSave pyThreadSave; + return onException(exceptData); +} + +/////////////////////////////////////////////////////////////////////////////////// + HRESULT debugEvent::LoadModule( __in ULONG64 ImageFileHandle, __in ULONG64 BaseOffset, diff --git a/pykd/dbgevent.h b/pykd/dbgevent.h index c8cd9fc..ffdfa77 100644 --- a/pykd/dbgevent.h +++ b/pykd/dbgevent.h @@ -16,6 +16,10 @@ public: virtual ~debugEvent(); + virtual ULONG onBreakpoint(boost::python::dict &/*bpParameters*/) = 0; + + virtual ULONG onException(boost::python::dict &/*exceptData*/) = 0; + virtual ULONG onLoadModule(const dbgModuleClass &/* module */) = 0; virtual ULONG onUnloadModule(const dbgModuleClass &/* module */) = 0; @@ -33,6 +37,16 @@ private: __out PULONG Mask ); + STDMETHOD(Breakpoint)( + __in PDEBUG_BREAKPOINT Bp + ); + + + STDMETHOD(Exception)( + __in PEXCEPTION_RECORD64 Exception, + __in ULONG FirstChance + ); + STDMETHOD(LoadModule)( __in ULONG64 ImageFileHandle, __in ULONG64 BaseOffset, @@ -68,6 +82,13 @@ class debugEventWrap : public boost::python::wrapper, public debugEv public: + ULONG onBreakpoint(boost::python::dict &bpParameters) { + return handler("onBreakpoint", bpParameters); + } + + ULONG onException(boost::python::dict &exceptData) { + return handler("onException", exceptData); + } ULONG onLoadModule(const dbgModuleClass &module) { return handler("onLoadModule", module ); diff --git a/pykd/dbgexcept.cpp b/pykd/dbgexcept.cpp index e14b4c7..cb1b63e 100644 --- a/pykd/dbgexcept.cpp +++ b/pykd/dbgexcept.cpp @@ -6,6 +6,7 @@ // типы исключений PyObject *baseExceptionType = NULL; +PyObject *eventExceptionType = NULL; PyObject *typeExceptionType = NULL; PyObject *memoryExceptionType = NULL; @@ -16,12 +17,21 @@ PyObject *memoryExceptionType = NULL; void DbgException::exceptionTranslate( const DbgException &e ) { boost::python::object pyExcept(e); - + PyErr_SetObject( baseExceptionType, pyExcept.ptr()); } ///////////////////////////////////////////////////////////////////////////////// +void WaitEventException::exceptionTranslate( const WaitEventException &e ) +{ + boost::python::object pyExcept(e); + + PyErr_SetObject( eventExceptionType, pyExcept.ptr()); +} + +///////////////////////////////////////////////////////////////////////////////// + void TypeException::exceptionTranslate( const TypeException &e ) { boost::python::object pyExcept(e); @@ -34,7 +44,7 @@ void TypeException::exceptionTranslate( const TypeException &e ) void MemoryException::translate( const MemoryException &e ) { boost::python::object pyExcept(e); - + PyErr_SetObject( memoryExceptionType, pyExcept.ptr()); } diff --git a/pykd/dbgexcept.h b/pykd/dbgexcept.h index 47da5b6..a7b873c 100644 --- a/pykd/dbgexcept.h +++ b/pykd/dbgexcept.h @@ -10,17 +10,27 @@ class DbgException : public std::exception public: DbgException( const std::string &desc ) : - std::exception( desc.c_str() ) - {} - + std::exception( desc.c_str() ) + {} + const char* getDesc() const { return what(); } - - + static void - exceptionTranslate(const DbgException &e ); + exceptionTranslate(const DbgException &e ); +}; + +class WaitEventException : public DbgException +{ +public: + WaitEventException() + : DbgException( "none of the targets could generate events" ) + { + } + + static void exceptionTranslate(const WaitEventException &e); }; ///////////////////////////////////////////////////////////////////////////////// @@ -40,7 +50,7 @@ public: static void exceptionTranslate(const TypeException &e ); - + }; ///////////////////////////////////////////////////////////////////////////////// @@ -114,6 +124,7 @@ private: ///////////////////////////////////////////////////////////////////////////////// extern PyObject *baseExceptionType; +extern PyObject *eventExceptionType; extern PyObject *typeExceptionType; extern PyObject *memoryExceptionType; diff --git a/pykd/dbgext.cpp b/pykd/dbgext.cpp index cde7b69..f706df3 100644 --- a/pykd/dbgext.cpp +++ b/pykd/dbgext.cpp @@ -352,14 +352,27 @@ BOOST_PYTHON_MODULE( pykd ) "Remove a breakpoint set before" ) .def( "__str__", &dbgBreakpointClass::print, "Return a nice string represention of the breakpoint class" ); - + boost::python::class_( "debugEvent", "Base class for debug events handlers" ) + .def( "onBreakpoint", &debugEventWrap::onBreakpoint, + "Triggered breakpoint event. Parameter is dict:\n" + "{\"Id\":int, \"BreakType\":int, \"ProcType\":int, \"Flags\":int, \"Offset\":int," + " \"Size\":int, \"AccessType\":int, \"PassCount\":int, \"CurrentPassCount\":int," + " \"MatchThreadId\":int, \"Command\":str, \"OffsetExpression\":str}\n" + "Detailed information: http://msdn.microsoft.com/en-us/library/ff539284(VS.85).aspx \n" + "For ignore event method must return DEBUG_STATUS_NO_CHANGE value" ) + .def( "onException", &debugEventWrap::onException, + "Exception event. Parameter is dict:\n" + "{\"Code\":int, \"Flags\":int, \"Record\":int, \"Address\":int," + " \"Parameters\":list_of_int, \"FirstChance\":bool}\n" + "Detailed information: http://msdn.microsoft.com/en-us/library/aa363082(VS.85).aspx \n" + "For ignore event method must return DEBUG_STATUS_NO_CHANGE value" ) .def( "onLoadModule", &debugEventWrap::onLoadModule, - "Load module event. Parameter is instance of dbgModuleClass. " + "Load module event. Parameter is instance of dbgModuleClass.\n" "For ignore event method must return DEBUG_STATUS_NO_CHANGE value" ) .def( "onUnloadModule", &debugEventWrap::onUnloadModule, - "Unload module event. Parameter is instance of dbgModuleClass. " + "Unload module event. Parameter is instance of dbgModuleClass.\n" "For ignore event method must return DEBUG_STATUS_NO_CHANGE value" ); boost::python::class_("disasm", "Class disassemble a processor instructions", boost::python::no_init ) @@ -384,6 +397,10 @@ BOOST_PYTHON_MODULE( pykd ) .def( "desc", &DbgException::getDesc, "Get exception description" ); + boost::python::class_ > waitExceptionClass( "WaitEventException", + "Type exception class", + boost::python::no_init ); + boost::python::class_ > typeExceptionClass( "TypeException", "Type exception class", boost::python::no_init ); @@ -396,13 +413,15 @@ BOOST_PYTHON_MODULE( pykd ) .def( boost::python::init( boost::python::args("targetAddress"), "constructor" ) ) .def( "getAddress", &MemoryException::getAddress, "Return target address" ); - + baseExceptionType = dbgExceptionClass.ptr(); + eventExceptionType = waitExceptionClass.ptr(); typeExceptionType = typeExceptionClass.ptr(); memoryExceptionType = memoryExceptionClass.ptr(); - boost::python::register_exception_translator( &DbgException::exceptionTranslate ); - boost::python::register_exception_translator( &TypeException::exceptionTranslate ); + boost::python::register_exception_translator( &DbgException::exceptionTranslate ); + boost::python::register_exception_translator( &WaitEventException::exceptionTranslate ); + boost::python::register_exception_translator( &TypeException::exceptionTranslate ); boost::python::register_exception_translator( &IndexException::translate); boost::python::register_exception_translator( &MemoryException::translate ); @@ -411,83 +430,83 @@ BOOST_PYTHON_MODULE( pykd ) "intBase") .def( boost::python::init<>() ) .def( boost::python::init( boost::python::args("value"), "constructor" ) ) - + .def( "value", &intBase::value ) .def( int_( boost::python::self ) ) - //.def( boost::python::self = long() ) - + //.def( boost::python::self = long() ) + .def( boost::python::self + long() ) .def( long() + boost::python::self ) .def( boost::python::self += long() ) .def( boost::python::self + boost::python::self ) - .def( boost::python::self += boost::python::self ) - + .def( boost::python::self += boost::python::self ) + .def( boost::python::self - long() ) .def( long() - boost::python::self ) .def( boost::python::self -= long() ) .def( boost::python::self - boost::python::self ) .def( boost::python::self -= boost::python::self ) - + .def( boost::python::self * long() ) .def( long() * boost::python::self ) .def( boost::python::self *= long() ) .def( boost::python::self * boost::python::self ) .def( boost::python::self *= boost::python::self ) - + .def( boost::python::self / long() ) .def( long() / boost::python::self ) .def( boost::python::self /= long() ) .def( boost::python::self / boost::python::self ) - .def( boost::python::self /= boost::python::self ) + .def( boost::python::self /= boost::python::self ) .def( boost::python::self % long() ) .def( long() % boost::python::self ) .def( boost::python::self %= long() ) .def( boost::python::self % boost::python::self ) - .def( boost::python::self %= boost::python::self ) - + .def( boost::python::self %= boost::python::self ) + .def( boost::python::self & long() ) .def( long() & boost::python::self ) .def( boost::python::self &= long() ) .def( boost::python::self & boost::python::self ) - .def( boost::python::self &= boost::python::self ) - + .def( boost::python::self &= boost::python::self ) + .def( boost::python::self | long() ) .def( long() | boost::python::self ) .def( boost::python::self |= long() ) .def( boost::python::self | boost::python::self ) - .def( boost::python::self |= boost::python::self ) - + .def( boost::python::self |= boost::python::self ) + .def( boost::python::self ^ long() ) .def( long() ^ boost::python::self ) .def( boost::python::self ^= long() ) .def( boost::python::self ^ boost::python::self ) - .def( boost::python::self ^= boost::python::self ) - + .def( boost::python::self ^= boost::python::self ) + .def( boost::python::self << long() ) .def( boost::python::self <<= long() ) - + .def( boost::python::self >> long() ) .def( boost::python::self >>= long() ) - - .def( boost::python::self < long() ) - .def( boost::python::self < boost::python::self ) - - .def( boost::python::self <= long() ) - .def( boost::python::self <= boost::python::self ) - + + .def( boost::python::self < long() ) + .def( boost::python::self < boost::python::self ) + + .def( boost::python::self <= long() ) + .def( boost::python::self <= boost::python::self ) + .def( boost::python::self == long() ) - .def( boost::python::self == boost::python::self ) - - .def( boost::python::self >= long() ) - .def( boost::python::self >= boost::python::self ) - - .def( boost::python::self > long() ) - .def( boost::python::self > boost::python::self ) - - .def( boost::python::self != long() ) - .def( boost::python::self != boost::python::self ) - + .def( boost::python::self == boost::python::self ) + + .def( boost::python::self >= long() ) + .def( boost::python::self >= boost::python::self ) + + .def( boost::python::self > long() ) + .def( boost::python::self > boost::python::self ) + + .def( boost::python::self != long() ) + .def( boost::python::self != boost::python::self ) + .def( ~boost::python::self ) .def( !boost::python::self ) diff --git a/samples/watchDog.py b/samples/watchDog.py new file mode 100644 index 0000000..670cb15 --- /dev/null +++ b/samples/watchDog.py @@ -0,0 +1,97 @@ +""" +Exception watchdog +""" + +from pykd import * +import sys + +# known exception codes +knownExcepCodes = { + 0xc0000005 : "EXCEPTION_ACCESS_VIOLATION", + 0x80000002 : "EXCEPTION_DATATYPE_MISALIGNMENT", + 0x80000003 : "EXCEPTION_BREAKPOINT", + 0x80000004 : "EXCEPTION_SINGLE_STEP", + 0xc000008c : "EXCEPTION_ARRAY_BOUNDS_EXCEEDED", + 0xc000008d : "EXCEPTION_FLT_DENORMAL_OPERAND", + 0xc000008e : "EXCEPTION_FLT_DIVIDE_BY_ZERO", + 0xc000008f : "EXCEPTION_FLT_INEXACT_RESULT", + 0xc0000090 : "EXCEPTION_FLT_INVALID_OPERATION", + 0xc0000091 : "EXCEPTION_FLT_OVERFLOW", + 0xc0000092 : "EXCEPTION_FLT_STACK_CHECK", + 0xc0000093 : "EXCEPTION_FLT_UNDERFLOW", + 0xc0000094 : "EXCEPTION_INT_DIVIDE_BY_ZERO", + 0xc0000095 : "EXCEPTION_INT_OVERFLOW", + 0xc0000096 : "EXCEPTION_PRIV_INSTRUCTION", + 0xc0000006 : "EXCEPTION_IN_PAGE_ERROR", + 0xc000001d : "EXCEPTION_ILLEGAL_INSTRUCTION", + 0xc0000025 : "EXCEPTION_NONCONTINUABLE_EXCEPTION", + 0xc00000fd : "EXCEPTION_STACK_OVERFLOW", + 0xc0000026 : "EXCEPTION_INVALID_DISPOSITION", + 0x80000001 : "EXCEPTION_GUARD_PAGE", + 0xc0000008 : "EXCEPTION_INVALID_HANDLE", + 0xc0000194 : "EXCEPTION_POSSIBLE_DEADLOCK", + 0xc000013a : "CONTROL_C_EXIT" +}; + + +class ExceptionHandler(debugEvent): + + def onException(self, exceptData): + dprintln("\n *** shit happens") + + exceptCode = exceptData["Code"] + dprint("Exception code : ") + if exceptCode in knownExcepCodes: + dprintln( knownExcepCodes[exceptCode] ) + else: + dprintln( "0x%08x" % exceptCode ) + + dprint("Exception flags : ") + exceptFlags = exceptData["Flags"] + if exceptFlags: + if exceptFlags & NONCONTINUABLE_EXCEPTION: + exceptFlags &= ~NONCONTINUABLE_EXCEPTION + dprint( "NONCONTINUABLE " ) + if exceptFlags: + dprintln( "| 0x%02x" % exceptFlags) + else: + dprintln( "" ) + else: + dprintln( "0" ) + + dprintln("Exception record : 0x%X" % exceptData["Record"]) + + exceptAddr = exceptData["Address"] + dprintln("\nException address : 0x%X" % exceptAddr) + dprintln( dbgCommand("ln 0x%X" % exceptAddr) ) + + if len( exceptData["Parameters"] ): + dprintln("Parameters : ") + for param in exceptData["Parameters"]: + dprintln("\t0x%X" % param) + + dprintln("\nFirst chance : " + str( exceptData["FirstChance"] )) + + dprintln( "\n " + dbgCommand( "r" ) ) + dbgCommand( ".reload" ) + dprintln( dbgCommand( "kb" ) ) + + return DEBUG_STATUS_BREAK + +if __name__ == '__main__': + + if len(sys.argv) != 1: + startComamnd = "" + for i in range(1, len(sys.argv)): + startComamnd += sys.argv[i] + " " + startProcess(startComamnd) + + exceptionHandler = ExceptionHandler() + + try: + go() + + except WaitEventException: + dprintln("none of the targets could generate events") + +