451 lines · python
1# DExTer : Debugging Experience Tester2# ~~~~~~ ~ ~~ ~ ~~3#4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5# See https://llvm.org/LICENSE.txt for license information.6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7 8from ctypes import *9from functools import partial10 11from .utils import *12from .breakpoint import *13 14 15class DEBUG_STACK_FRAME_EX(Structure):16 _fields_ = [17 ("InstructionOffset", c_ulonglong),18 ("ReturnOffset", c_ulonglong),19 ("FrameOffset", c_ulonglong),20 ("StackOffset", c_ulonglong),21 ("FuncTableEntry", c_ulonglong),22 ("Params", c_ulonglong * 4),23 ("Reserved", c_ulonglong * 6),24 ("Virtual", c_bool),25 ("FrameNumber", c_ulong),26 ("InlineFrameContext", c_ulong),27 ("Reserved1", c_ulong),28 ]29 30 31PDEBUG_STACK_FRAME_EX = POINTER(DEBUG_STACK_FRAME_EX)32 33 34class DEBUG_VALUE_U(Union):35 _fields_ = [36 ("I8", c_byte),37 ("I16", c_short),38 ("I32", c_int),39 ("I64", c_long),40 ("F32", c_float),41 ("F64", c_double),42 ("RawBytes", c_ubyte * 24), # Force length to 24b.43 ]44 45 46class DEBUG_VALUE(Structure):47 _fields_ = [("U", DEBUG_VALUE_U), ("TailOfRawBytes", c_ulong), ("Type", c_ulong)]48 49 50PDEBUG_VALUE = POINTER(DEBUG_VALUE)51 52 53class DebugValueType(IntEnum):54 DEBUG_VALUE_INVALID = 055 DEBUG_VALUE_INT8 = 156 DEBUG_VALUE_INT16 = 257 DEBUG_VALUE_INT32 = 358 DEBUG_VALUE_INT64 = 459 DEBUG_VALUE_FLOAT32 = 560 DEBUG_VALUE_FLOAT64 = 661 DEBUG_VALUE_FLOAT80 = 762 DEBUG_VALUE_FLOAT82 = 863 DEBUG_VALUE_FLOAT128 = 964 DEBUG_VALUE_VECTOR64 = 1065 DEBUG_VALUE_VECTOR128 = 1166 DEBUG_VALUE_TYPES = 1267 68 69# UUID for DebugControl7 interface.70DebugControl7IID = IID(71 0xB86FB3B1,72 0x80D4,73 0x475B,74 IID_Data4_Type(0xAE, 0xA3, 0xCF, 0x06, 0x53, 0x9C, 0xF6, 0x3A),75)76 77 78class IDebugControl7(Structure):79 pass80 81 82class IDebugControl7Vtbl(Structure):83 wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugControl7))84 idc_getnumbereventfilters = wrp(c_ulong_p, c_ulong_p, c_ulong_p)85 idc_setexceptionfiltersecondcommand = wrp(c_ulong, c_char_p)86 idc_waitforevent = wrp(c_long, c_long)87 idc_execute = wrp(c_long, c_char_p, c_long)88 idc_setexpressionsyntax = wrp(c_ulong)89 idc_addbreakpoint2 = wrp(c_ulong, c_ulong, POINTER(POINTER(DebugBreakpoint2)))90 idc_setexecutionstatus = wrp(c_ulong)91 idc_getexecutionstatus = wrp(c_ulong_p)92 idc_getstacktraceex = wrp(93 c_ulonglong, c_ulonglong, c_ulonglong, PDEBUG_STACK_FRAME_EX, c_ulong, c_ulong_p94 )95 idc_evaluate = wrp(c_char_p, c_ulong, PDEBUG_VALUE, c_ulong_p)96 idc_setengineoptions = wrp(c_ulong)97 _fields_ = [98 ("QueryInterface", c_void_p),99 ("AddRef", c_void_p),100 ("Release", c_void_p),101 ("GetInterrupt", c_void_p),102 ("SetInterrupt", c_void_p),103 ("GetInterruptTimeout", c_void_p),104 ("SetInterruptTimeout", c_void_p),105 ("GetLogFile", c_void_p),106 ("OpenLogFile", c_void_p),107 ("CloseLogFile", c_void_p),108 ("GetLogMask", c_void_p),109 ("SetLogMask", c_void_p),110 ("Input", c_void_p),111 ("ReturnInput", c_void_p),112 ("Output", c_void_p),113 ("OutputVaList", c_void_p),114 ("ControlledOutput", c_void_p),115 ("ControlledOutputVaList", c_void_p),116 ("OutputPrompt", c_void_p),117 ("OutputPromptVaList", c_void_p),118 ("GetPromptText", c_void_p),119 ("OutputCurrentState", c_void_p),120 ("OutputVersionInformation", c_void_p),121 ("GetNotifyEventHandle", c_void_p),122 ("SetNotifyEventHandle", c_void_p),123 ("Assemble", c_void_p),124 ("Disassemble", c_void_p),125 ("GetDisassembleEffectiveOffset", c_void_p),126 ("OutputDisassembly", c_void_p),127 ("OutputDisassemblyLines", c_void_p),128 ("GetNearInstruction", c_void_p),129 ("GetStackTrace", c_void_p),130 ("GetReturnOffset", c_void_p),131 ("OutputStackTrace", c_void_p),132 ("GetDebuggeeType", c_void_p),133 ("GetActualProcessorType", c_void_p),134 ("GetExecutingProcessorType", c_void_p),135 ("GetNumberPossibleExecutingProcessorTypes", c_void_p),136 ("GetPossibleExecutingProcessorTypes", c_void_p),137 ("GetNumberProcessors", c_void_p),138 ("GetSystemVersion", c_void_p),139 ("GetPageSize", c_void_p),140 ("IsPointer64Bit", c_void_p),141 ("ReadBugCheckData", c_void_p),142 ("GetNumberSupportedProcessorTypes", c_void_p),143 ("GetSupportedProcessorTypes", c_void_p),144 ("GetProcessorTypeNames", c_void_p),145 ("GetEffectiveProcessorType", c_void_p),146 ("SetEffectiveProcessorType", c_void_p),147 ("GetExecutionStatus", idc_getexecutionstatus),148 ("SetExecutionStatus", idc_setexecutionstatus),149 ("GetCodeLevel", c_void_p),150 ("SetCodeLevel", c_void_p),151 ("GetEngineOptions", c_void_p),152 ("AddEngineOptions", c_void_p),153 ("RemoveEngineOptions", c_void_p),154 ("SetEngineOptions", idc_setengineoptions),155 ("GetSystemErrorControl", c_void_p),156 ("SetSystemErrorControl", c_void_p),157 ("GetTextMacro", c_void_p),158 ("SetTextMacro", c_void_p),159 ("GetRadix", c_void_p),160 ("SetRadix", c_void_p),161 ("Evaluate", idc_evaluate),162 ("CoerceValue", c_void_p),163 ("CoerceValues", c_void_p),164 ("Execute", idc_execute),165 ("ExecuteCommandFile", c_void_p),166 ("GetNumberBreakpoints", c_void_p),167 ("GetBreakpointByIndex", c_void_p),168 ("GetBreakpointById", c_void_p),169 ("GetBreakpointParameters", c_void_p),170 ("AddBreakpoint", c_void_p),171 ("RemoveBreakpoint", c_void_p),172 ("AddExtension", c_void_p),173 ("RemoveExtension", c_void_p),174 ("GetExtensionByPath", c_void_p),175 ("CallExtension", c_void_p),176 ("GetExtensionFunction", c_void_p),177 ("GetWindbgExtensionApis32", c_void_p),178 ("GetWindbgExtensionApis64", c_void_p),179 ("GetNumberEventFilters", idc_getnumbereventfilters),180 ("GetEventFilterText", c_void_p),181 ("GetEventFilterCommand", c_void_p),182 ("SetEventFilterCommand", c_void_p),183 ("GetSpecificFilterParameters", c_void_p),184 ("SetSpecificFilterParameters", c_void_p),185 ("GetSpecificFilterArgument", c_void_p),186 ("SetSpecificFilterArgument", c_void_p),187 ("GetExceptionFilterParameters", c_void_p),188 ("SetExceptionFilterParameters", c_void_p),189 ("GetExceptionFilterSecondCommand", c_void_p),190 ("SetExceptionFilterSecondCommand", idc_setexceptionfiltersecondcommand),191 ("WaitForEvent", idc_waitforevent),192 ("GetLastEventInformation", c_void_p),193 ("GetCurrentTimeDate", c_void_p),194 ("GetCurrentSystemUpTime", c_void_p),195 ("GetDumpFormatFlags", c_void_p),196 ("GetNumberTextReplacements", c_void_p),197 ("GetTextReplacement", c_void_p),198 ("SetTextReplacement", c_void_p),199 ("RemoveTextReplacements", c_void_p),200 ("OutputTextReplacements", c_void_p),201 ("GetAssemblyOptions", c_void_p),202 ("AddAssemblyOptions", c_void_p),203 ("RemoveAssemblyOptions", c_void_p),204 ("SetAssemblyOptions", c_void_p),205 ("GetExpressionSyntax", c_void_p),206 ("SetExpressionSyntax", idc_setexpressionsyntax),207 ("SetExpressionSyntaxByName", c_void_p),208 ("GetNumberExpressionSyntaxes", c_void_p),209 ("GetExpressionSyntaxNames", c_void_p),210 ("GetNumberEvents", c_void_p),211 ("GetEventIndexDescription", c_void_p),212 ("GetCurrentEventIndex", c_void_p),213 ("SetNextEventIndex", c_void_p),214 ("GetLogFileWide", c_void_p),215 ("OpenLogFileWide", c_void_p),216 ("InputWide", c_void_p),217 ("ReturnInputWide", c_void_p),218 ("OutputWide", c_void_p),219 ("OutputVaListWide", c_void_p),220 ("ControlledOutputWide", c_void_p),221 ("ControlledOutputVaListWide", c_void_p),222 ("OutputPromptWide", c_void_p),223 ("OutputPromptVaListWide", c_void_p),224 ("GetPromptTextWide", c_void_p),225 ("AssembleWide", c_void_p),226 ("DisassembleWide", c_void_p),227 ("GetProcessrTypeNamesWide", c_void_p),228 ("GetTextMacroWide", c_void_p),229 ("SetTextMacroWide", c_void_p),230 ("EvaluateWide", c_void_p),231 ("ExecuteWide", c_void_p),232 ("ExecuteCommandFileWide", c_void_p),233 ("GetBreakpointByIndex2", c_void_p),234 ("GetBreakpointById2", c_void_p),235 ("AddBreakpoint2", idc_addbreakpoint2),236 ("RemoveBreakpoint2", c_void_p),237 ("AddExtensionWide", c_void_p),238 ("GetExtensionByPathWide", c_void_p),239 ("CallExtensionWide", c_void_p),240 ("GetExtensionFunctionWide", c_void_p),241 ("GetEventFilterTextWide", c_void_p),242 ("GetEventfilterCommandWide", c_void_p),243 ("SetEventFilterCommandWide", c_void_p),244 ("GetSpecificFilterArgumentWide", c_void_p),245 ("SetSpecificFilterArgumentWide", c_void_p),246 ("GetExceptionFilterSecondCommandWide", c_void_p),247 ("SetExceptionFilterSecondCommandWider", c_void_p),248 ("GetLastEventInformationWide", c_void_p),249 ("GetTextReplacementWide", c_void_p),250 ("SetTextReplacementWide", c_void_p),251 ("SetExpressionSyntaxByNameWide", c_void_p),252 ("GetExpressionSyntaxNamesWide", c_void_p),253 ("GetEventIndexDescriptionWide", c_void_p),254 ("GetLogFile2", c_void_p),255 ("OpenLogFile2", c_void_p),256 ("GetLogFile2Wide", c_void_p),257 ("OpenLogFile2Wide", c_void_p),258 ("GetSystemVersionValues", c_void_p),259 ("GetSystemVersionString", c_void_p),260 ("GetSystemVersionStringWide", c_void_p),261 ("GetContextStackTrace", c_void_p),262 ("OutputContextStackTrace", c_void_p),263 ("GetStoredEventInformation", c_void_p),264 ("GetManagedStatus", c_void_p),265 ("GetManagedStatusWide", c_void_p),266 ("ResetManagedStatus", c_void_p),267 ("GetStackTraceEx", idc_getstacktraceex),268 ("OutputStackTraceEx", c_void_p),269 ("GetContextStackTraceEx", c_void_p),270 ("OutputContextStackTraceEx", c_void_p),271 ("GetBreakpointByGuid", c_void_p),272 ("GetExecutionStatusEx", c_void_p),273 ("GetSynchronizationStatus", c_void_p),274 ("GetDebuggeeType2", c_void_p),275 ]276 277 278IDebugControl7._fields_ = [("lpVtbl", POINTER(IDebugControl7Vtbl))]279 280 281class DebugStatus(IntEnum):282 DEBUG_STATUS_NO_CHANGE = 0283 DEBUG_STATUS_GO = 1284 DEBUG_STATUS_GO_HANDLED = 2285 DEBUG_STATUS_GO_NOT_HANDLED = 3286 DEBUG_STATUS_STEP_OVER = 4287 DEBUG_STATUS_STEP_INTO = 5288 DEBUG_STATUS_BREAK = 6289 DEBUG_STATUS_NO_DEBUGGEE = 7290 DEBUG_STATUS_STEP_BRANCH = 8291 DEBUG_STATUS_IGNORE_EVENT = 9292 DEBUG_STATUS_RESTART_REQUESTED = 10293 DEBUG_STATUS_REVERSE_GO = 11294 DEBUG_STATUS_REVERSE_STEP_BRANCH = 12295 DEBUG_STATUS_REVERSE_STEP_OVER = 13296 DEBUG_STATUS_REVERSE_STEP_INTO = 14297 DEBUG_STATUS_OUT_OF_SYNC = 15298 DEBUG_STATUS_WAIT_INPUT = 16299 DEBUG_STATUS_TIMEOUT = 17300 301 302class DebugSyntax(IntEnum):303 DEBUG_EXPR_MASM = 0304 DEBUG_EXPR_CPLUSPLUS = 1305 306 307class Control(object):308 def __init__(self, control):309 self.ptr = control310 self.control = control.contents311 self.vt = self.control.lpVtbl.contents312 # Keep a handy ulong for passing into C methods.313 self.ulong = c_ulong()314 315 def GetExecutionStatus(self, doprint=False):316 ret = self.vt.GetExecutionStatus(self.control, byref(self.ulong))317 aborter(ret, "GetExecutionStatus")318 status = DebugStatus(self.ulong.value)319 if doprint:320 print("Execution status: {}".format(status))321 return status322 323 def SetExecutionStatus(self, status):324 assert isinstance(status, DebugStatus)325 res = self.vt.SetExecutionStatus(self.control, status.value)326 aborter(res, "SetExecutionStatus")327 328 def WaitForEvent(self, timeout=100):329 # No flags are taken by WaitForEvent, hence 0330 ret = self.vt.WaitForEvent(self.control, 0, timeout)331 aborter(ret, "WaitforEvent", ignore=[S_FALSE])332 return ret333 334 def GetNumberEventFilters(self):335 specific_events = c_ulong()336 specific_exceptions = c_ulong()337 arbitrary_exceptions = c_ulong()338 res = self.vt.GetNumberEventFilters(339 self.control,340 byref(specific_events),341 byref(specific_exceptions),342 byref(arbitrary_exceptions),343 )344 aborter(res, "GetNumberEventFilters")345 return (346 specific_events.value,347 specific_exceptions.value,348 arbitrary_exceptions.value,349 )350 351 def SetExceptionFilterSecondCommand(self, index, command):352 buf = create_string_buffer(command.encode("ascii"))353 res = self.vt.SetExceptionFilterSecondCommand(self.control, index, buf)354 aborter(res, "SetExceptionFilterSecondCommand")355 return356 357 def AddBreakpoint2(self, offset=None, enabled=None):358 breakpoint = POINTER(DebugBreakpoint2)()359 res = self.vt.AddBreakpoint2(360 self.control,361 BreakpointTypes.DEBUG_BREAKPOINT_CODE,362 DEBUG_ANY_ID,363 byref(breakpoint),364 )365 aborter(res, "Add breakpoint 2")366 bp = Breakpoint(breakpoint)367 368 if offset is not None:369 bp.SetOffset(offset)370 if enabled is not None and enabled:371 bp.SetFlags(BreakpointFlags.DEBUG_BREAKPOINT_ENABLED)372 373 return bp374 375 def RemoveBreakpoint(self, bp):376 res = self.vt.RemoveBreakpoint2(self.control, bp.breakpoint)377 aborter(res, "RemoveBreakpoint2")378 bp.die()379 380 def GetStackTraceEx(self):381 # XXX -- I can't find a way to query for how many stack frames there _are_382 # in advance. Guess 128 for now.383 num_frames_buffer = 128384 385 frames = (DEBUG_STACK_FRAME_EX * num_frames_buffer)()386 numframes = c_ulong()387 388 # First three args are frame/stack/IP offsets -- leave them as zero to389 # default to the current instruction.390 res = self.vt.GetStackTraceEx(391 self.control, 0, 0, 0, frames, num_frames_buffer, byref(numframes)392 )393 aborter(res, "GetStackTraceEx")394 return frames, numframes.value395 396 def Execute(self, command):397 # First zero is DEBUG_OUTCTL_*, which we leave as a default, second398 # zero is DEBUG_EXECUTE_* flags, of which we set none.399 res = self.vt.Execute(self.control, 0, command.encode("ascii"), 0)400 aborter(res, "Client execute")401 402 def SetExpressionSyntax(self, cpp=True):403 if cpp:404 syntax = DebugSyntax.DEBUG_EXPR_CPLUSPLUS405 else:406 syntax = DebugSyntax.DEBUG_EXPR_MASM407 408 res = self.vt.SetExpressionSyntax(self.control, syntax)409 aborter(res, "SetExpressionSyntax")410 411 def Evaluate(self, expr):412 ptr = DEBUG_VALUE()413 res = self.vt.Evaluate(414 self.control,415 expr.encode("ascii"),416 DebugValueType.DEBUG_VALUE_INVALID,417 byref(ptr),418 None,419 )420 aborter(res, "Evaluate", ignore=[E_INTERNALEXCEPTION, E_FAIL])421 if res != 0:422 return None423 424 val_type = DebugValueType(ptr.Type)425 426 # Here's a map from debug value types to fields. Unclear what happens427 # with unsigned values, as DbgEng doesn't present any unsigned fields.428 429 extract_map = {430 DebugValueType.DEBUG_VALUE_INT8: ("I8", "char"),431 DebugValueType.DEBUG_VALUE_INT16: ("I16", "short"),432 DebugValueType.DEBUG_VALUE_INT32: ("I32", "int"),433 DebugValueType.DEBUG_VALUE_INT64: ("I64", "long"),434 DebugValueType.DEBUG_VALUE_FLOAT32: ("F32", "float"),435 DebugValueType.DEBUG_VALUE_FLOAT64: ("F64", "double"),436 } # And everything else is invalid.437 438 if val_type not in extract_map:439 raise Exception(440 "Unexpected debug value type {} when evalutaing".format(val_type)441 )442 443 # Also produce a type name...444 445 return getattr(ptr.U, extract_map[val_type][0]), extract_map[val_type][1]446 447 def SetEngineOptions(self, opt):448 res = self.vt.SetEngineOptions(self.control, opt)449 aborter(res, "SetEngineOptions")450 return451