Esempio n. 1
0
    def fPostProcess(oBugReport, oCdbWrapper):
        # Calculate sStackId, determine sBugLocation and optionally create and return sStackHTML.
        aoRelevantStackFrames, sStackHTML = cBugReport_fxProcessStack(
            oBugReport, oCdbWrapper)
        oBugReport.sId = "%s %s" % (oBugReport.sBugTypeId, oBugReport.sStackId)
        if oBugReport.sSecurityImpact is None:
            oBugReport.sSecurityImpact = "Denial of Service"

        # Find cModule for main process binary (i.e. the .exe)
        aoProcessBinaryModules = oCdbWrapper.faoGetModulesForFileNameInCurrentProcess(
            oBugReport.sProcessBinaryName)
        if not oCdbWrapper.bCdbRunning: return None
        assert len(
            aoProcessBinaryModules
        ) > 0, "Cannot find binary %s module" % oBugReport.sProcessBinaryName
        # Add main process binary version information to bug report. If the binary is loaded as a module multiple times
        # in the process, the first should be the binary that was executed.
        oMainModule = aoProcessBinaryModules[0]

        # Find cModule for bug binary (i.e. the module in which the bug is located)
        oBugModule = aoRelevantStackFrames and aoRelevantStackFrames[0].oModule
        # If bug niary and main binary are not the same, gather information for both of them:
        aoRelevantModules = [oMainModule]
        if oBugModule and oBugModule != oMainModule:
            aoRelevantModules.append(oBugModule)
        # Add relevant binaries information to cBugReport and optionally to the HTML report.
        if oCdbWrapper.bGetDetailsHTML:  # Generate sDetailsHTML?
            # If a HTML report is requested, these will be used later on to construct it.
            asBinaryInformationHTML = []
            asBinaryVersionHTML = []
        oBugReport.asVersionInformation = []
        for oModule in aoRelevantModules:
            sBinaryInformationHTML = oModule.fsGetInformationHTML(oCdbWrapper)
            if not oCdbWrapper.bCdbRunning: return None
            if oCdbWrapper.bGetDetailsHTML:  # Generate sDetailsHTML?
                asBinaryInformationHTML.append(sBinaryInformationHTML)
                asBinaryVersionHTML.append(
                    "<b>%s</b>: %s" %
                    (oModule.sBinaryName, oModule.sFileVersion
                     or oModule.sTimestamp or "unknown"))
            oBugReport.asVersionInformation.append(
                "%s %s" % (oModule.sBinaryName, oModule.sFileVersion
                           or oModule.sTimestamp or "unknown"))

        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": "<span class=\"Stack\">%s</span>" % sStackHTML
                })

            # Add exception specific blocks if needed:
            asBlocksHTML += oBugReport.asExceptionSpecificBlocksHTML

            if oBugReport.bRegistersRelevant:
                # Create and add registers block
                asRegisters = oCdbWrapper.fasSendCommandAndReadOutput(
                    "rM 0x%X; $$ Get register information" %
                    (0x1 + 0x4 + 0x8 + 0x10 + 0x20 + 0x40),
                    bOutputIsInformative=True,
                )
                if not oCdbWrapper.bCdbRunning: return None
                sRegistersHTML = "<br/>".join(
                    [oCdbWrapper.fsHTMLEncode(s) for s in asRegisters])
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName":
                        "Registers",
                        "sContent":
                        "<span class=\"Registers\">%s</span>" % sRegistersHTML,
                    })

            # Add relevant memory blocks in order if needed
            auAddresses = set()
            for (sDescription, uStartAddress,
                 uSize) in oBugReport.atxMemoryDumps:
                auAddresses.add(uStartAddress)
            for uAddress in sorted(list(auAddresses)):
                for (sDescription, uStartAddress,
                     uSize) in oBugReport.atxMemoryDumps:
                    if uStartAddress == uAddress:
                        sMemoryDumpHTML = cBugReport_fsMemoryDumpHTML(
                            oBugReport, oCdbWrapper, sDescription,
                            uStartAddress, uSize)
                        if not oCdbWrapper.bCdbRunning: return None
                        if sMemoryDumpHTML:
                            asBlocksHTML.append(
                                sBlockHTMLTemplate % {
                                    "sName":
                                    sDescription,
                                    "sContent":
                                    "<span class=\"Memory\">%s</span>" %
                                    sMemoryDumpHTML,
                                })

            # Create and add disassembly blocks if needed:
            uLastInstructionPointer = None
            for oFrame in aoRelevantStackFrames:
                # Inlined functions do not add a new location to disassemble.
                if uLastInstructionPointer != oFrame.uInstructionPointer:
                    uLastInstructionPointer = oFrame.uInstructionPointer
                    uFrameNumber = oFrame.uNumber + 1
                    # Make it base 1, rather than 0
                    if oFrame.uNumber == 0:
                        sBeforeAddressInstructionDescription = None
                        sAtAddressInstructionDescription = "current instruction"
                    else:
                        sBeforeAddressInstructionDescription = "call"
                        sAtAddressInstructionDescription = "return address"
                    sFrameDisassemblyHTML = cBugReport_fsGetDisassemblyHTML(oBugReport, oCdbWrapper, oFrame.uInstructionPointer, \
                        sBeforeAddressInstructionDescription, sAtAddressInstructionDescription)
                    if not oCdbWrapper.bCdbRunning: return None
                    if sFrameDisassemblyHTML:
                        asBlocksHTML.append(
                            sBlockHTMLTemplate % {
                                "sName":
                                "Disassembly of stack frame %d at %s" %
                                (uFrameNumber, oFrame.sAddress),
                                "sContent":
                                "<span class=\"Disassembly\">%s</span>" %
                                sFrameDisassemblyHTML,
                            })

            # Find cModule for main process binary (i.e. the .exe)
            aoProcessBinaryModules = oCdbWrapper.faoGetModulesForFileNameInCurrentProcess(
                oBugReport.sProcessBinaryName)
            if not oCdbWrapper.bCdbRunning: return None
            assert len(
                aoProcessBinaryModules
            ) > 0, "Cannot find binary %s module" % oBugReport.sProcessBinaryName
            # Add main process binary version information to bug report. If the binary is loaded as a module multiple times
            # in the process, the first should be the binary that was executed.
            oMainModule = aoProcessBinaryModules[0]

            # Find cModule for bug binary (i.e. the module in which the bug is located)
            oBugModule = aoRelevantStackFrames and aoRelevantStackFrames[
                0].oModule
            # If bug niary and main binary are not the same, gather information for both of them:
            aoRelevantModules = [oMainModule]
            if oBugModule and oBugModule != oMainModule:
                aoRelevantModules.append(oBugModule)
            # Add relevant binaries information to cBugReport and HTML report.
            sBinaryInformationHTML = "<br/><br/>".join(asBinaryInformationHTML)
            sBinaryVersionHTML = "<br/>".join(
                asBinaryVersionHTML) or "not available"
            if sBinaryInformationHTML:
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName":
                        "Binary information",
                        "sContent":
                        "<span class=\"BinaryInformation\">%s</span>" %
                        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:]
            asBlocksHTML.append(
                sBlockHTMLTemplate % {
                    "sName": "Application and cdb output log",
                    "sContent": sCdbStdIOHTML
                })
            # Stick everything together.
            oBugReport.sDetailsHTML = sDetailsHTMLTemplate % {
              "sId": oCdbWrapper.fsHTMLEncode(oBugReport.sId),
              "sBugLocation": oCdbWrapper.fsHTMLEncode(oBugReport.sBugLocation),
              "sBugDescription": oCdbWrapper.fsHTMLEncode(oBugReport.sBugDescription),
              "sBinaryVersion": sBinaryVersionHTML,
              "sOptionalSource": oBugReport.sBugSourceLocation and \
                  "<tr><td>Source: </td><td>%s</td></tr>" % oBugReport.sBugSourceLocation or "",
              "sSecurityImpact": (oBugReport.sSecurityImpact == "Denial of Service" and
                  "%s" or '<span class="SecurityImpact">%s</span>') % oCdbWrapper.fsHTMLEncode(oBugReport.sSecurityImpact),
              "sOptionalCommandLine": oBugReport.oCdbWrapper.asApplicationCommandLine and \
                  "<tr><td>Command line: </td><td>%s</td></tr>" % oBugReport.oCdbWrapper.asApplicationCommandLine or "",
              "sBlocks": "".join(asBlocksHTML),
              "sCdbStdIO": sCdbStdIOHTML,
              "sBugIdVersion": sVersion,
            }

        # See if a dump should be saved
        if dxBugIdConfig["bSaveDump"]:
            # We'd like a dump file name base on the BugId, but the later may contain characters that are not valid in a file name
            sDesiredDumpFileName = "%s @ %s.dmp" % (oBugReport.sId,
                                                    oBugReport.sBugLocation)
            # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId.
            # Unfortunately, we cannot use Unicode as the communication channel with cdb is ASCII.
            sValidDumpFileName = FileSystem.fsValidName(sDesiredDumpFileName,
                                                        bUnicode=False)
            sOverwriteFlag = dxBugIdConfig["bOverwriteDump"] and "/o" or ""
            oCdbWrapper.fasSendCommandAndReadOutput( \
                ".dump %s /ma \"%s\"; $$ Save dump to file" % (sOverwriteFlag, sValidDumpFileName))
            if not oCdbWrapper.bCdbRunning: return
