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 foCreateForException(cBugReport, oProcess, uExceptionCode, sExceptionDescription, bApplicationCannotHandleException): uStackFramesCount = dxConfig["uMaxStackFramesCount"] if uExceptionCode == STATUS_STACK_OVERFLOW: # In order to detect a recursion loop, we need more stack frames: uStackFramesCount += (dxConfig["uMinStackRecursionLoops"] + 1) * dxConfig["uMaxStackRecursionLoopSize"] oException = cException.foCreate(oProcess, uExceptionCode, sExceptionDescription, bApplicationCannotHandleException) # 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( oProcess=oProcess, sBugTypeId=oException.sTypeId, sBugDescription=oException.sDescription, sSecurityImpact=oException.sSecurityImpact, uStackFramesCount=uStackFramesCount, ) # Apply the first round of translations fApplyBugTranslationsToBugReport(oBugReport) # Perform exception specific analysis: if oException.uCode in dfoAnalyzeException_by_uExceptionCode: oBugReport = dfoAnalyzeException_by_uExceptionCode[ oException.uCode](oBugReport, oProcess, oException) if oBugReport: # Apply another round of translations fApplyBugTranslationsToBugReport(oBugReport) return oBugReport
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"] * dxBugIdConfig["uMaxStackRecursionLoopSize"] 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; # 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. oBugReport = cBugReport( oCdbWrapper = oCdbWrapper, sBugTypeId = oException.sTypeId, sBugDescription = oException.sDescription, sSecurityImpact = oException.sSecurityImpact, 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.foPostProcess(oCdbWrapper);
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"] * dxBugIdConfig[ "uMaxStackRecursionLoopSize"] 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 # 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. oBugReport = cBugReport( oCdbWrapper=oCdbWrapper, sBugTypeId=oException.sTypeId, sBugDescription=oException.sDescription, sSecurityImpact=oException.sSecurityImpact, 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(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 _fDebugApplication(oSelf): try: # Read the initial cdb output if oSelf._fasReadOutput() is None: return None; # Make a list of all the cdb commands that need to be execute to initialize and start the application. asInitialCommands = []; # if requested, resume all threads in current process. asInitialCommands.append(".childdbg 1"); if oSelf._bResumeThreads: asInitialCommands.append("~*m"); # if requested, attach to additional processes and optionally resume all threads in those as well. for uAttachProcessId in oSelf._auAttachProcessIds: asInitialCommands.append(".attach 0n%d;g" % uAttachProcessId); asInitialCommands.append(".childdbg 1"); if oSelf._bResumeThreads: asInitialCommands.append("~*m"); # request second chance debugger break for certain exceptions that indicate the application has a bug. for sException in asDetectedExceptions: if dxCrashInfoConfig.get("bOutputFirstChanceExceptions", False): asInitialCommands.append("sxe %s" % sException); else: asInitialCommands.append("sxd %s" % sException); # ignore certain other exceptions for sException in asIgnoredExceptions: asInitialCommands.append("sxi %s" % sException); # if epr is disabled, the debugger will silently exit when the application terminates. # To distinguish this from other unexpected terminations of the debugger, epr is enabled and the "g" command is # executed whenever a process terminates. This will continue execution of the application until the last process # is terminated, at which point cdb outputs an error message. This error message is detected to determine that # the application has terminated without crashing. asInitialCommands.append("sxe -c \"g\" epr"); # Execute all commands in the list and stop if cdb terminates in the mean time. for sCommand in asInitialCommands: if oSelf._fasSendCommandAndReadOutput(sCommand) is None: return; oSelf._fApplicationStartedCallback(); # The application is now started, read its output until an exception is detected: while 1: asExceptionDetectedOutput = oSelf._fasSendCommandAndReadOutput("g"); if asExceptionDetectedOutput is None: return; # Scan backwards through the output to detect the exception that occured: for uIndex in xrange(len(asExceptionDetectedOutput) - 1, -1, -1): sLine = asExceptionDetectedOutput[uIndex]; # Event output looks like this: # |(16c0.11c): Access violation - code c0000005 (!!! second chance !!!) # |(273c.1f1c): Security check failure or stack buffer overrun - code c0000409 (!!! second chance !!!) oEventMatch = re.match(r"^\s*%s\s*$" % ( r"\([0-9A-F]+\.[0-9A-F]+\): " # "(" process id "." thread id "): " r"(.*?)" # (exception description)" r" \- code " # " - code " r"([0-9A-F`]+)" # (exception code) r" \(!*\s*(first|second) chance\s*!*\)" # " (first chance)" or " (!!! second chance !!!)" ), sLine, re.I); if oEventMatch: sDescription, sCode, sChance = oEventMatch.groups(); uCode = int(sCode.replace("`", ""), 16); break; oTerminatedMatch = re.match(r"^\s*\^ No runnable debuggees error in '.*'\s*$", sLine); if oTerminatedMatch: oSelf._bExpectTermination = True; asDebuggerOutput = oSelf._fasSendCommandAndReadOutput("q"); assert asDebuggerOutput is None, "Debugger did not terminate"; return; else: raise AssertionError( "Could not find what caused the debugger to break into the application!\r\n%s" % "\r\n".join(asExceptionDetectedOutput) ); if sChance == "second": break; # we've found something interesting # get a stack for this first chance exception (in case it turns out to be interesting later) if oSelf._fasSendCommandAndReadOutput("kn 0x%X" % dxCrashInfoConfig.get("uMaxStackFramesCount", 50)) is None: return; # continue the application. # Report that an exception has been detected. oSelf._fErrorDetectedCallback(); # Gather exception information: oException = cException.foCreate(oSelf, uCode, sDescription); if oException is None: return None; # Sve the exception report for returning when we're finished. oSelf._oErrorReport = cErrorReport.foCreateFromException(oException, oSelf._asIO); # terminate the debugger. oSelf._bExpectTermination = True; asDebuggerOutput = oSelf._fasSendCommandAndReadOutput("q"); assert asDebuggerOutput is None, "Debugger did not terminate"; except Exception, oException: oSelf._fInternalExceptionCallback(oException); raise;
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 _fDebugApplication(oSelf): try: # Read the initial cdb output if oSelf._fasReadOutput() is None: return None # Make a list of all the cdb commands that need to be execute to initialize and start the application. asInitialCommands = [] # if requested, resume all threads in current process. asInitialCommands.append(".childdbg 1") if oSelf._bResumeThreads: asInitialCommands.append("~*m") # if requested, attach to additional processes and optionally resume all threads in those as well. for uAttachProcessId in oSelf._auAttachProcessIds: asInitialCommands.append(".attach 0n%d;g" % uAttachProcessId) asInitialCommands.append(".childdbg 1") if oSelf._bResumeThreads: asInitialCommands.append("~*m") # request second chance debugger break for certain exceptions that indicate the application has a bug. for sException in asDetectedExceptions: if dxCrashInfoConfig.get("bOutputFirstChanceExceptions", False): asInitialCommands.append("sxe %s" % sException) else: asInitialCommands.append("sxd %s" % sException) # ignore certain other exceptions for sException in asIgnoredExceptions: asInitialCommands.append("sxi %s" % sException) # if epr is disabled, the debugger will silently exit when the application terminates. # To distinguish this from other unexpected terminations of the debugger, epr is enabled and the "g" command is # executed whenever a process terminates. This will continue execution of the application until the last process # is terminated, at which point cdb outputs an error message. This error message is detected to determine that # the application has terminated without crashing. asInitialCommands.append("sxe -c \"g\" epr") # Execute all commands in the list and stop if cdb terminates in the mean time. for sCommand in asInitialCommands: if oSelf._fasSendCommandAndReadOutput(sCommand) is None: return oSelf._fApplicationStartedCallback() # The application is now started, read its output until an exception is detected: while 1: asExceptionDetectedOutput = oSelf._fasSendCommandAndReadOutput( "g") if asExceptionDetectedOutput is None: return # Scan backwards through the output to detect the exception that occured: for uIndex in xrange( len(asExceptionDetectedOutput) - 1, -1, -1): sLine = asExceptionDetectedOutput[uIndex] # Event output looks like this: # |(16c0.11c): Access violation - code c0000005 (!!! second chance !!!) # |(273c.1f1c): Security check failure or stack buffer overrun - code c0000409 (!!! second chance !!!) oEventMatch = re.match( r"^\s*%s\s*$" % ( r"\([0-9A-F]+\.[0-9A-F]+\): " # "(" process id "." thread id "): " r"(.*?)" # (exception description)" r" \- code " # " - code " r"([0-9A-F`]+)" # (exception code) r" \(!*\s*(first|second) chance\s*!*\)" # " (first chance)" or " (!!! second chance !!!)" ), sLine, re.I) if oEventMatch: sDescription, sCode, sChance = oEventMatch.groups() uCode = int(sCode.replace("`", ""), 16) break oTerminatedMatch = re.match( r"^\s*\^ No runnable debuggees error in '.*'\s*$", sLine) if oTerminatedMatch: oSelf._bExpectTermination = True asDebuggerOutput = oSelf._fasSendCommandAndReadOutput( "q") assert asDebuggerOutput is None, "Debugger did not terminate" return else: raise AssertionError( "Could not find what caused the debugger to break into the application!\r\n%s" % "\r\n".join(asExceptionDetectedOutput)) if sChance == "second": break # we've found something interesting # get a stack for this first chance exception (in case it turns out to be interesting later) if oSelf._fasSendCommandAndReadOutput( "kn 0x%X" % dxCrashInfoConfig.get( "uMaxStackFramesCount", 50)) is None: return # continue the application. # Report that an exception has been detected. oSelf._fErrorDetectedCallback() # Gather exception information: oException = cException.foCreate(oSelf, uCode, sDescription) if oException is None: return None # Sve the exception report for returning when we're finished. oSelf._oErrorReport = cErrorReport.foCreateFromException( oException, oSelf._asIO) # terminate the debugger. oSelf._bExpectTermination = True asDebuggerOutput = oSelf._fasSendCommandAndReadOutput("q") assert asDebuggerOutput is None, "Debugger did not terminate" except Exception, oException: oSelf._fInternalExceptionCallback(oException) raise
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