def foCreateForException(cBugReport, oCdbWrapper, uExceptionCode, sExceptionDescription): uStackFramesCount = dxBugIdConfig["uMaxStackFramesCount"]; if uExceptionCode == STATUS_STACK_OVERFLOW: # In order to detect a recursion loop, we need more stack frames: uStackFramesCount += (dxBugIdConfig["uMinStackRecursionLoops"] + 1) * dxBugIdConfig["uMaxStackRecursionLoopSize"] oProcess = cProcess.foCreate(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; oStack = cStack.foCreate(oCdbWrapper, uStackFramesCount); if not oCdbWrapper.bCdbRunning: return None; oException = cException.foCreate(oCdbWrapper, uExceptionCode, sExceptionDescription, oStack); if not oCdbWrapper.bCdbRunning: return None; # If this exception was not caused by the application, but by cdb itself, None is return. This is not a bug. if oException is None: return None; # Create a preliminary error report. oBugReport = cBugReport( oCdbWrapper = oCdbWrapper, sBugTypeId = oException.sTypeId, sBugDescription = oException.sDescription, sSecurityImpact = oException.sSecurityImpact, oProcess = oProcess, oStack = oStack, ); # Perform exception specific analysis: foAnalyzeException = dfoAnalyzeException_by_uExceptionCode.get(oException.uCode); if foAnalyzeException: oBugReport = foAnalyzeException(oBugReport, oCdbWrapper, oException); if not oCdbWrapper.bCdbRunning: return None; if not oBugReport: # This exception is not a bug, continue the application. return None; return oBugReport;
def foCreate(cBugReport, oCdbWrapper, sBugTypeId, sBugDescription, sSecurityImpact): uStackFramesCount = dxBugIdConfig["uMaxStackFramesCount"]; oProcess = cProcess.foCreate(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; oStack = cStack.foCreate(oCdbWrapper, uStackFramesCount); if not oCdbWrapper.bCdbRunning: return None; # Create a preliminary error report. oBugReport = cBugReport( oCdbWrapper = oCdbWrapper, sBugTypeId = sBugTypeId, sBugDescription = sBugDescription, sSecurityImpact = sSecurityImpact, oProcess = oProcess, oStack = oStack, ); return oBugReport;
def __init__(oBugReport, oCdbWrapper, sBugTypeId, sBugDescription, sSecurityImpact, oStack): oBugReport.oCdbWrapper = oCdbWrapper; oBugReport.sBugTypeId = sBugTypeId; oBugReport.sBugDescription = sBugDescription; oBugReport.sSecurityImpact = sSecurityImpact; oBugReport.oProcess = cProcess.foCreate(oCdbWrapper); oBugReport.oStack = oStack; if oCdbWrapper.bGetDetailsHTML: oBugReport.sImportantOutputHTML = oCdbWrapper.sImportantOutputHTML; oBugReport.sProcessBinaryName = oBugReport.oProcess.sBinaryName; oBugReport.asExceptionSpecificBlocksHTML = []; # This information is gathered later, when it turns out this bug needs to be reported: oBugReport.sStackId = None; oBugReport.sId = None; oBugReport.oTopmostRelevantCodeFrame = None; oBugReport.sBugLocation = None; oBugReport.sBugSourceLocation = None;
def cBugReport_foAnalyzeException_STATUS_STOWED_EXCEPTION( oBugReport, oCdbWrapper, oException): # Parameter[0] = paStowedExceptionInformationArray; # Parameter[1] = uStowedExceptionInformationArrayLength; assert len(oException.auParameters) == 2, \ "Unexpected number of WinRT language exception parameters (%d vs 2)" % len(oException.auParameters) pStowedExceptionsAddresses = oException.auParameters[0] uStowedExceptionsCount = oException.auParameters[1] assert uStowedExceptionsCount <= 1, \ "Unexpected number of WinRT language exception stowed exceptions (%d vs 1)" % uStowedExceptionsCount # Get the stowed exception and replace information in the bug report: oStowedException = cStowedException.foCreate(oCdbWrapper, pStowedExceptionsAddresses) oBugReport.sBugTypeId = oStowedException.sTypeId oBugReport.sBugDescription = oStowedException.sDescription oBugReport.sSecurityImpact = oStowedException.sSecurityImpact oBugReport.oProcess = cProcess.foCreate(oCdbWrapper) oBugReport.oStack = cStack.foCreateFromAddress( oCdbWrapper, oStowedException.pStackTrace, oStowedException.uStackTraceSize) return oBugReport
def __init__(oBugReport, oCdbWrapper, sBugTypeId, sBugDescription, sSecurityImpact, oStack): oBugReport.oCdbWrapper = oCdbWrapper oBugReport.sBugTypeId = sBugTypeId oBugReport.sBugDescription = sBugDescription oBugReport.sSecurityImpact = sSecurityImpact oBugReport.oProcess = cProcess.foCreate(oCdbWrapper) oBugReport.oStack = oStack oBugReport.duRelevantAddress_by_sDescription = {} oBugReport.bRegistersRelevant = True # Set to false if register contents are not relevant to the crash if oCdbWrapper.bGetDetailsHTML: oBugReport.sImportantOutputHTML = oCdbWrapper.sImportantOutputHTML oBugReport.sProcessBinaryName = oBugReport.oProcess.sBinaryName oBugReport.asExceptionSpecificBlocksHTML = [] # This information is gathered later, when it turns out this bug needs to be reported: oBugReport.sStackId = None oBugReport.sId = None oBugReport.sBugLocation = None oBugReport.sBugSourceLocation = None
def foCreate(cSelf, oCdbWrapper, uExceptionCode, sExceptionDescription, uBreakpointId): # Get current process details oProcess = cProcess.foCreate(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; if uBreakpointId is not None: # Create exception details for a breakpoint: oException = cException(oProcess, STATUS_BREAKPOINT, "A BugId breakpoint"); (sBugTypeId, sBugDescription, sSecurityImpact) = oCdbWrapper.xBugBreakpointInformation_by_uBreakpointId[uBreakpointId]; else: # Get exception details from cdb. oException = cException.foCreate(oCdbWrapper, oProcess, uExceptionCode, sExceptionDescription); if not oCdbWrapper.bCdbRunning: return None; sBugTypeId = oException.sTypeId; sBugDescription = oException.sDescription; sSecurityImpact = oException.sSecurityImpact; # Get the stack based on the exception info and load symbols for all modules containing functions on the stack. oStack = oException.foGetStack(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; # Hide some functions at the top of the stack that are merely helper functions and not relevant to the error: oStack.fHideTopFrames(asHiddenTopFrames); if oException.uCode == STATUS_BREAKPOINT and oException.oFunction and oException.oFunction.sName == "ntdll.dll!DbgBreakPoint": # This breakpoint most likely got inserted into the process by cdb. There will be no trace of it in the stack, # so do not try to check that exception information matches the first stack frame. return None; # Create a preliminary error report. oBugReport = cSelf( oCdbWrapper = oCdbWrapper, sBugTypeId = sBugTypeId, sBugDescription = sBugDescription, sSecurityImpact = sSecurityImpact, oException = oException, oStack = oStack, ); if uBreakpointId is None: # Perform exception specific analysis: foAnalyzeException = dfoAnalyzeException_by_uExceptionCode.get(oException.uCode); if foAnalyzeException: oBugReport = foAnalyzeException(oBugReport, oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; if not oBugReport: # This exception is not a bug, continue the application. return None; # Calculate sStackId, determine sBugLocation and optionally create and return sStackHTML. sStackHTML = cBugReport_fsProcessStack(oBugReport, oCdbWrapper); oBugReport.sId = "%s %s" % (oBugReport.sBugTypeId, oBugReport.sStackId); if oCdbWrapper.bGetDetailsHTML: # Generate sDetailsHTML? # Create HTML details asBlocksHTML = []; # Create and add important output block if needed if oBugReport.sImportantOutputHTML: asBlocksHTML.append(sBlockHTMLTemplate % {"sName": "Potentially important application output", "sContent": oBugReport.sImportantOutputHTML}); # Add stack block asBlocksHTML.append(sBlockHTMLTemplate % {"sName": "Stack", "sContent": sStackHTML}); # Add exception specific blocks if needed: asBlocksHTML += oBugReport.asExceptionSpecificBlocksHTML; # Create and add disassembly block if possible sDisassemblyHTML = cBugReport_fsGetDisassemblyHTML(oBugReport, oCdbWrapper); if sDisassemblyHTML is None: return None; if sDisassemblyHTML: asBlocksHTML.append(sBlockHTMLTemplate % {"sName": "Disassembly", "sContent": sDisassemblyHTML}); # Create and add registers block asRegisters = oCdbWrapper.fasSendCommandAndReadOutput("rM 0x%X" % (0x1 + 0x4 + 0x8 + 0x10 + 0x20 + 0x40)); if not oCdbWrapper.bCdbRunning: return None; sRegistersHTML = "<br/>".join(['<span class="Registers">%s</span>' % oCdbWrapper.fsHTMLEncode(s) for s in asRegisters]); asBlocksHTML.append(sBlockHTMLTemplate % {"sName": "Registers", "sContent": sRegistersHTML}); # Add referenced memory to memory block and add memory block if needed sReferencedMemoryHTML = cBugReport_fsGetReferencedMemoryHTML(oBugReport, oCdbWrapper) if sReferencedMemoryHTML is None: return None; if sReferencedMemoryHTML: asBlocksHTML.append(sBlockHTMLTemplate % {"sName": "Referenced memory", "sContent": sReferencedMemoryHTML}); sBinaryInformationHTML = cBugReport_fsGetBinaryInformationHTML(oBugReport, oCdbWrapper); if sBinaryInformationHTML is None: return None; if sBinaryInformationHTML: asBlocksHTML.append(sBlockHTMLTemplate % {"sName": "Binary information", "sContent": sBinaryInformationHTML}); # Convert saved cdb IO HTML into one string and delete everything but the last line to free up some memory. sCdbStdIOHTML = '<hr/>'.join(oBugReport.oCdbWrapper.asCdbStdIOBlocksHTML); oBugReport.oCdbWrapper.asCdbStdIOBlocksHTML = oBugReport.oCdbWrapper.asCdbStdIOBlocksHTML[-1:]; # Stick everything together. oBugReport.sDetailsHTML = sDetailsHTMLTemplate % { "sId": oCdbWrapper.fsHTMLEncode(oBugReport.sId), "sBugDescription": oCdbWrapper.fsHTMLEncode(oBugReport.sBugDescription), "sBugLocation": oCdbWrapper.fsHTMLEncode(oBugReport.sBugLocation), "sSecurityImpact": oBugReport.sSecurityImpact and \ '<span class="SecurityImpact">%s</span>' % oCdbWrapper.fsHTMLEncode(oBugReport.sSecurityImpact) or "Denial of Service", "sOptionalBlocks": "".join(asBlocksHTML), "sCdbStdIO": sCdbStdIOHTML, }; return oBugReport;
def foCreate(cSelf, oCdbWrapper, uExceptionCode, sExceptionDescription): # Get current process details oProcess = cProcess.foCreate(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; # Get exception details oException = cException.foCreate(oCdbWrapper, oProcess, uExceptionCode, sExceptionDescription); if not oCdbWrapper.bCdbRunning: return None; # Get the stack oStack = oException.foGetStack(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; # Hide some functions at the top of the stack that are merely helper functions and not relevant to the error: oStack.fHideTopFrames(asHiddenTopFrames); # Create a preliminary error report. oErrorReport = cSelf( sErrorTypeId = oException.sTypeId, sErrorDescription = oException.sDescription, sSecurityImpact = oException.sSecurityImpact, oException = oException, oStack = oStack, ); # Make exception specific changes to the error report: foSpecialErrorReport = dfoSpecialErrorReport_uExceptionCode.get(oException.uCode); if foSpecialErrorReport: oErrorReport = foSpecialErrorReport(oErrorReport, oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; if not oErrorReport: # This exception is not an error, continue the application. return None; # Find out which frame should be the "main" frame and get stack id. oTopmostRelevantFrame = None; # topmost relevant frame oTopmostRelevantFunctionFrame = None; # topmost relevant frame that has a function symbol oTopmostRelevantModuleFrame = None; # topmost relevant frame that has no function symbol but a module uFramesHashed = 0; asHTMLStack = []; sStackId = ""; for oStackFrame in oStack.aoFrames: if oStackFrame.bIsHidden: # This frame is hidden (because it is irrelevant to the crash) asHTMLStack.append("<s>%s</s><br/>" % fsHTMLEncode(oStackFrame.sAddress)); else: oTopmostRelevantFrame = oTopmostRelevantFrame or oStackFrame; sHTMLAddress = fsHTMLEncode(oStackFrame.sAddress); # Make stack frames without a function symbol italic if not oStackFrame.oFunction: sHTMLAddress = "<i>%s</i>" % sHTMLAddress; # Hash frame address for id and output frame to html if uFramesHashed == oStack.uHashFramesCount: # no more hashing is needed: just output as is: asHTMLStack.append("%s<br/>" % sHTMLAddress); else: sStackId += oStackFrame.sId or "__"; if oStackFrame.sId: # frame adds useful infoormation to the id: add hash and output bold uFramesHashed += 1; asHTMLStack.append("<b>%s</b> (%s in id)<br/>" % (sHTMLAddress, oStackFrame.sId)); # Determine the top frame for the id: if oStackFrame.oFunction: oTopmostRelevantFunctionFrame = oTopmostRelevantFunctionFrame or oStackFrame; elif oStackFrame.oModule: oTopmostRelevantModuleFrame = oTopmostRelevantModuleFrame or oStackFrame; else: # This is not part of the id, but between frames that are: add "__" to id and output strike-through asHTMLStack.append("<s>%s</s><br/>" % sHTMLAddress); # If there are not enouogh id-able stack frames, there may be many trailing "_"-s; remove these. Also, if there # was not id, or nothing is left after removing the "_"-s, use the id "##". oErrorReport.sStackId = sStackId.rstrip("_") or "##"; if oStack.bPartialStack: asHTMLStack.append("... (rest of the stack was ignored)<br/>"); # Use a function for the id oCodeIdFrame = oTopmostRelevantFunctionFrame or oTopmostRelevantModuleFrame; oErrorReport.sCodeId = oCodeIdFrame and oCodeIdFrame.sSimplifiedAddress or "(unknown)"; # Create the location description oErrorReport.sCodeDescription = oTopmostRelevantFrame and oTopmostRelevantFrame.sAddress or "(unknown)"; # Get the binary's cdb name for retreiving version information: asBinaryCdbNames = oCdbWrapper.fasGetCdbIdsForModuleFileNameInCurrentProcess(oErrorReport.sProcessBinaryName); if not oCdbWrapper.bCdbRunning: return None; assert len(asBinaryCdbNames) > 0, "Cannot find binary %s module" % oErrorReport.sProcessBinaryName; # If the binary is loaded as a module multiple times in the process, the first should be the binary that was # executed. dsGetVersionCdbId_by_sBinaryName = {oErrorReport.sProcessBinaryName: asBinaryCdbNames[0]}; # Get the id frame's module cdb name for retreiving version information: if oCodeIdFrame: dsGetVersionCdbId_by_sBinaryName[oCodeIdFrame.oModule.sBinaryName] = oCodeIdFrame.oModule.sCdbId; asHTMLBinaryInformation = []; for sBinaryName, sCdbId in dsGetVersionCdbId_by_sBinaryName.items(): asModuleInformationOutput = oCdbWrapper.fasSendCommandAndReadOutput("lmv m *%s" % sCdbId); if not oCdbWrapper.bCdbRunning: return None; # Sample output: # |0:004> lmv M firefox.exe # |start end module name # |00000000`011b0000 00000000`0120f000 firefox (deferred) # | Image path: firefox.exe # | Image name: firefox.exe # | Timestamp: Thu Aug 13 03:23:30 2015 (55CBF192) # | CheckSum: 0006133B # | ImageSize: 0005F000 # | File version: 40.0.2.5702 # | Product version: 40.0.2.0 # | File flags: 0 (Mask 3F) # | File OS: 4 Unknown Win32 # | File type: 2.0 Dll # | File date: 00000000.00000000 # | Translations: 0000.04b0 # | CompanyName: Mozilla Corporation # | ProductName: Firefox # | InternalName: Firefox # | OriginalFilename: firefox.exe # | ProductVersion: 40.0.2 # | FileVersion: 40.0.2 # | FileDescription: Firefox # | LegalCopyright: (c)Firefox and Mozilla Developers; available under the MPL 2 license. # | LegalTrademarks: Firefox is a Trademark of The Mozilla Foundation. # | Comments: Firefox is a Trademark of The Mozilla Foundation. # The first two lines can be skipped. asHTMLBinaryInformation.append(sHTMLBinaryInformationTemplate % { "sName": fsHTMLEncode(sBinaryName), "sInformation": "".join(["%s<br/>" % fsHTMLEncode(x) for x in asModuleInformationOutput[2:]]), }); # Turn cdb I/O into formatted HTML. It is separated into blocks, one for the initial cdb output and one for each # command executed. asHTMLBlocks = []; for asBlock in oCdbWrapper.aasCdbStdIO: asHTMLBlocks.append("".join(["%s<br/>" % fsHTMLEncode(sLine) for sLine in asBlock])); sHTMLCdbStdIO = "<hr/>".join(asHTMLBlocks); sHTMLCdbStdErr = "".join(["%s<br/>" % fsHTMLEncode(sLine) for sLine in oCdbWrapper.asCdbStdErr]); # Create HTML details oErrorReport.sHTMLDetails = sHTMLDetailsTemplate % { "sId": fsHTMLEncode(oErrorReport.sId), "sExceptionDescription": fsHTMLEncode(oErrorReport.sErrorDescription), "sProcessBinaryName": fsHTMLEncode(oErrorReport.sProcessBinaryName), "sCodeDescription": fsHTMLEncode(oErrorReport.sCodeDescription), "sSecurityImpact": oErrorReport.sSecurityImpact and "<b>%s</b>" % fsHTMLEncode(oErrorReport.sSecurityImpact) or "None", "sStack": "".join(asHTMLStack), "sBinaryInformation": "".join(asHTMLBinaryInformation), "sCdbStdErr": sHTMLCdbStdErr, "sCdbStdIO": sHTMLCdbStdIO, }; return oErrorReport;
def foCreate(cSelf, oCrashInfo, uCode, sCodeDescription): oProcess = cProcess.foCreate(oCrashInfo); oSelf = cSelf(oProcess, uCode, sCodeDescription); # We do this twice to make sure symbols are loaded the first time, which may create additional symbal warnings and # errors that makes the output harder to parse. The second time, there will be no such output, so we can parse it # a lot easier. asExceptionRecord = oCrashInfo._fasSendCommandAndReadOutput(".exr -1"); if asExceptionRecord is None: return None; asExceptionRecord = oCrashInfo._fasSendCommandAndReadOutput(".exr -1"); if asExceptionRecord is None: return None; uParameterCount = None; uParameterIndex = None; for sLine in asExceptionRecord: oNameValueMatch = re.match(r"^\s*%s\s*$" % ( r"(\w+)(?:\[(\d+)\])?\:\s+" # (name) optional{ "[" (index) "]" } ":" whitespace r"([0-9A-F`]+)" # (value) r"(?:\s+\((.*)\))?" # optional{ whitespace "(" (symbol || description) ")" } ), sLine, re.I); if oNameValueMatch: sName, sIndex, sValue, sDetails = oNameValueMatch.groups(); uValue = int(sValue.replace("`", ""), 16); if sName == "ExceptionAddress": oSelf.uAddress = uValue; oSelf.sAddressSymbol = sDetails; elif sName == "ExceptionCode": assert uValue == uCode, \ "Exception record has an unexpected ExceptionCode value (0x%08X vs 0x%08X)" % (uValue, uCode); assert sDetails is None or sDetails == sCodeDescription, \ "Exception record has an unexpected ExceptionCode description (%s vs %s)" % \ (repr(sDetails), repr(sCodeDescription)); elif sName == "ExceptionFlags": oSelf.uFlags = uValue; elif sName == "NumberParameters": uParameterCount = uValue; uParameterIndex = 0; oSelf.auParameters = []; elif sName == "Parameter": assert int(sIndex, 16) == uParameterIndex, \ "Unexpected parameter #0x%s vs 0x%X" % (sIndex, uParameterIndex); oSelf.auParameters.append(uValue); uParameterIndex += 1; else: raise AssertionError("Unknown exception record value %s" % sLine); elif oSelf.sDetails is None: oSelf.sDetails = sLine; else: raise AssertionError("Superfluous exception record line %s" % sLine); assert oSelf.uAddress is not None, \ "Exception record is missing an ExceptionAddress value"; assert oSelf.uFlags is not None, \ "Exception record is missing an ExceptionFlags value"; assert uParameterCount is not None, \ "Exception record is missing an NumberParameters value"; assert uParameterCount == len(oSelf.auParameters), \ "Unexpected number of parameters (%d vs %d)" % (len(oSelf.auParameters), uParameterCount); # Now handle the information in the exception record and perform additional tasks as needed. Create an exception # id that uniquely identifies the exception and a description of the exception. if uCode == 0xC000027B: # Parameter[0] = paStowedExceptionInformationArray; # Parameter[1] = uStowedExceptionInformationArrayLength; assert len(oSelf.auParameters) == 2, \ "Unexpected number of WinRT language exception parameters (%d vs 2)" % len(oSelf.auParameters); pStowedExceptionsAddress = oSelf.auParameters[0]; uStowedExceptionsCount = oSelf.auParameters[1]; assert uStowedExceptionsCount == 1, \ "Unexpected number of WinRT language exception stowed exceptions (%d vs 1)" % uStowedExceptionsCount; # The stowed exception replaces this exception: return cStowedException.foCreate(oCrashInfo, oProcess, pStowedExceptionsAddress); elif uCode == STATUS_ACCESS_VIOLATION: # Parameter[0] = access type (0 = read, 1 = write, 8 = execute) # Parameter[1] = address assert len(oSelf.auParameters) == 2, \ "Unexpected number of access violation exception parameters (%d vs 2)" % len(oSelf.auParameters); # Access violation: add the type of operation and the location to the exception id. sViolationTypeId = "AV" + {0:"R", 1:"W", 8:"E"}.get(oSelf.auParameters[0], "?"); sViolationTypeDescription = {0:"reading", 1:"writing", 8:"executing"}.get(oSelf.auParameters[0], "0x%X-ing" % oSelf.auParameters[0]); uAddress = oSelf.auParameters[1]; sAddressId, sAddressDescription = ftsGetAddressIdAndDescription(uAddress); if sAddressId != "NULL": asPageHeapInformation = oCrashInfo._fasSendCommandAndReadOutput("!heap -p -a 0x%X" % uAddress); asPageHeapInformation = oCrashInfo._fasSendCommandAndReadOutput("!heap -p -a 0x%X" % uAddress); asPageHeapInformation = oCrashInfo._fasSendCommandAndReadOutput("!heap -p -a 0x%X" % (uAddress - 0x4)); if asPageHeapInformation is None: return None; #if uAddress & 0xFFF < 0x8: # # Assuming page heap is This may be a buffer overrun oSelf.sTypeId = "%s@%s" % (sViolationTypeId, sAddressId); oSelf.sDescription = "%s while %s memory at 0x%X (%s)" % \ (sCodeDescription, sViolationTypeDescription, uAddress, sAddressDescription); uNULLMinusOffsetMinAddress = {"x64": 0xFFFFFFFFFFFF0000, "x86": 0xFFFF0000}[oProcess.sISA]; oSelf.sSecurityImpact = ((uAddress > uNULLMinusOffsetMinAddress or uAddress < 0x10000) and "Not a security issue" or "Probably a security issue" ); elif uCode == STATUS_STACK_BUFFER_OVERRUN: # Parameter[0] = fail fast code assert len(oSelf.auParameters) == 1, \ "Unexpected number of fail fast exception parameters (%d vs 1)" % len(oSelf.auParameters); sCodeId, sCodeDescription, sSecurityImpact = \ ftsGetFailFastErrorCodeIdDescriptionAndSecurityImpact(oSelf.auParameters[0]); oSelf.sTypeId = "FF@%s" % sCodeId; oSelf.sDescription = "A critical issue was detected (code %d: %s)" % (oSelf.auParameters[0], sCodeDescription); oSelf.sSecurityImpact = sSecurityImpact; else: oSelf.sTypeId = fsGetExceptionTypeId(uCode); oSelf.sDescription = "%s (code 0x%08X)" % (sCodeDescription, uCode); oSelf.sSecurityImpact = fsGetSecurityImpact(uCode); # Get the stack oSelf.oStack = cStack.foCreate(oCrashInfo, oSelf.oProcess); if oSelf.oStack is None: return None; return oSelf;
def cCrashInfo_foCdbRunApplicationAndGetErrorReport(oCrashInfo, asIntialOutput): # cdb can either start an application, or attach to paused processes; which is it? bCdbStartedAnApplication = len(oCrashInfo._auProcessIdsPendingAttach) == 0; bDebuggerIsAttachingToProcesses = not bCdbStartedAnApplication; # If cdb was asked to attach to a process, make sure it worked. if bDebuggerIsAttachingToProcesses: fDetectFailedAttach(asIntialOutput); # If the debugger started a process, we should set up exception handling now. Otherwise wait until the debugger has # attached to all the processes. bExceptionHandlersHaveBeenSet = False; # While no error has been reported: oErrorReport = None; while oErrorReport is None: # Find out what event caused the debugger break asLastEventOutput = oCrashInfo._fasSendCommandAndReadOutput(".lastevent"); if not oCrashInfo._bCdbRunning: return None; # Sample output: # |Last event: 3d8.1348: Create process 3:3d8 # | debugger time: Tue Aug 25 00:06:07.311 2015 (UTC + 2:00) # - or - # |Last event: c74.10e8: Exit process 4:c74, code 0 # | debugger time: Tue Aug 25 00:06:07.311 2015 (UTC + 2:00) bValidLastEventOutput = len(asLastEventOutput) == 2 and re.match(r"^\s*debugger time: .*$", asLastEventOutput[1]); oEventMatch = bValidLastEventOutput and re.match( "".join([ r"^Last event: ([0-9a-f]+)\.[0-9a-f]+: ", r"(?:", r"(Create|Exit) process [0-9a-f]+\:([0-9a-f]+)(?:, code [0-9a-f]+)?", r"|", r"(.*?) \- code ([0-9a-f]+) \(!*\s*(?:first|second) chance\s*!*\)", r")\s*$", ]), asLastEventOutput[0], re.I ); assert oEventMatch, "Invalid .lastevent output:\r\n%s" % "\r\n".join(asLastEventOutput); ( sProcessIdHex, sCreateExitProcess, sCreateExitProcessIdHex, sExceptionDescription, sExceptionCode ) = oEventMatch.groups(); uProcessId = long(sProcessIdHex, 16); if sCreateExitProcess: # Make sure the created/exited process is the current process. assert sProcessIdHex == sCreateExitProcessIdHex, "%s vs %s" % (sProcessIdHex, sCreateExitProcessIdHex); oCrashInfo.fHandleCreateExitProcess(sCreateExitProcess, uProcessId); if len(oCrashInfo._auProcessIds) == 0: break; # The last process was terminated. else: uExceptionCode = int(sExceptionCode, 16); if uExceptionCode == STATUS_BREAKPOINT and uProcessId not in oCrashInfo._auProcessIds: # This is assumed to be the initial breakpoint after starting/attaching to the first process or after a new # process was created by the application. This assumption may not be correct, in which case the code needs to # be modifed to check the stack to determine if this really is the initial breakpoint. But that comes at a # performance cost, so until proven otherwise, the code is based on this assumption. oCrashInfo.fHandleCreateExitProcess("Create", uProcessId); else: # Report that analysis is starting... oCrashInfo._fExceptionDetectedCallback(uExceptionCode, sExceptionDescription); # Turn off noizy symbol loading; it can mess up the output of commands, making it unparsable. asOutput = oCrashInfo._fasSendCommandAndReadOutput("!sym quiet"); if not oCrashInfo._bCdbRunning: return None; # Gather relevant information... oProcess = cProcess.foCreate(oCrashInfo); oException = cException.foCreate(oCrashInfo, oProcess, uExceptionCode, sExceptionDescription); if not oCrashInfo._bCdbRunning: return None; # Save the exception report for returning when we're finished. oErrorReport = cErrorReport.foCreateFromException(oCrashInfo, oException); if not oCrashInfo._bCdbRunning: return None; # Stop the debugger if there was a fatal error that needs to be reported. if oErrorReport is not None: break; # Turn noizy symbol loading back on if it was enabled. if dxCrashInfoConfig["bDebugSymbolLoading"]: asOutput = oCrashInfo._fasSendCommandAndReadOutput("!sym noizy"); if not oCrashInfo._bCdbRunning: return None; # If there are more processes to attach to, do so: if len(oCrashInfo._auProcessIdsPendingAttach) > 0: asAttachToProcess = oCrashInfo._fasSendCommandAndReadOutput(".attach 0n%d" % oCrashInfo._auProcessIdsPendingAttach[0]); if not oCrashInfo._bCdbRunning: return; else: # The debugger has started the process or attached to all processes. # Set up exception handling if this has not beenm done yet. if not bExceptionHandlersHaveBeenSet: # Note to self: when rewriting the code, make sure not to set up exception handling before the debugger has # attached to all processes. But do so before resuming the threads. Otherwise one or more of the processes can # end up having only one thread that has a suspend count of 2 and no amount of resuming will cause the process # to run. The reason for this is unknown, but if things are done in the correct order, this problem is avoided. bExceptionHandlersHaveBeenSet = False; oCrashInfo._fasSendCommandAndReadOutput(sExceptionHandlingCommands); if not oCrashInfo._bCdbRunning: return; # If the debugger attached to processes, mark that as done and resume threads in all processes. if bDebuggerIsAttachingToProcesses: bDebuggerIsAttachingToProcesses = False; for uProcessId in oCrashInfo._auProcessIds: oCrashInfo._fasSendCommandAndReadOutput("|~[0n%d]s;~*m;~" % uProcessId); if not oCrashInfo._bCdbRunning: return; # Run the application asRunApplicationOutput = oCrashInfo._fasSendCommandAndReadOutput("g"); if not oCrashInfo._bCdbRunning: return; # If cdb is attaching to a process, make sure it worked. if bDebuggerIsAttachingToProcesses: fDetectFailedAttach(asRunApplicationOutput); # Terminate cdb. oCrashInfo._bCdbTerminated = True; oCrashInfo._fasSendCommandAndReadOutput("q"); assert not oCrashInfo._bCdbRunning, "Debugger did not terminate when requested"; return oErrorReport;
def foCreate(cSelf, oCdbWrapper, uExceptionCode, sExceptionDescription): # Get current process details oProcess = cProcess.foCreate(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; # Get exception details oException = cException.foCreate(oCdbWrapper, oProcess, uExceptionCode, sExceptionDescription); if not oCdbWrapper.bCdbRunning: return None; # Get registers. This information is not analyzed, but will end up in cdb output in the report, for potential # use by an analyst. oCdbWrapper.fasSendCommandAndReadOutput("r"); if not oCdbWrapper.bCdbRunning: return None; # Similarly, try to get disassembly around code in which exception happened. This may not be possible if rip/eip # points to memeory that cannot be read. asDisassemblyOutput = oCdbWrapper.fasSendCommandAndReadOutput(".if ($vvalid(@$scopeip, 1)) { ub; }; .else { .echo Disassembly not possible };"); if not oCdbWrapper.bCdbRunning: return None; if asDisassemblyOutput != ["Disassembly not possible"]: # rip/eip must be valid as disassembly before this address was returned: get disassembly after as well: oCdbWrapper.fasSendCommandAndReadOutput("u"); if not oCdbWrapper.bCdbRunning: return None; # Get the stack oStack = oException.foGetStack(oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; # Compare stack with exception information if oException.sAddressSymbol: doModules_by_sCdbId = oCdbWrapper.fdoGetModulesByCdbIdForCurrentProcess(); ( uAddress, sUnloadedModuleFileName, oModule, uModuleOffset, oFunction, uFunctionOffset ) = oCdbWrapper.ftxSplitSymbolOrAddress(oException.sAddressSymbol, doModules_by_sCdbId); sCdbSource = oException.sAddressSymbol; else: sCdbSource = "%X" % oException.uAddress; # Kinda faking it here :) uAddress = oException.uAddress; sUnloadedModuleFileName, oModule, uModuleOffset = None, None, None; oFunction, uFunctionOffset = None, None; if not oStack.aoFrames: # Failed to get stack, use information from exception. uFrameNumber = 0; oStack.fCreateAndAddStackFrame(oCdbWrapper, uFrameNumber, sCdbSource, uAddress, sUnloadedModuleFileName, oModule, uModuleOffset, oFunction, uFunctionOffset); else: if oException.uCode in [STATUS_WX86_BREAKPOINT, STATUS_BREAKPOINT]: # A breakpoint happens at an int 3 instruction, and eip/rip may have been updated to the next instruction. # If the first stack frame is not the same as the exception address, fix this off-by-one: oFrame = oStack.aoFrames[0]; if ( oFrame.uAddress == uAddress and oFrame.sUnloadedModuleFileName == sUnloadedModuleFileName and oFrame.oModule == oModule and oFrame.uModuleOffset == uModuleOffset and oFrame.oFunction == oFunction and oFrame.uFunctionOffset == uFunctionOffset ): pass; else: if uAddress is not None: uAddress -= 1; elif uModuleOffset is not None: uModuleOffset -= 1; elif uFunctionOffset is not None: oFrame.uFunctionOffset -= 1; else: raise AssertionError("The first stack frame appears to have no address or offet to adjust."); assert ( oFrame.uAddress == uAddress and oFrame.sUnloadedModuleFileName == sUnloadedModuleFileName and oFrame.oModule == oModule and oFrame.uModuleOffset == uModuleOffset and oFrame.oFunction == oFunction and oFrame.uFunctionOffset == uFunctionOffset ), "The first stack frame does not appear to match the exception address"; else: # Check that the address where the exception happened is on the stack and hide any frames that appear above it, # as these are not interesting (e.g. ntdll!RaiseException). for oFrame in oStack.aoFrames: if ( oFrame.uAddress == uAddress and oFrame.sUnloadedModuleFileName == sUnloadedModuleFileName and oFrame.oModule == oModule and oFrame.uModuleOffset == uModuleOffset and oFrame.oFunction == oFunction and oFrame.uFunctionOffset == uFunctionOffset ): break; oFrame.bIsHidden = True; else: raise AssertionError("The exception address %s was not found on the stack" % sCdbSource); # Hide some functions at the top of the stack that are merely helper functions and not relevant to the error: oStack.fHideTopFrames(asHiddenTopFrames); # Create a preliminary error report. oErrorReport = cSelf( oCdbWrapper = oCdbWrapper, sErrorTypeId = oException.sTypeId, sErrorDescription = oException.sDescription, sSecurityImpact = oException.sSecurityImpact, oException = oException, oStack = oStack, asImportantStdErrLines = oCdbWrapper.asImportantStdErrLines, ); # Make exception specific changes to the error report: foSpecialErrorReport = dfoSpecialErrorReport_uExceptionCode.get(oException.uCode); if foSpecialErrorReport: oErrorReport = foSpecialErrorReport(oErrorReport, oCdbWrapper); if not oCdbWrapper.bCdbRunning: return None; if not oErrorReport: # This exception is not an error, continue the application. return None; # Find out which frame should be the "main" frame and get stack id. oTopmostRelevantFrame = None; # topmost relevant frame oTopmostRelevantFunctionFrame = None; # topmost relevant frame that has a function symbol oTopmostRelevantModuleFrame = None; # topmost relevant frame that has no function symbol but a module uFramesHashed = 0; bStackShowsNoSignOfCorruption = True; asHTMLStack = []; sStackId = ""; for oStackFrame in oStack.aoFrames: if oStackFrame.bIsHidden: # This frame is hidden (because it is irrelevant to the crash) asHTMLStack.append('<span class="StackIgnored">%s</span><br/>' % fsHTMLEncode(oStackFrame.sAddress)); else: # Once a stack frame is encountered with no id, the stack can no longer be trusted to be correct. bStackShowsNoSignOfCorruption = bStackShowsNoSignOfCorruption and (oStackFrame.sId and True or False); oTopmostRelevantFrame = oTopmostRelevantFrame or oStackFrame; sHTMLAddress = fsHTMLEncode(oStackFrame.sAddress); # Make stack frames without a function symbol italic if not oStackFrame.oFunction: sHTMLAddress = '<span class="StackNoSymbol">%s</span>' % sHTMLAddress; # Hash frame address for id and output frame to html if not bStackShowsNoSignOfCorruption or uFramesHashed == oStack.uHashFramesCount: # no more hashing is needed: just output as is: asHTMLStack.append('<span class="Stack">%s</span><br/>' % sHTMLAddress); else: sStackId += oStackFrame.sId; # frame adds useful information to the id: add hash and output bold uFramesHashed += 1; asHTMLStack.append('<span class="StackHash">%s</span> (%s in id)<br/>' % (sHTMLAddress, oStackFrame.sId)); # Determine the top frame for the id: if oStackFrame.oFunction: oTopmostRelevantFunctionFrame = oTopmostRelevantFunctionFrame or oStackFrame; elif oStackFrame.oModule: oTopmostRelevantModuleFrame = oTopmostRelevantModuleFrame or oStackFrame; # If there are not enouogh id-able stack frames, there may be many trailing "_"-s; remove these. Also, if there # was not id, or nothing is left after removing the "_"-s, use the id "##". oErrorReport.sStackId = sStackId.rstrip("_") or "##"; if oStack.bPartialStack: asHTMLStack.append("... (rest of the stack was ignored)<br/>"); oErrorReport.sHTMLStack = "".join(asHTMLStack); # Use a function for the id oCodeIdFrame = oTopmostRelevantFunctionFrame or oTopmostRelevantModuleFrame; oErrorReport.sCodeId = oCodeIdFrame and oCodeIdFrame.sSimplifiedAddress or "(unknown)"; # Create the location description oErrorReport.sCodeDescription = oTopmostRelevantFrame and oTopmostRelevantFrame.sAddress or "(unknown)"; # Get the binary's cdb name for retreiving version information: asBinaryCdbNames = oCdbWrapper.fasGetCdbIdsForModuleFileNameInCurrentProcess(oErrorReport.sProcessBinaryName); if not oCdbWrapper.bCdbRunning: return None; assert len(asBinaryCdbNames) > 0, "Cannot find binary %s module" % oErrorReport.sProcessBinaryName; # If the binary is loaded as a module multiple times in the process, the first should be the binary that was # executed. # If this turns out to be wrong, this code should be switch to use "u @$exentry L1" to determine the cdb id of the # module in which the process's entry point is found. dsGetVersionCdbId_by_sBinaryName = {oErrorReport.sProcessBinaryName: asBinaryCdbNames[0]}; # Get the id frame's module cdb name for retreiving version information: if oCodeIdFrame: dsGetVersionCdbId_by_sBinaryName[oCodeIdFrame.oModule.sBinaryName] = oCodeIdFrame.oModule.sCdbId; asHTMLBinaryInformation = []; for sBinaryName, sCdbId in dsGetVersionCdbId_by_sBinaryName.items(): asModuleInformationOutput = oCdbWrapper.fasSendCommandAndReadOutput("lmv m *%s" % sCdbId); if not oCdbWrapper.bCdbRunning: return None; # Sample output: # |0:004> lmv M firefox.exe # |start end module name # |00000000`011b0000 00000000`0120f000 firefox (deferred) # | Image path: firefox.exe # | Image name: firefox.exe # | Timestamp: Thu Aug 13 03:23:30 2015 (55CBF192) # | CheckSum: 0006133B # | ImageSize: 0005F000 # | File version: 40.0.2.5702 # | Product version: 40.0.2.0 # | File flags: 0 (Mask 3F) # | File OS: 4 Unknown Win32 # | File type: 2.0 Dll # | File date: 00000000.00000000 # | Translations: 0000.04b0 # | CompanyName: Mozilla Corporation # | ProductName: Firefox # | InternalName: Firefox # | OriginalFilename: firefox.exe # | ProductVersion: 40.0.2 # | FileVersion: 40.0.2 # | FileDescription: Firefox # | LegalCopyright: (c)Firefox and Mozilla Developers; available under the MPL 2 license. # | LegalTrademarks: Firefox is a Trademark of The Mozilla Foundation. # | Comments: Firefox is a Trademark of The Mozilla Foundation. # The first two lines can be skipped. asHTMLBinaryInformation.append(sHTMLBinaryInformationTemplate % { "sName": fsHTMLEncode(sBinaryName), "sInformation": "".join(["%s<br/>" % fsHTMLEncode(x) for x in asModuleInformationOutput[2:]]), }); oErrorReport.sHTMLBinaryInformation = "".join(asHTMLBinaryInformation); # TODO: At some point the instruction that cause the exception could be added. Use "u @$eventip L1" to retreive it. return oErrorReport;
def foCreate(cSelf, oCdbWrapper, uExceptionCode, sExceptionDescription): # Get current process details oProcess = cProcess.foCreate(oCdbWrapper) if not oCdbWrapper.bCdbRunning: return None # Get exception details oException = cException.foCreate(oCdbWrapper, oProcess, uExceptionCode, sExceptionDescription) if not oCdbWrapper.bCdbRunning: return None # Get the stack oStack = oException.foGetStack(oCdbWrapper) if not oCdbWrapper.bCdbRunning: return None # Hide some functions at the top of the stack that are merely helper functions and not relevant to the error: oStack.fHideTopFrames(asHiddenTopFrames) # Create a preliminary error report. oErrorReport = cSelf( oCdbWrapper=oCdbWrapper, sErrorTypeId=oException.sTypeId, sErrorDescription=oException.sDescription, sSecurityImpact=oException.sSecurityImpact, oException=oException, oStack=oStack, ) # Make exception specific changes to the error report: foSpecialErrorReport = dfoSpecialErrorReport_uExceptionCode.get( oException.uCode) if foSpecialErrorReport: oErrorReport = foSpecialErrorReport(oErrorReport, oCdbWrapper) if not oCdbWrapper.bCdbRunning: return None if not oErrorReport: # This exception is not an error, continue the application. return None # Find out which frame should be the "main" frame and get stack id. oTopmostRelevantFrame = None # topmost relevant frame oTopmostRelevantFunctionFrame = None # topmost relevant frame that has a function symbol oTopmostRelevantModuleFrame = None # topmost relevant frame that has no function symbol but a module uFramesHashed = 0 asHTMLStack = [] sStackId = "" for oStackFrame in oStack.aoFrames: if oStackFrame.bIsHidden: # This frame is hidden (because it is irrelevant to the crash) asHTMLStack.append( '<span class="StackIgnored">%s</span><br/>' % fsHTMLEncode(oStackFrame.sAddress)) else: oTopmostRelevantFrame = oTopmostRelevantFrame or oStackFrame sHTMLAddress = fsHTMLEncode(oStackFrame.sAddress) # Make stack frames without a function symbol italic if not oStackFrame.oFunction: sHTMLAddress = '<span class="StackNoSymbol">%s</span>' % sHTMLAddress # Hash frame address for id and output frame to html if uFramesHashed == oStack.uHashFramesCount: # no more hashing is needed: just output as is: asHTMLStack.append('<span class="Stack">%s</span><br/>' % sHTMLAddress) else: sStackId += oStackFrame.sId or "__" if oStackFrame.sId: # frame adds useful infoormation to the id: add hash and output bold uFramesHashed += 1 asHTMLStack.append( '<span class="StackHash">%s</span> (%s in id)<br/>' % (sHTMLAddress, oStackFrame.sId)) # Determine the top frame for the id: if oStackFrame.oFunction: oTopmostRelevantFunctionFrame = oTopmostRelevantFunctionFrame or oStackFrame elif oStackFrame.oModule: oTopmostRelevantModuleFrame = oTopmostRelevantModuleFrame or oStackFrame else: # This is not part of the id, but between frames that are: add "__" to id and output strike-through asHTMLStack.append( '<span class="StackHashIgnored">%s</span><br/>' % sHTMLAddress) # If there are not enouogh id-able stack frames, there may be many trailing "_"-s; remove these. Also, if there # was not id, or nothing is left after removing the "_"-s, use the id "##". oErrorReport.sStackId = sStackId.rstrip("_") or "##" if oStack.bPartialStack: asHTMLStack.append("... (rest of the stack was ignored)<br/>") oErrorReport.sHTMLStack = "".join(asHTMLStack) # Use a function for the id oCodeIdFrame = oTopmostRelevantFunctionFrame or oTopmostRelevantModuleFrame oErrorReport.sCodeId = oCodeIdFrame and oCodeIdFrame.sSimplifiedAddress or "(unknown)" # Create the location description oErrorReport.sCodeDescription = oTopmostRelevantFrame and oTopmostRelevantFrame.sAddress or "(unknown)" # Get the binary's cdb name for retreiving version information: asBinaryCdbNames = oCdbWrapper.fasGetCdbIdsForModuleFileNameInCurrentProcess( oErrorReport.sProcessBinaryName) if not oCdbWrapper.bCdbRunning: return None assert len( asBinaryCdbNames ) > 0, "Cannot find binary %s module" % oErrorReport.sProcessBinaryName # If the binary is loaded as a module multiple times in the process, the first should be the binary that was # executed. # If this turns out to be wrong, this code should be switch to use "u @$exentry L1" to determine the cdb id of the # module in which the process's entry point is found. dsGetVersionCdbId_by_sBinaryName = { oErrorReport.sProcessBinaryName: asBinaryCdbNames[0] } # Get the id frame's module cdb name for retreiving version information: if oCodeIdFrame: dsGetVersionCdbId_by_sBinaryName[ oCodeIdFrame.oModule.sBinaryName] = oCodeIdFrame.oModule.sCdbId asHTMLBinaryInformation = [] for sBinaryName, sCdbId in dsGetVersionCdbId_by_sBinaryName.items(): asModuleInformationOutput = oCdbWrapper.fasSendCommandAndReadOutput( "lmv m *%s" % sCdbId) if not oCdbWrapper.bCdbRunning: return None # Sample output: # |0:004> lmv M firefox.exe # |start end module name # |00000000`011b0000 00000000`0120f000 firefox (deferred) # | Image path: firefox.exe # | Image name: firefox.exe # | Timestamp: Thu Aug 13 03:23:30 2015 (55CBF192) # | CheckSum: 0006133B # | ImageSize: 0005F000 # | File version: 40.0.2.5702 # | Product version: 40.0.2.0 # | File flags: 0 (Mask 3F) # | File OS: 4 Unknown Win32 # | File type: 2.0 Dll # | File date: 00000000.00000000 # | Translations: 0000.04b0 # | CompanyName: Mozilla Corporation # | ProductName: Firefox # | InternalName: Firefox # | OriginalFilename: firefox.exe # | ProductVersion: 40.0.2 # | FileVersion: 40.0.2 # | FileDescription: Firefox # | LegalCopyright: (c)Firefox and Mozilla Developers; available under the MPL 2 license. # | LegalTrademarks: Firefox is a Trademark of The Mozilla Foundation. # | Comments: Firefox is a Trademark of The Mozilla Foundation. # The first two lines can be skipped. asHTMLBinaryInformation.append( sHTMLBinaryInformationTemplate % { "sName": fsHTMLEncode(sBinaryName), "sInformation": "".join([ "%s<br/>" % fsHTMLEncode(x) for x in asModuleInformationOutput[2:] ]), }) oErrorReport.sHTMLBinaryInformation = "".join(asHTMLBinaryInformation) # TODO: At some point the instruction that cause the exception could be added. Use "u @$eventip L1" to retreive it. return oErrorReport
def foCreate(cSelf, oCrashInfo, uCode, sCodeDescription): oProcess = cProcess.foCreate(oCrashInfo) oSelf = cSelf(oProcess, uCode, sCodeDescription) # We do this twice to make sure symbols are loaded the first time, which may create additional symbal warnings and # errors that makes the output harder to parse. The second time, there will be no such output, so we can parse it # a lot easier. asExceptionRecord = oCrashInfo._fasSendCommandAndReadOutput(".exr -1") if asExceptionRecord is None: return None asExceptionRecord = oCrashInfo._fasSendCommandAndReadOutput(".exr -1") if asExceptionRecord is None: return None uParameterCount = None uParameterIndex = None for sLine in asExceptionRecord: oNameValueMatch = re.match( r"^\s*%s\s*$" % ( r"(\w+)(?:\[(\d+)\])?\:\s+" # (name) optional{ "[" (index) "]" } ":" whitespace r"([0-9A-F`]+)" # (value) r"(?:\s+\((.*)\))?" # optional{ whitespace "(" (symbol || description) ")" } ), sLine, re.I) if oNameValueMatch: sName, sIndex, sValue, sDetails = oNameValueMatch.groups() uValue = int(sValue.replace("`", ""), 16) if sName == "ExceptionAddress": oSelf.uAddress = uValue oSelf.sAddressSymbol = sDetails elif sName == "ExceptionCode": assert uValue == uCode, \ "Exception record has an unexpected ExceptionCode value (0x%08X vs 0x%08X)" % (uValue, uCode) assert sDetails is None or sDetails == sCodeDescription, \ "Exception record has an unexpected ExceptionCode description (%s vs %s)" % \ (repr(sDetails), repr(sCodeDescription)) elif sName == "ExceptionFlags": oSelf.uFlags = uValue elif sName == "NumberParameters": uParameterCount = uValue uParameterIndex = 0 oSelf.auParameters = [] elif sName == "Parameter": assert int(sIndex, 16) == uParameterIndex, \ "Unexpected parameter #0x%s vs 0x%X" % (sIndex, uParameterIndex) oSelf.auParameters.append(uValue) uParameterIndex += 1 else: raise AssertionError("Unknown exception record value %s" % sLine) elif oSelf.sDetails is None: oSelf.sDetails = sLine else: raise AssertionError("Superfluous exception record line %s" % sLine) assert oSelf.uAddress is not None, \ "Exception record is missing an ExceptionAddress value" assert oSelf.uFlags is not None, \ "Exception record is missing an ExceptionFlags value" assert uParameterCount is not None, \ "Exception record is missing an NumberParameters value" assert uParameterCount == len(oSelf.auParameters), \ "Unexpected number of parameters (%d vs %d)" % (len(oSelf.auParameters), uParameterCount) # Now handle the information in the exception record and perform additional tasks as needed. Create an exception # id that uniquely identifies the exception and a description of the exception. if uCode == 0xC000027B: # Parameter[0] = paStowedExceptionInformationArray; # Parameter[1] = uStowedExceptionInformationArrayLength; assert len(oSelf.auParameters) == 2, \ "Unexpected number of WinRT language exception parameters (%d vs 2)" % len(oSelf.auParameters) pStowedExceptionsAddress = oSelf.auParameters[0] uStowedExceptionsCount = oSelf.auParameters[1] assert uStowedExceptionsCount == 1, \ "Unexpected number of WinRT language exception stowed exceptions (%d vs 1)" % uStowedExceptionsCount # The stowed exception replaces this exception: return cStowedException.foCreate(oCrashInfo, oProcess, pStowedExceptionsAddress) elif uCode == STATUS_ACCESS_VIOLATION: # Access violation: add the type of operation and the location to the exception id. sViolationTypeId = "AV" + { 0: "R", 1: "W", 8: "E" }.get(oSelf.auParameters[0], "?") sViolationTypeDescription = { 0: "reading", 1: "writing", 8: "executing" }.get(oSelf.auParameters[0], "?") uAddress = oSelf.auParameters[1] sAddressId = fsGetAddressId(uAddress) oSelf.sTypeId = "%s@%s" % (sViolationTypeId, sAddressId) oSelf.sDescription = "%s while %s memory at 0x%08X (%s)" % ( sCodeDescription, sViolationTypeDescription, uAddress, sAddressId) oSelf.sSecurityImpact = ( (uAddress > 0xFFFF0000 or uAddress < 0x10000) and "Not a security issue" or "Probably a security issue") else: oSelf.sTypeId = fsGetExceptionTypeId(uCode) oSelf.sDescription = "%s (code 0x%08X)" % (sCodeDescription, uCode) oSelf.sSecurityImpact = fsGetSecurityImpact(uCode) # Get the stack oSelf.oStack = cStack.foCreate(oCrashInfo, oSelf.oProcess) if oSelf.oStack is None: return None return oSelf