diff --git a/pykd/bpoint.cpp b/pykd/bpoint.cpp new file mode 100644 index 0000000..06b1f93 --- /dev/null +++ b/pykd/bpoint.cpp @@ -0,0 +1,157 @@ +// +// Breakpoints management +// + +#include "stdafx.h" +#include "bpoint.h" + +//////////////////////////////////////////////////////////////////////////////// + +namespace pykd { + +static IDebugBreakpoint *setBreakPoint( + IDebugControl4 *control, + ULONG bpType, + ULONG64 addr +) +{ + IDebugBreakpoint *bp; + HRESULT hres = control->AddBreakpoint(bpType, DEBUG_ANY_ID, &bp); + if (S_OK != hres) + throw DbgException("IDebugControl::AddBreakpoint", hres); + + hres = bp->SetOffset(addr); + if (S_OK != hres) + { + control->RemoveBreakpoint(bp); + throw DbgException("IDebugBreakpoint::SetOffset", hres); + } + + ULONG bpFlags; + hres = bp->GetFlags(&bpFlags); + if (S_OK != hres) + { + control->RemoveBreakpoint(bp); + throw DbgException("IDebugBreakpoint::GetFlags", hres); + } + + bpFlags |= DEBUG_BREAKPOINT_ENABLED; + hres = bp->SetFlags(bpFlags); + if (S_OK != hres) + { + control->RemoveBreakpoint(bp); + throw DbgException("IDebugBreakpoint::SetFlags", hres); + } + + return bp; +} + +//////////////////////////////////////////////////////////////////////////////// + +static ULONG getBpId(IDebugBreakpoint *bp, IDebugControl4 *control = NULL) +{ + ULONG Id; + HRESULT hres = bp->GetId(&Id); + if (S_OK != hres) + { + if (control) + control->RemoveBreakpoint(bp); + throw DbgException("IDebugBreakpoint::GetId", hres); + } + return Id; +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG DebugClient::setSoftwareBp(ULONG64 addr) +{ + addr = addr64(addr); + IDebugBreakpoint *bp = setBreakPoint(m_control, DEBUG_BREAKPOINT_CODE, addr); + + return getBpId(bp, m_control); +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG DebugClient::setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType) +{ + addr = addr64(addr); + IDebugBreakpoint *bp = setBreakPoint(m_control, DEBUG_BREAKPOINT_DATA, addr); + + HRESULT hres = bp->SetDataParameters(size, accessType); + if (S_OK != hres) + { + m_control->RemoveBreakpoint(bp); + throw DbgException("IDebugBreakpoint::SetDataParameters", hres); + } + + return getBpId(bp, m_control); +} + +//////////////////////////////////////////////////////////////////////////////// + +python::list DebugClient::getAllBp() +{ + ULONG numberOfBps; + HRESULT hres = m_control->GetNumberBreakpoints(&numberOfBps); + if (S_OK != hres) + throw DbgException("IDebugControl::GetNumberBreakpoints", hres); + + python::list lstIds; + + for (ULONG i =0; i < numberOfBps; ++i) + { + IDebugBreakpoint *bp; + hres = m_control->GetBreakpointByIndex(i, &bp); + if (S_OK != hres) + throw DbgException("IDebugControl::GetBreakpointByIndex", hres); + lstIds.append( getBpId(bp) ); + } + + return lstIds; +} + +//////////////////////////////////////////////////////////////////////////////// + +void DebugClient::removeBp(ULONG Id) +{ + IDebugBreakpoint *bp; + HRESULT hres = m_control->GetBreakpointById(Id, &bp); + if (S_OK != hres) + throw DbgException("IDebugControl::GetBreakpointById", hres); + + hres = m_control->RemoveBreakpoint(bp); + if (S_OK != hres) + throw DbgException("IDebugControl::RemoveBreakpoint", hres); +} + +//////////////////////////////////////////////////////////////////////////////// + +void DebugClient::removeAllBp() +{ + ULONG numberOfBps; + do { + HRESULT hres = m_control->GetNumberBreakpoints(&numberOfBps); + if (S_OK != hres) + throw DbgException("IDebugControl::GetNumberBreakpoints", hres); + if (!numberOfBps) + break; + + IDebugBreakpoint *bp; + hres = m_control->GetBreakpointByIndex(0, &bp); + if (S_OK != hres) + throw DbgException("IDebugControl::GetBreakpointByIndex", hres); + + hres = m_control->RemoveBreakpoint(bp); + if (S_OK != hres) + throw DbgException("IDebugControl::RemoveBreakpoint", hres); + + } while (numberOfBps); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace pykd + + +//////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/bpoint.h b/pykd/bpoint.h new file mode 100644 index 0000000..c987a31 --- /dev/null +++ b/pykd/bpoint.h @@ -0,0 +1,46 @@ +// +// Breakpoints management +// + +#include "dbgclient.h" + +//////////////////////////////////////////////////////////////////////////////// + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +inline ULONG setSoftwareBp(ULONG64 addr) { + return g_dbgClient->setSoftwareBp(addr); +} + +//////////////////////////////////////////////////////////////////////////////// + +inline ULONG setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType) { + return g_dbgClient->setHardwareBp(addr, size, accessType); +} + +//////////////////////////////////////////////////////////////////////////////// + +inline python::list getAllBp() { + return g_dbgClient->getAllBp(); +} + +//////////////////////////////////////////////////////////////////////////////// + +inline void removeBp(ULONG Id) { + return g_dbgClient->removeBp(Id); +} + +//////////////////////////////////////////////////////////////////////////////// + +inline void removeAllBp() { + return g_dbgClient->removeAllBp(); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace pykd + +//////////////////////////////////////////////////////////////////////////////// + diff --git a/pykd/dbgclient.h b/pykd/dbgclient.h index 9282556..3aacc80 100644 --- a/pykd/dbgclient.h +++ b/pykd/dbgclient.h @@ -297,6 +297,15 @@ public: return m_symSymbols; } + // breakpoints management + ULONG setSoftwareBp(ULONG64 addr); + ULONG setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType); + + python::list getAllBp(); + + void removeBp(ULONG Id); + void removeAllBp(); + private: template<typename T> python::list diff --git a/pykd/dbgext.cpp b/pykd/dbgext.cpp index e0ff237..5ba7123 100644 --- a/pykd/dbgext.cpp +++ b/pykd/dbgext.cpp @@ -18,6 +18,7 @@ #include "dbgmem.h" #include "intbase.h" #include "process.h" +#include "bpoint.h" #include "pykdver.h" using namespace pykd; @@ -284,7 +285,7 @@ BOOST_PYTHON_MODULE( pykd ) .def( "step", &DebugClient::changeDebuggerStatus<DEBUG_STATUS_STEP_OVER>, "Change debugger status to DEBUG_STATUS_STEP_OVER" ) .def( "symbolsPath", &DebugClient::dbgSymPath, - "Return symbol path" ) + "Return symbol path" ) .def( "trace", &DebugClient::changeDebuggerStatus<DEBUG_STATUS_STEP_INTO>, "Change debugger status to DEBUG_STATUS_STEP_INTO" ) .def( "waitForEvent", &DebugClient::waitForEvent, @@ -299,6 +300,16 @@ BOOST_PYTHON_MODULE( pykd ) "Get context of current thread (register values)" ) .def( "getLocals", &DebugClient::getLocals, DebugClient_getLocals( python::args( "ctx" ), "Get list of local variables" ) ) + .def( "setBp", &DebugClient::setSoftwareBp, + "Set software breakpoint on executiont" ) + .def( "setBp", &DebugClient::setHardwareBp, + "Set hardware breakpoint" ) + .def( "getAllBp", &DebugClient::getAllBp, + "Get all breapoint IDs" ) + .def( "removeBp", &DebugClient::removeBp, + "Remove breapoint by IDs" ) + .def( "removeBp", &DebugClient::removeAllBp, + "Remove all breapoints" ) .def( "addSynSymbol", &DebugClient::addSyntheticSymbol, "Add new synthetic symbol for virtual address" ) .def( "delAllSynSymbols", &DebugClient::delAllSyntheticSymbols, @@ -473,7 +484,17 @@ BOOST_PYTHON_MODULE( pykd ) python::def( "getContext", &getThreadContext, "Get context of current thread (register values)" ); python::def( "getLocals", &getLocals, getLocals_( python::args( "ctx" ), - "Get list of local variables" ) ); + "Get list of local variables" ) ); + python::def( "setBp", &setSoftwareBp, + "Set software breakpoint on executiont" ); + python::def( "setBp", &setHardwareBp, + "Set hardware breakpoint" ); + python::def( "getAllBp", &getAllBp, + "Get all breapoint IDs" ); + python::def( "removeBp", &removeBp, + "Remove breapoint by IDs" ); + python::def( "removeBp", &removeAllBp, + "Remove all breapoints" ); python::class_<TypeInfo, TypeInfoPtr, python::bases<intBase>, boost::noncopyable >("typeInfo", "Class representing typeInfo", python::no_init ) .def( "name", &TypeInfo::getName ) @@ -872,6 +893,15 @@ BOOST_PYTHON_MODULE( pykd ) DEF_PY_CONST_ULONG(DEBUG_STATUS_REVERSE_STEP_BRANCH); DEF_PY_CONST_ULONG(DEBUG_STATUS_REVERSE_STEP_OVER); DEF_PY_CONST_ULONG(DEBUG_STATUS_REVERSE_STEP_INTO); + + // breakpoints constatns + DEF_PY_CONST_ULONG(DEBUG_BREAKPOINT_CODE); + DEF_PY_CONST_ULONG(DEBUG_BREAKPOINT_DATA); + + DEF_PY_CONST_ULONG(DEBUG_BREAK_READ); + DEF_PY_CONST_ULONG(DEBUG_BREAK_WRITE); + DEF_PY_CONST_ULONG(DEBUG_BREAK_EXECUTE); + DEF_PY_CONST_ULONG(DEBUG_BREAK_IO); } #undef DEF_PY_CONST_ULONG diff --git a/pykd/pykd_2008.vcproj b/pykd/pykd_2008.vcproj index 9ae61f6..86fddea 100644 --- a/pykd/pykd_2008.vcproj +++ b/pykd/pykd_2008.vcproj @@ -349,6 +349,10 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + <File + RelativePath=".\bpoint.cpp" + > + </File> <File RelativePath=".\context.cpp" > @@ -483,6 +487,10 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + <File + RelativePath=".\bpoint.h" + > + </File> <File RelativePath=".\context.h" > diff --git a/test/scripts/ehexcepttest.py b/test/scripts/ehexcepttest.py index 8d8cb52..fc9af07 100644 --- a/test/scripts/ehexcepttest.py +++ b/test/scripts/ehexcepttest.py @@ -14,6 +14,7 @@ class BreakExceptionHandler(pykd.eventHandler): self.wasSecondChance = False self.wasBreakpoint = False + self.wasBreakpointIds = [] self.bpLastModuleName = "" self.bpCount = 0 @@ -24,6 +25,7 @@ class BreakExceptionHandler(pykd.eventHandler): def onBreakpoint(self, bpId): """Breakpoint handler""" self.wasBreakpoint = True + self.wasBreakpointIds.append( bpId ) return pykd.DEBUG_STATUS_NO_CHANGE def onException(self, exceptParams): @@ -33,7 +35,6 @@ class BreakExceptionHandler(pykd.eventHandler): if exceptParams["Code"] == pykd.EXCEPTION_ACCESS_VIOLATION: if self.wasSecondChance: - print exceptParams["Parameters"] self.secondChanceAccessAddresses.append( exceptParams["Parameters"][1] ) else: self.firstChanceAccessAddresses.append( exceptParams["Parameters"][1] ) @@ -50,7 +51,23 @@ class EhExceptionBreakpointTest(unittest.TestCase): testClient = pykd.createDbgClient() testClient.startProcess( target.appPath + " -testExceptions" ) - testClient.dbgCommand( ".reload /f; bp targetapp!doExeptions" ) + targetMod = testClient.loadModule( "targetapp" ) + + bpIdSoftware = testClient.setBp( targetMod.offset("changeValueForAccessTesting") ) + + bpIdHwExecute = testClient.setBp( targetMod.offset("readValueForAccessTesting"), + 1, + pykd.DEBUG_BREAK_EXECUTE ) + + bpIdHwWrite = testClient.setBp( targetMod.offset("g_valueForAccessTesting1"), + 1, + pykd.DEBUG_BREAK_WRITE ) + + bpIdHwRead = testClient.setBp( targetMod.offset("g_valueForAccessTesting2"), + 1, + pykd.DEBUG_BREAK_READ ) + + self.assertEqual( 4, len( testClient.getAllBp() ) ) breakExceptionHandler = BreakExceptionHandler( testClient ) while not breakExceptionHandler.wasSecondChance: @@ -63,3 +80,11 @@ class EhExceptionBreakpointTest(unittest.TestCase): self.assertEqual( 2, breakExceptionHandler.bpCount ) # main and doExeptions self.assertEqual( [3, ], breakExceptionHandler.secondChanceAccessAddresses ) + + self.assertTrue( bpIdSoftware in breakExceptionHandler.wasBreakpointIds ) + + testClient.removeBp(bpIdHwRead) + self.assertEqual( 3, len( testClient.getAllBp() ) ) + + testClient.removeBp() + self.assertEqual( 0, len( testClient.getAllBp() ) ) diff --git a/test/targetapp/targetapp.cpp b/test/targetapp/targetapp.cpp index d7b76ba..04e7721 100644 --- a/test/targetapp/targetapp.cpp +++ b/test/targetapp/targetapp.cpp @@ -343,10 +343,34 @@ int doLoadUnload() return 0; } + +//////////////////////////////////////////////////////////////////////////////// + +int g_valueForAccessTesting1 = 4; +int g_valueForAccessTesting2 = 5; + +//////////////////////////////////////////////////////////////////////////////// +#pragma optimize("g", off) +void changeValueForAccessTesting() +{ + g_valueForAccessTesting1 = 5; +} + +//////////////////////////////////////////////////////////////////////////////// + +void readValueForAccessTesting() +{ + std::cout << g_valueForAccessTesting1 << g_valueForAccessTesting2; +} +#pragma optimize("g", on) //////////////////////////////////////////////////////////////////////////////// int doExeptions() { + changeValueForAccessTesting(); + + readValueForAccessTesting(); + __debugbreak(); PUCHAR _ptr = reinterpret_cast<UCHAR *>(2);