# DExTer : Debugging Experience Tester # ~~~~~~ ~ ~~ ~ ~~ # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception from ctypes import * from functools import partial from .utils import * from .breakpoint import * class DEBUG_STACK_FRAME_EX(Structure): _fields_ = [ ("InstructionOffset", c_ulonglong), ("ReturnOffset", c_ulonglong), ("FrameOffset", c_ulonglong), ("StackOffset", c_ulonglong), ("FuncTableEntry", c_ulonglong), ("Params", c_ulonglong * 4), ("Reserved", c_ulonglong * 6), ("Virtual", c_bool), ("FrameNumber", c_ulong), ("InlineFrameContext", c_ulong), ("Reserved1", c_ulong) ] PDEBUG_STACK_FRAME_EX = POINTER(DEBUG_STACK_FRAME_EX) class DEBUG_VALUE_U(Union): _fields_ = [ ("I8", c_byte), ("I16", c_short), ("I32", c_int), ("I64", c_long), ("F32", c_float), ("F64", c_double), ("RawBytes", c_ubyte * 24) # Force length to 24b. ] class DEBUG_VALUE(Structure): _fields_ = [ ("U", DEBUG_VALUE_U), ("TailOfRawBytes", c_ulong), ("Type", c_ulong) ] PDEBUG_VALUE = POINTER(DEBUG_VALUE) class DebugValueType(IntEnum): DEBUG_VALUE_INVALID = 0 DEBUG_VALUE_INT8 = 1 DEBUG_VALUE_INT16 = 2 DEBUG_VALUE_INT32 = 3 DEBUG_VALUE_INT64 = 4 DEBUG_VALUE_FLOAT32 = 5 DEBUG_VALUE_FLOAT64 = 6 DEBUG_VALUE_FLOAT80 = 7 DEBUG_VALUE_FLOAT82 = 8 DEBUG_VALUE_FLOAT128 = 9 DEBUG_VALUE_VECTOR64 = 10 DEBUG_VALUE_VECTOR128 = 11 DEBUG_VALUE_TYPES = 12 # UUID for DebugControl7 interface. DebugControl7IID = IID(0xb86fb3b1, 0x80d4, 0x475b, IID_Data4_Type(0xae, 0xa3, 0xcf, 0x06, 0x53, 0x9c, 0xf6, 0x3a)) class IDebugControl7(Structure): pass class IDebugControl7Vtbl(Structure): wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugControl7)) idc_getnumbereventfilters = wrp(c_ulong_p, c_ulong_p, c_ulong_p) idc_setexceptionfiltersecondcommand = wrp(c_ulong, c_char_p) idc_waitforevent = wrp(c_long, c_long) idc_execute = wrp(c_long, c_char_p, c_long) idc_setexpressionsyntax = wrp(c_ulong) idc_addbreakpoint2 = wrp(c_ulong, c_ulong, POINTER(POINTER(DebugBreakpoint2))) idc_setexecutionstatus = wrp(c_ulong) idc_getexecutionstatus = wrp(c_ulong_p) idc_getstacktraceex = wrp(c_ulonglong, c_ulonglong, c_ulonglong, PDEBUG_STACK_FRAME_EX, c_ulong, c_ulong_p) idc_evaluate = wrp(c_char_p, c_ulong, PDEBUG_VALUE, c_ulong_p) idc_setengineoptions = wrp(c_ulong) _fields_ = [ ("QueryInterface", c_void_p), ("AddRef", c_void_p), ("Release", c_void_p), ("GetInterrupt", c_void_p), ("SetInterrupt", c_void_p), ("GetInterruptTimeout", c_void_p), ("SetInterruptTimeout", c_void_p), ("GetLogFile", c_void_p), ("OpenLogFile", c_void_p), ("CloseLogFile", c_void_p), ("GetLogMask", c_void_p), ("SetLogMask", c_void_p), ("Input", c_void_p), ("ReturnInput", c_void_p), ("Output", c_void_p), ("OutputVaList", c_void_p), ("ControlledOutput", c_void_p), ("ControlledOutputVaList", c_void_p), ("OutputPrompt", c_void_p), ("OutputPromptVaList", c_void_p), ("GetPromptText", c_void_p), ("OutputCurrentState", c_void_p), ("OutputVersionInformation", c_void_p), ("GetNotifyEventHandle", c_void_p), ("SetNotifyEventHandle", c_void_p), ("Assemble", c_void_p), ("Disassemble", c_void_p), ("GetDisassembleEffectiveOffset", c_void_p), ("OutputDisassembly", c_void_p), ("OutputDisassemblyLines", c_void_p), ("GetNearInstruction", c_void_p), ("GetStackTrace", c_void_p), ("GetReturnOffset", c_void_p), ("OutputStackTrace", c_void_p), ("GetDebuggeeType", c_void_p), ("GetActualProcessorType", c_void_p), ("GetExecutingProcessorType", c_void_p), ("GetNumberPossibleExecutingProcessorTypes", c_void_p), ("GetPossibleExecutingProcessorTypes", c_void_p), ("GetNumberProcessors", c_void_p), ("GetSystemVersion", c_void_p), ("GetPageSize", c_void_p), ("IsPointer64Bit", c_void_p), ("ReadBugCheckData", c_void_p), ("GetNumberSupportedProcessorTypes", c_void_p), ("GetSupportedProcessorTypes", c_void_p), ("GetProcessorTypeNames", c_void_p), ("GetEffectiveProcessorType", c_void_p), ("SetEffectiveProcessorType", c_void_p), ("GetExecutionStatus", idc_getexecutionstatus), ("SetExecutionStatus", idc_setexecutionstatus), ("GetCodeLevel", c_void_p), ("SetCodeLevel", c_void_p), ("GetEngineOptions", c_void_p), ("AddEngineOptions", c_void_p), ("RemoveEngineOptions", c_void_p), ("SetEngineOptions", idc_setengineoptions), ("GetSystemErrorControl", c_void_p), ("SetSystemErrorControl", c_void_p), ("GetTextMacro", c_void_p), ("SetTextMacro", c_void_p), ("GetRadix", c_void_p), ("SetRadix", c_void_p), ("Evaluate", idc_evaluate), ("CoerceValue", c_void_p), ("CoerceValues", c_void_p), ("Execute", idc_execute), ("ExecuteCommandFile", c_void_p), ("GetNumberBreakpoints", c_void_p), ("GetBreakpointByIndex", c_void_p), ("GetBreakpointById", c_void_p), ("GetBreakpointParameters", c_void_p), ("AddBreakpoint", c_void_p), ("RemoveBreakpoint", c_void_p), ("AddExtension", c_void_p), ("RemoveExtension", c_void_p), ("GetExtensionByPath", c_void_p), ("CallExtension", c_void_p), ("GetExtensionFunction", c_void_p), ("GetWindbgExtensionApis32", c_void_p), ("GetWindbgExtensionApis64", c_void_p), ("GetNumberEventFilters", idc_getnumbereventfilters), ("GetEventFilterText", c_void_p), ("GetEventFilterCommand", c_void_p), ("SetEventFilterCommand", c_void_p), ("GetSpecificFilterParameters", c_void_p), ("SetSpecificFilterParameters", c_void_p), ("GetSpecificFilterArgument", c_void_p), ("SetSpecificFilterArgument", c_void_p), ("GetExceptionFilterParameters", c_void_p), ("SetExceptionFilterParameters", c_void_p), ("GetExceptionFilterSecondCommand", c_void_p), ("SetExceptionFilterSecondCommand", idc_setexceptionfiltersecondcommand), ("WaitForEvent", idc_waitforevent), ("GetLastEventInformation", c_void_p), ("GetCurrentTimeDate", c_void_p), ("GetCurrentSystemUpTime", c_void_p), ("GetDumpFormatFlags", c_void_p), ("GetNumberTextReplacements", c_void_p), ("GetTextReplacement", c_void_p), ("SetTextReplacement", c_void_p), ("RemoveTextReplacements", c_void_p), ("OutputTextReplacements", c_void_p), ("GetAssemblyOptions", c_void_p), ("AddAssemblyOptions", c_void_p), ("RemoveAssemblyOptions", c_void_p), ("SetAssemblyOptions", c_void_p), ("GetExpressionSyntax", c_void_p), ("SetExpressionSyntax", idc_setexpressionsyntax), ("SetExpressionSyntaxByName", c_void_p), ("GetNumberExpressionSyntaxes", c_void_p), ("GetExpressionSyntaxNames", c_void_p), ("GetNumberEvents", c_void_p), ("GetEventIndexDescription", c_void_p), ("GetCurrentEventIndex", c_void_p), ("SetNextEventIndex", c_void_p), ("GetLogFileWide", c_void_p), ("OpenLogFileWide", c_void_p), ("InputWide", c_void_p), ("ReturnInputWide", c_void_p), ("OutputWide", c_void_p), ("OutputVaListWide", c_void_p), ("ControlledOutputWide", c_void_p), ("ControlledOutputVaListWide", c_void_p), ("OutputPromptWide", c_void_p), ("OutputPromptVaListWide", c_void_p), ("GetPromptTextWide", c_void_p), ("AssembleWide", c_void_p), ("DisassembleWide", c_void_p), ("GetProcessrTypeNamesWide", c_void_p), ("GetTextMacroWide", c_void_p), ("SetTextMacroWide", c_void_p), ("EvaluateWide", c_void_p), ("ExecuteWide", c_void_p), ("ExecuteCommandFileWide", c_void_p), ("GetBreakpointByIndex2", c_void_p), ("GetBreakpointById2", c_void_p), ("AddBreakpoint2", idc_addbreakpoint2), ("RemoveBreakpoint2", c_void_p), ("AddExtensionWide", c_void_p), ("GetExtensionByPathWide", c_void_p), ("CallExtensionWide", c_void_p), ("GetExtensionFunctionWide", c_void_p), ("GetEventFilterTextWide", c_void_p), ("GetEventfilterCommandWide", c_void_p), ("SetEventFilterCommandWide", c_void_p), ("GetSpecificFilterArgumentWide", c_void_p), ("SetSpecificFilterArgumentWide", c_void_p), ("GetExceptionFilterSecondCommandWide", c_void_p), ("SetExceptionFilterSecondCommandWider", c_void_p), ("GetLastEventInformationWide", c_void_p), ("GetTextReplacementWide", c_void_p), ("SetTextReplacementWide", c_void_p), ("SetExpressionSyntaxByNameWide", c_void_p), ("GetExpressionSyntaxNamesWide", c_void_p), ("GetEventIndexDescriptionWide", c_void_p), ("GetLogFile2", c_void_p), ("OpenLogFile2", c_void_p), ("GetLogFile2Wide", c_void_p), ("OpenLogFile2Wide", c_void_p), ("GetSystemVersionValues", c_void_p), ("GetSystemVersionString", c_void_p), ("GetSystemVersionStringWide", c_void_p), ("GetContextStackTrace", c_void_p), ("OutputContextStackTrace", c_void_p), ("GetStoredEventInformation", c_void_p), ("GetManagedStatus", c_void_p), ("GetManagedStatusWide", c_void_p), ("ResetManagedStatus", c_void_p), ("GetStackTraceEx", idc_getstacktraceex), ("OutputStackTraceEx", c_void_p), ("GetContextStackTraceEx", c_void_p), ("OutputContextStackTraceEx", c_void_p), ("GetBreakpointByGuid", c_void_p), ("GetExecutionStatusEx", c_void_p), ("GetSynchronizationStatus", c_void_p), ("GetDebuggeeType2", c_void_p) ] IDebugControl7._fields_ = [("lpVtbl", POINTER(IDebugControl7Vtbl))] class DebugStatus(IntEnum): DEBUG_STATUS_NO_CHANGE = 0 DEBUG_STATUS_GO = 1 DEBUG_STATUS_GO_HANDLED = 2 DEBUG_STATUS_GO_NOT_HANDLED = 3 DEBUG_STATUS_STEP_OVER = 4 DEBUG_STATUS_STEP_INTO = 5 DEBUG_STATUS_BREAK = 6 DEBUG_STATUS_NO_DEBUGGEE = 7 DEBUG_STATUS_STEP_BRANCH = 8 DEBUG_STATUS_IGNORE_EVENT = 9 DEBUG_STATUS_RESTART_REQUESTED = 10 DEBUG_STATUS_REVERSE_GO = 11 DEBUG_STATUS_REVERSE_STEP_BRANCH = 12 DEBUG_STATUS_REVERSE_STEP_OVER = 13 DEBUG_STATUS_REVERSE_STEP_INTO = 14 DEBUG_STATUS_OUT_OF_SYNC = 15 DEBUG_STATUS_WAIT_INPUT = 16 DEBUG_STATUS_TIMEOUT = 17 class DebugSyntax(IntEnum): DEBUG_EXPR_MASM = 0 DEBUG_EXPR_CPLUSPLUS = 1 class Control(object): def __init__(self, control): self.ptr = control self.control = control.contents self.vt = self.control.lpVtbl.contents # Keep a handy ulong for passing into C methods. self.ulong = c_ulong() def GetExecutionStatus(self, doprint=False): ret = self.vt.GetExecutionStatus(self.control, byref(self.ulong)) aborter(ret, "GetExecutionStatus") status = DebugStatus(self.ulong.value) if doprint: print("Execution status: {}".format(status)) return status def SetExecutionStatus(self, status): assert isinstance(status, DebugStatus) res = self.vt.SetExecutionStatus(self.control, status.value) aborter(res, "SetExecutionStatus") def WaitForEvent(self, timeout=100): # No flags are taken by WaitForEvent, hence 0 ret = self.vt.WaitForEvent(self.control, 0, timeout) aborter(ret, "WaitforEvent", ignore=[S_FALSE]) return ret def GetNumberEventFilters(self): specific_events = c_ulong() specific_exceptions = c_ulong() arbitrary_exceptions = c_ulong() res = self.vt.GetNumberEventFilters(self.control, byref(specific_events), byref(specific_exceptions), byref(arbitrary_exceptions)) aborter(res, "GetNumberEventFilters") return (specific_events.value, specific_exceptions.value, arbitrary_exceptions.value) def SetExceptionFilterSecondCommand(self, index, command): buf = create_string_buffer(command.encode('ascii')) res = self.vt.SetExceptionFilterSecondCommand(self.control, index, buf) aborter(res, "SetExceptionFilterSecondCommand") return def AddBreakpoint2(self, offset=None, enabled=None): breakpoint = POINTER(DebugBreakpoint2)() res = self.vt.AddBreakpoint2(self.control, BreakpointTypes.DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, byref(breakpoint)) aborter(res, "Add breakpoint 2") bp = Breakpoint(breakpoint) if offset is not None: bp.SetOffset(offset) if enabled is not None and enabled: bp.SetFlags(BreakpointFlags.DEBUG_BREAKPOINT_ENABLED) return bp def RemoveBreakpoint(self, bp): res = self.vt.RemoveBreakpoint2(self.control, bp.breakpoint) aborter(res, "RemoveBreakpoint2") bp.die() def GetStackTraceEx(self): # XXX -- I can't find a way to query for how many stack frames there _are_ # in advance. Guess 128 for now. num_frames_buffer = 128 frames = (DEBUG_STACK_FRAME_EX * num_frames_buffer)() numframes = c_ulong() # First three args are frame/stack/IP offsets -- leave them as zero to # default to the current instruction. res = self.vt.GetStackTraceEx(self.control, 0, 0, 0, frames, num_frames_buffer, byref(numframes)) aborter(res, "GetStackTraceEx") return frames, numframes.value def Execute(self, command): # First zero is DEBUG_OUTCTL_*, which we leave as a default, second # zero is DEBUG_EXECUTE_* flags, of which we set none. res = self.vt.Execute(self.control, 0, command.encode('ascii'), 0) aborter(res, "Client execute") def SetExpressionSyntax(self, cpp=True): if cpp: syntax = DebugSyntax.DEBUG_EXPR_CPLUSPLUS else: syntax = DebugSyntax.DEBUG_EXPR_MASM res = self.vt.SetExpressionSyntax(self.control, syntax) aborter(res, "SetExpressionSyntax") def Evaluate(self, expr): ptr = DEBUG_VALUE() res = self.vt.Evaluate(self.control, expr.encode("ascii"), DebugValueType.DEBUG_VALUE_INVALID, byref(ptr), None) aborter(res, "Evaluate", ignore=[E_INTERNALEXCEPTION, E_FAIL]) if res != 0: return None val_type = DebugValueType(ptr.Type) # Here's a map from debug value types to fields. Unclear what happens # with unsigned values, as DbgEng doesn't present any unsigned fields. extract_map = { DebugValueType.DEBUG_VALUE_INT8 : ("I8", "char"), DebugValueType.DEBUG_VALUE_INT16 : ("I16", "short"), DebugValueType.DEBUG_VALUE_INT32 : ("I32", "int"), DebugValueType.DEBUG_VALUE_INT64 : ("I64", "long"), DebugValueType.DEBUG_VALUE_FLOAT32 : ("F32", "float"), DebugValueType.DEBUG_VALUE_FLOAT64 : ("F64", "double") } # And everything else is invalid. if val_type not in extract_map: raise Exception("Unexpected debug value type {} when evalutaing".format(val_type)) # Also produce a type name... return getattr(ptr.U, extract_map[val_type][0]), extract_map[val_type][1] def SetEngineOptions(self, opt): res = self.vt.SetEngineOptions(self.control, opt) aborter(res, "SetEngineOptions") return