Esempio n. 2
0
    def fPostProcess(oBugReport, oCdbWrapper):
        # Calculate sStackId, determine sBugLocation and optionally create and return sStackHTML.
        aoStackFramesPartOfId, sStackHTML = cBugReport_fxProcessStack(
            oBugReport, oCdbWrapper)
        oBugReport.sId = "%s %s" % (oBugReport.sBugTypeId, oBugReport.sStackId)
        if oBugReport.sSecurityImpact is None:
            oBugReport.sSecurityImpact = "Denial of Service"

        # If bug binary and main binary are not the same, gather information for both of them:
        aoRelevantModules = [oBugReport.oProcess.oMainModule]
        # Find the Module in which the bug is reported and add it to the relevant list if it's not there already.
        for oStackFrame in aoStackFramesPartOfId:
            if oStackFrame.oModule:
                if oStackFrame.oModule != oBugReport.oProcess.oMainModule:
                    aoRelevantModules.append(oStackFrame.oModule)
                break
        # Add relevant binaries information to cBugReport and optionally to the HTML report.
        if oCdbWrapper.bGenerateReportHTML:
            # If a HTML report is requested, these will be used later on to construct it.
            asBinaryInformationHTML = []
            asBinaryVersionHTML = []
        oBugReport.asVersionInformation = []
        for oModule in aoRelevantModules:
            # This function populates the version properties of the oModule object and returns HTML if a report is needed.
            oBugReport.asVersionInformation.append(
                "%s %s (%s)" %
                (oModule.sBinaryName, oModule.sFileVersion
                 or oModule.sTimestamp or "unknown", oModule.sISA))
            if oCdbWrapper.bGenerateReportHTML:
                asBinaryInformationHTML.append(oModule.sInformationHTML)
                asBinaryVersionHTML.append("<b>%s</b>: %s (%s)" % \
                    (oModule.sBinaryName, oModule.sFileVersion or oModule.sTimestamp or "unknown", oModule.sISA))

        if oCdbWrapper.bGenerateReportHTML:
            # Create HTML details
            asBlocksHTML = []
            # Create and add important output block if needed
            if oBugReport.sImportantOutputHTML:
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName": "Potentially important application output",
                        "sCollapsed": "Collapsible",  # ...but not Collapsed
                        "sContent": oBugReport.sImportantOutputHTML,
                    })

            # Add stack block
            asBlocksHTML.append(
                sBlockHTMLTemplate % {
                    "sName": "Stack",
                    "sCollapsed": "Collapsible",  # ...but not Collapsed
                    "sContent": "<span class=\"Stack\">%s</span>" % sStackHTML
                })

            # Add exception specific blocks if needed:
            asBlocksHTML += oBugReport.asExceptionSpecificBlocksHTML

            if oBugReport.bRegistersRelevant:
                # Create and add registers block
                asRegisters = oCdbWrapper.fasExecuteCdbCommand(
                    sCommand="rM 0x%X;" %
                    (0x1 + 0x4 + 0x8 + 0x10 + 0x20 + 0x40),
                    sComment="Get register information",
                    bOutputIsInformative=True,
                )
                sRegistersHTML = "<br/>".join([
                    oCdbWrapper.fsHTMLEncode(s, uTabStop=8)
                    for s in asRegisters
                ])
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName":
                        "Registers",
                        "sCollapsed":
                        "Collapsed",
                        "sContent":
                        "<span class=\"Registers\">%s</span>" % sRegistersHTML,
                    })

            # Add relevant memory blocks in order if needed
            for uStartAddress in sorted(oBugReport.__dtxMemoryDumps.keys()):
                (uEndAddress,
                 sDescription) = oBugReport.__dtxMemoryDumps[uStartAddress]
                sMemoryDumpHTML = cBugReport_fsMemoryDumpHTML(
                    oBugReport, oCdbWrapper, sDescription, uStartAddress,
                    uEndAddress)
                if sMemoryDumpHTML:
                    asBlocksHTML.append(
                        sBlockHTMLTemplate % {
                            "sName":
                            sDescription,
                            "sCollapsed":
                            "Collapsed",
                            "sContent":
                            "<span class=\"Memory\">%s</span>" %
                            sMemoryDumpHTML,
                        })

            # Create and add disassembly blocks if needed:
            for oFrame in aoStackFramesPartOfId:
                if oFrame.uIndex == 0:
                    sBeforeAddressInstructionDescription = None
                    sAtAddressInstructionDescription = "current instruction"
                else:
                    sBeforeAddressInstructionDescription = "call"
                    sAtAddressInstructionDescription = "return address"
                sFrameDisassemblyHTML = cBugReport_fsGetDisassemblyHTML(oBugReport, oCdbWrapper, oFrame.uInstructionPointer, \
                    sBeforeAddressInstructionDescription, sAtAddressInstructionDescription)
                if sFrameDisassemblyHTML:
                    asBlocksHTML.append(
                        sBlockHTMLTemplate % {
                            "sName":
                            "Disassembly of stack frame %d at %s" %
                            (oFrame.uIndex + 1, oFrame.sAddress),
                            "sCollapsed":
                            "Collapsed",
                            "sContent":
                            "<span class=\"Disassembly\">%s</span>" %
                            sFrameDisassemblyHTML,
                        })

            # Add relevant binaries information to cBugReport and HTML report.
            sBinaryInformationHTML = "<br/><br/>".join(asBinaryInformationHTML)
            sBinaryVersionHTML = "<br/>".join(
                asBinaryVersionHTML) or "not available"
            if sBinaryInformationHTML:
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName":
                        "Binary information",
                        "sCollapsed":
                        "Collapsed",
                        "sContent":
                        "<span class=\"BinaryInformation\">%s</span>" %
                        sBinaryInformationHTML
                    })
            # Get process integrity level.
            if oBugReport.oProcess.uIntegrityLevel is None:
                sOptionalIntegrityLevelHTML = "(unknown)"
            else:
                sIntegrityLevel = " ".join([
                    s for s in [
                        {
                            0: "Untrusted",
                            1: "Low",
                            2: "Medium",
                            3: "High",
                            4: "System"
                        }.get(oBugReport.oProcess.uIntegrityLevel >> 12,
                              "Unknown"),
                        "Integrity",
                        oBugReport.oProcess.uIntegrityLevel & 0x100 and "Plus",
                    ] if s
                ])
                if oBugReport.oProcess.uIntegrityLevel >= 0x3000:
                    sIntegrityLevel += "; this process appears to run with elevated privileges!"
                elif oBugReport.oProcess.uIntegrityLevel >= 0x2000:
                    sIntegrityLevel += "; this process appears to not be sandboxed!"
                else:
                    sIntegrityLevel += "; this process appears to be sandboxed."
                sOptionalIntegrityLevelHTML = "<tr><td>Integrity level: </td><td>0x%X (%s)</td></tr>" % \
                    (oBugReport.oProcess.uIntegrityLevel, sIntegrityLevel)
            # Add Cdb IO to HTML report
            asBlocksHTML.append(
                sBlockHTMLTemplate % {
                    "sName": "Application and cdb output log",
                    "sCollapsed": "Collapsed",
                    "sContent": oCdbWrapper.sCdbIOHTML,
                })
            # Create the report using all available information, or a limit amount of information if there is not enough
            # memory to do that.
            while asBlocksHTML:
                bReportTruncated = False
                try:
                    oBugReport.sReportHTML = sReportHTMLTemplate % {
                      "sId": oCdbWrapper.fsHTMLEncode(oBugReport.sId),
                      "sBugLocation": oCdbWrapper.fsHTMLEncode(oBugReport.sBugLocation),
                      "sBugDescription": oCdbWrapper.fsHTMLEncode(oBugReport.sBugDescription),
                      "sBinaryVersion": sBinaryVersionHTML,
                      "sOptionalSource": oBugReport.sBugSourceLocation and \
                          "<tr><td>Source: </td><td>%s</td></tr>" % oBugReport.sBugSourceLocation or "",
                      "sSecurityImpact": (oBugReport.sSecurityImpact == "Denial of Service" and
                          "%s" or '<span class="SecurityImpact">%s</span>') % oCdbWrapper.fsHTMLEncode(oBugReport.sSecurityImpact),
                      "sOptionalIntegrityLevel": sOptionalIntegrityLevelHTML,
                      "sOptionalApplicationArguments": oCdbWrapper.asApplicationArguments and \
                          "<tr><td>Arguments: </td><td>%s</td></tr>" % oCdbWrapper.asApplicationArguments or "",
                      "sBlocks": "\r\n".join(asBlocksHTML) +
                          (bReportTruncated and "\r\n<hr/>The report was truncated because there was not enough memory available to add all information available." or ""),
                      "sBugIdVersion": oVersionInformation.sCurrentVersion,
                    }
                except MemoryError:
                    # We cannot add everything, so let's remove a block of information to free up some memory and reduce the size
                    # of the final report before we try again. It makes sense to remove the last block, as they are ordered
                    # (somewhat) by how useful the information is to the user, the last block containing less useful information
                    # than the first.
                    asBlocksHTML.pop()
                    # Add a notice to the report about it being truncated.
                    bReportTruncated = True
                else:
                    break
            else:
                # There is so little memory available that we cannot seem to be able to create a report at all.
                # This is highly unlikely, but let's try to handle every eventuality.
                oBugReport.sReportHTML = "The report was <b>NOT</b> created because there was not enough memory available to add any information."
        # Remove the process object from the bug report and add the process binary name; we only provide basic types.
        oBugReport.sProcessBinaryName = oBugReport.oProcess.sBinaryName
        del oBugReport.oProcess

        # See if a dump should be saved
        if dxConfig["bSaveDump"]:
            # We'd like a dump file name base on the BugId, but the later may contain characters that are not valid in a file name
            sDesiredDumpFileName = "%s @ %s.dmp" % (oBugReport.sId,
                                                    oBugReport.sBugLocation)
            # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId.
            # Unfortunately, we cannot use Unicode as the communication channel with cdb is ASCII.
            sValidDumpFileName = FileSystem.fsValidName(sDesiredDumpFileName,
                                                        bUnicode=False)
            if len(dxConfig["sDumpPath"]) != 0:
                sValidDumpFileName = dxConfig["sDumpPath"] + sValidDumpFileName
            sOverwriteFlag = dxConfig["bOverwriteDump"] and "/o" or ""
            sMiniOptions = dxConfig["sMiniOptions"]
            oCdbWrapper.fasExecuteCdbCommand( \
              sCommand = ".dump %s /%s \"%s\";" % (sOverwriteFlag, sMiniOptions, sValidDumpFileName),
              sComment = "Save dump to file",
            )
Esempio n. 3
0
 def fxProcessStack(oBugReport, oCdbWrapper, oProcess, oStack):
     return cBugReport_fxProcessStack(oBugReport, oCdbWrapper, oProcess,
                                      oStack)
Esempio n. 4
0
    def fPostProcess(oBugReport, oCdbWrapper):
        # Calculate sStackId, determine sBugLocation and optionally create and return sStackHTML.
        aoRelevantStackFrames, sStackHTML = cBugReport_fxProcessStack(
            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": "<span class=\"Stack\">%s</span>" % sStackHTML
                })

            # Add exception specific blocks if needed:
            asBlocksHTML += oBugReport.asExceptionSpecificBlocksHTML

            if oBugReport.bRegistersRelevant:
                # Create and add registers block
                asRegisters = oCdbWrapper.fasSendCommandAndReadOutput(
                    "rM 0x%X; $$ Get register information" %
                    (0x1 + 0x4 + 0x8 + 0x10 + 0x20 + 0x40),
                    bOutputIsInformative=True,
                )
                if not oCdbWrapper.bCdbRunning: return None
                sRegistersHTML = "<br/>".join(
                    [oCdbWrapper.fsHTMLEncode(s) for s in asRegisters])
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName":
                        "Registers",
                        "sContent":
                        "<span class=\"Registers\">%s</span>" % sRegistersHTML,
                    })

            # Add relevant memory blocks in order if needed
            for uSequentialAddress in sorted(
                    list(
                        set(oBugReport.duRelevantAddress_by_sDescription.
                            values()))):
                for (sDescription, uRelevantAddress
                     ) in oBugReport.duRelevantAddress_by_sDescription.items():
                    if uRelevantAddress == uSequentialAddress:
                        sRelevantMemoryHTML = cBugReport_fsGetRelevantMemoryHTML(
                            oBugReport, oCdbWrapper, uRelevantAddress,
                            sDescription)
                        if not oCdbWrapper.bCdbRunning: return None
                        if sRelevantMemoryHTML:
                            asBlocksHTML.append(
                                sBlockHTMLTemplate % {
                                    "sName":
                                    "Memory for %s" % sDescription,
                                    "sContent":
                                    "<span class=\"Memory\">%s</span>" %
                                    sRelevantMemoryHTML,
                                })

            # Create and add disassembly blocks if needed:
            for oFrame in aoRelevantStackFrames:
                sAddress = oFrame.sCdbSymbolOrAddress
                uAddress = oCdbWrapper.fuGetValue(sAddress)
                uFrameNumber = oFrame.uNumber + 1
                # Make it base 1, rather than 0
                if oFrame.uNumber == 0:
                    sBeforeAddressInstructionDescription = None
                    sAtAddressInstructionDescription = "current instruction"
                else:
                    sBeforeAddressInstructionDescription = "call"
                    sAtAddressInstructionDescription = "return address"
                    while uAddress is None and oFrame.oPreviousFrame and oFrame.oPreviousFrame not in aoRelevantStackFrames:
                        # Can't resolve symbol; try to get return address from previous non-inline frame, unless it was
                        # also relevant, in which case don't add disassembly for this second frame as it would be the same as
                        # the previous.
                        oFrame = oFrame.oPreviousFrame
                        uAddress = oFrame.uReturnAddress
                if uAddress is not None:
                    sFrameDisassemblyHTML = cBugReport_fsGetDisassemblyHTML(oBugReport, oCdbWrapper, uAddress, \
                        sBeforeAddressInstructionDescription, sAtAddressInstructionDescription)
                    if not oCdbWrapper.bCdbRunning: return None
                    if sFrameDisassemblyHTML:
                        asBlocksHTML.append(
                            sBlockHTMLTemplate % {
                                "sName":
                                "Disassembly of stack frame %d at %s" %
                                (uFrameNumber, sAddress),
                                "sContent":
                                "<span class=\"Disassembly\">%s</span>" %
                                sFrameDisassemblyHTML,
                            })

            # Add relevant binaries block
            aoProcessBinaryModules = oCdbWrapper.faoGetModulesForFileNameInCurrentProcess(
                oBugReport.sProcessBinaryName)
            if not oCdbWrapper.bCdbRunning: return None
            assert len(
                aoProcessBinaryModules
            ) > 0, "Cannot find binary %s module" % oBugReport.sProcessBinaryName
            # If the binary is loaded as a module multiple times in the process, the first should be the binary that was
            # executed.
            aoModules = aoProcessBinaryModules[:1]
            # Get the id frame's module cdb name for retreiving version information:
            oRelevantModule = aoRelevantStackFrames and aoRelevantStackFrames[
                0].oModule
            if oRelevantModule and oRelevantModule not in aoModules:
                aoModules.append(oRelevantModule)
            asBinaryInformationHTML = []
            asBinaryVersionHTML = []
            for oModule in aoModules:
                asBinaryInformationHTML.append(
                    oModule.fsGetInformationHTML(oCdbWrapper))
                if not oCdbWrapper.bCdbRunning: return None
                asBinaryVersionHTML.append(
                    "<b>%s</b>: %s" %
                    (oModule.sBinaryName, oModule.sFileVersion
                     or oModule.sTimestamp or "unknown"))
            sBinaryInformationHTML = "<br/><br/>".join(asBinaryInformationHTML)
            sBinaryVersionHTML = "<br/>".join(asBinaryVersionHTML)
            if sBinaryInformationHTML:
                asBlocksHTML.append(
                    sBlockHTMLTemplate % {
                        "sName":
                        "Binary information",
                        "sContent":
                        "<span class=\"BinaryInformation\">%s</span>" %
                        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:]
            asBlocksHTML.append(
                sBlockHTMLTemplate % {
                    "sName": "Application and cdb output log",
                    "sContent": sCdbStdIOHTML
                })
            # 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",
              "sBinaryVersionHTML": sBinaryVersionHTML,
              "sBlocks": "".join(asBlocksHTML),
              "sCdbStdIO": sCdbStdIOHTML,
              "sBugIdVersion": sVersion,
            }

        # See if a dump should be saved
        if dxBugIdConfig["bSaveDump"]:
            # We'd like a dump file name base on the BugId, but the later may contain characters that are not valid in a file name
            sDesiredDumpFileName = "%s @ %s.dmp" % (
                oBugId.oBugReport.sId, oBugId.oBugReport.sBugLocation)
            # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId.
            # Unfortunately, we cannot use Unicode as the communication channel with cdb is ASCII.
            sValidDumpFileName = FileSystem.fsValidName(sDesiredDumpFileName,
                                                        bUnicode=False)
            sOverwriteFlag = dxBugIdConfig["bOverwriteDump"] and "/o" or ""
            oCdbWrapper.fasSendCommandAndReadOutput( \
                ".dump %s /ma \"%s\"; $$ Save dump to file" % (sOverwriteFlag, sValidDumpFileName))
            if not oCdbWrapper.bCdbRunning: return