Пример #1
0
def foGetAllocationInformationForProcessAndAddress(
        oProcess, uAllocationInformationStartAddress):
    # DPH_HEAP_BLOCK structures are stored sequentially in a virtual allocation.
    oAllocationInformationVirtualAllocation = cVirtualAllocation(
        oProcess.uId, uAllocationInformationStartAddress)
    assert oAllocationInformationVirtualAllocation, \
        "Cannot find virtual allocation for page heap allocation information at 0x%X" % uAllocationInformationStartAddress
    if gbDebugOutput:
        print(",- oAllocationInformationVirtualAllocation ").ljust(80, "-")
        for sLine in oAllocationInformationVirtualAllocation.fasDump():
            print "| %s" % sLine
        print "`".ljust(80, "-")
    if not oAllocationInformationVirtualAllocation.bAllocated:
        return None
    # Read the page heap allocation information
    DPH_HEAP_BLOCK = {
        4: DPH_HEAP_BLOCK_32,
        8: DPH_HEAP_BLOCK_64
    }[oProcess.uPointerSize]
    oAllocationInformation = oAllocationInformationVirtualAllocation.foReadStructureForOffset(
        cStructure=DPH_HEAP_BLOCK,
        uOffset=uAllocationInformationStartAddress -
        oAllocationInformationVirtualAllocation.uStartAddress,
    )
    if gbDebugOutput:
        print(",- oAllocationInformation ").ljust(80, "-")
        for sLine in oAllocationInformation.fasDump():
            print "| %s" % sLine
        print "`".ljust(80, "-")
    return oAllocationInformation
Пример #2
0
def foGetVirtualAllocationForProcessAndAddress(oProcess,
                                               uAddressInVirtualAllocation):
    oVirtualAllocation = cVirtualAllocation(oProcess.uId,
                                            uAddressInVirtualAllocation)
    assert oVirtualAllocation, \
        "Cannot find virtual allocation for page heap block allocation at 0x%X" % uAddressInVirtualAllocation
    if gbDebugOutput:
        print(",- oVirtualAllocation ").ljust(80, "-")
        for sLine in oVirtualAllocation.fasDump():
            print "| %s" % sLine
        print "`".ljust(80, "-")
    return oVirtualAllocation
def fUpdateReportForProcessThreadTypeIdAndAddress(oCdbWrapper, oBugReport,
                                                  oProcess, oThread,
                                                  sViolationTypeId,
                                                  uAccessViolationAddress):

    sViolationTypeDescription = {
        "R": "reading",
        "W": "writing",
        "E": "executing"
    }.get(sViolationTypeId, "accessing")
    oVirtualAllocation = cVirtualAllocation(oProcess.uId,
                                            uAccessViolationAddress)

    # Handling this AV as the appropriate type of pointer:
    for fbUpdateReportForSpecificPointerType in [
            fbUpdateReportForNULLPointer,
            fbUpdateReportForCollateralPoisonPointer,
            fbUpdateReportForHeapManagerPointer,
            fbUpdateReportForStackPointer,
            fbUpdateReportForSpecialPointer,
            fbUpdateReportForAllocatedPointer,
            fbUpdateReportForReservedPointer,
            fbUpdateReportForUnallocatedPointer,
            fbUpdateReportForInvalidPointer,
    ]:
        if fbUpdateReportForSpecificPointerType(oCdbWrapper, oBugReport,
                                                oProcess, oThread,
                                                sViolationTypeId,
                                                uAccessViolationAddress,
                                                sViolationTypeDescription,
                                                oVirtualAllocation):
            # We handled the pointer as this type, and it can be only one type, so stop trying other types:
            return
    else:
        # This pointer is not of a known type: should never happend
        raise AssertionError("Could not handle AV%s@0x%08X" %
                             (sViolationTypeId, uAccessViolationAddress))
Пример #4
0
def cBugReport_foAnalyzeException_STATUS_ACCESS_VIOLATION(
        oBugReport, oProcess, oException):
    oCdbWrapper = oProcess.oCdbWrapper
    # Parameter[0] = access type (0 = read, 1 = write, 8 = execute)
    # Parameter[1] = address
    assert len(oException.auParameters) == 2, \
        "Unexpected number of access violation exception parameters (%d vs 2)" % len(oException.auParameters)
    # Access violation: add the type of operation and the location to the exception id.
    sViolationTypeId = {
        0: "R",
        1: "W",
        8: "E"
    }.get(oException.auParameters[0], "_")
    sViolationTypeDescription = {
        0: "reading",
        1: "writing",
        8: "executing"
    }.get(oException.auParameters[0], "accessing")
    sViolationTypeNotes = sViolationTypeId == "_" and " (the type-of-accesss code was 0x%X)" % oException.auParameters[
        0] or ""
    uAccessViolationAddress = oException.auParameters[1]
    if uAccessViolationAddress == 0xFFFFFFFFFFFFFFFF and sViolationTypeId == "R":
        # In x64 mode, current processors will thrown an exception when you use an address larger than 0x7FFFFFFFFFFF and
        # smaller than 0xFFFF800000000000. In such cases cdb reports incorrect information in the exception parameters,
        # e.g. the address is always reported as 0xFFFFFFFFFFFFFFFF and the access type is always "read".
        # A partial work-around is to get the address from the last instruction output, which can be retrieved by asking
        # cdb to output disassembly and address after each command. This may also tell us if the access type was "execute".
        oProcess.fasExecuteCdbCommand(
            sCommand=".prompt_allow +dis +ea;",
            sComment="Enable disassembly and address in cdb prompt",
        )
        # Do this twice in case the first time requires loading symbols, which can output junk that makes parsing ouput difficult.
        oProcess.fasExecuteCdbCommand( \
          sCommand = "~s;",
          sComment = "Show disassembly and optional symbol loading stuff",
        )
        asLastInstructionAndAddress = oProcess.fasExecuteCdbCommand(
            sCommand="~s;",
            sComment="Show disassembly",
            bOutputIsInformative=True,
        )
        # Revert to not showing disassembly and address:
        oProcess.fasExecuteCdbCommand( \
          sCommand = ".prompt_allow -dis -ea;",
          sComment = "Revert to clean cdb prompt",
        )
        # Sample output:
        # |00007ffd`420b213e 488b14c2        mov     rdx,qword ptr [rdx+rax*8] ds:00007df5`ffb60000=????????????????
        # or
        # |60053594 ff7008          push    dword ptr [eax+8]    ds:002b:00000008=????????
        # or
        # |00007ff6`e7ab1204 ffe1            jmp     rcx {c0c0c0c0`c0c0c0c0}
        # or
        # |00000000`7fffffff ??              ???
        # or
        # |00007ff9`b6f1a904 488b8d500d0000  mov     rcx,qword ptr [rbp+0D50h] ss:00000244`4124f590=0000024441210240
        assert len(asLastInstructionAndAddress) == 1, \
            "Unexpected last instruction output:\r\n%r" % "\r\n".join(asLastInstructionAndAddress)
        oEIPOutsideAllocatedMemoryMatch = re.match(
            "^%s$" % "".join([
                r"([0-9`a-f]+)",
                r"\s+",
                r"\?\?",
                r"\s+",
                r"\?\?\?"  # address   spaces "??" spaces "???"
            ]),
            asLastInstructionAndAddress[0])
        if oEIPOutsideAllocatedMemoryMatch:
            sAddress = oEIPOutsideAllocatedMemoryMatch.group(1)
            sViolationTypeId = "E"
            sViolationTypeDescription = "executing"
        else:
            oLastInstructionMatch = re.match(
                "^%s$" % "".join([
                    r"[0-9`a-f]+",
                    r"\s+",  # address   spaces
                    r"[0-9`a-f]+",
                    r"\s+",  # opcode   spaces
                    r"\w+",
                    r"\s+",  # instruction   spaces
                    r"(?:",  # either{
                    r"([^\[,]+,.+)",  #   (destination operand that does not reference memory "," source operand )
                    r"|",  # }or{
                    ".*"  #   any other combination of operands
                    r")",  # }
                    r"(?:",  # either{
                    r"\ws:",  #   segment register ":"
                    r"(?:[0-9a-f`]{4}:)?",  #   optional { segment value ":" }
                    r"([0-9a-f`]+)",  #   (sAddress1)
                    r"="
                    r"(?:",  # "=" either {
                    r"\?+",  #    "???????" <cannot be read>
                    "|",  # } or {
                    r"([0-9`a-f]+)",  #   (sValue)  <memory at address can be read>
                    "|",  # } or {
                    r"\{",  #   "{"
                    r".+",  #      symbol
                    r"\s+",  #      spaces
                    r"\(([0-9`a-f]+)\)",  #     "(" (sAddress2) ")"
                    r"\}",  #   "}"
                    ")",
                    r"|",  # }or{
                    r"\{([0-9`a-f]+)\}",  #   "{" (sAddress3) "}"
                    r")",  # }
                ]),
                asLastInstructionAndAddress[0])
            assert oLastInstructionMatch, \
                "Unexpected last instruction output:\r\n%s" % "\r\n".join(asLastInstructionAndAddress)
            sDestinationOperandThatDoesNotReferenceMemory, sAddress1, sValue, sAddress2, sAddress3 = oLastInstructionMatch.groups(
            )
            sAddress = sAddress1 or sAddress2 or sAddress3
            if sAddress1:
                if sDestinationOperandThatDoesNotReferenceMemory:
                    # The destination operand does not reference memory, so this must be a read AV
                    sViolationTypeId = "R"
                    sViolationTypeDescription = "reading"
                elif sAddress1 and sValue:
                    # The adress referenced can be read, so it must be write AV
                    sViolationTypeId = "W"
                    sViolationTypeDescription = "writing"
                else:
                    sViolationTypeId = "_"
                    sViolationTypeDescription = "accessing"
                    sViolationTypeNotes = " (the type of accesss must be read or write, but cannot be determined)"
            else:
                sViolationTypeId = "E"
                sViolationTypeDescription = "executing"
        uAccessViolationAddress = long(sAddress.replace("`", ""), 16)
    oBugReport.atxMemoryRemarks.append(
        ("Access violation", uAccessViolationAddress, None))
    # TODO Find out size of access

    if sViolationTypeId == "E":
        # Hide the top stack frame if it is for the address at which the execute access violation happened:
        if oBugReport and oBugReport.oStack and oBugReport.oStack.aoFrames \
            and oBugReport.oStack.aoFrames[0].uInstructionPointer == uAccessViolationAddress:
            oBugReport.oStack.aoFrames[0].sIsHiddenBecause = "called address"

    oVirtualAllocation = cVirtualAllocation(oProcess.uId,
                                            uAccessViolationAddress)

    # Try handling this as a NULL pointer, which is special cased because we do not always report these.
    if fbAccessViolationIsNULLPointer(oCdbWrapper, oBugReport, oProcess,
                                      sViolationTypeId,
                                      uAccessViolationAddress,
                                      sViolationTypeDescription,
                                      oVirtualAllocation):
        if dxConfig[
                "bIgnoreFirstChanceNULLPointerAccessViolations"] and not oException.bApplicationCannotHandleException:
            # This is a first chance exception; let the application handle it first.
            return None
    else:
        # Try various ways of handling this AV:
        for fbAccessViolationHandled in [
                fbAccessViolationIsCollateralPoisonPointer,
                fbAccessViolationIsHeapManagerPointer,
                fbAccessViolationIsStackPointer,
                fbAccessViolationIsSpecialPointer,
                fbAccessViolationIsAllocatedPointer,
                fbAccessViolationIsReservedPointer,
                fbAccessViolationIsFreePointer,
                fbAccessViolationIsInvalidPointer,
        ]:
            if fbAccessViolationHandled(oCdbWrapper, oBugReport, oProcess,
                                        sViolationTypeId,
                                        uAccessViolationAddress,
                                        sViolationTypeDescription,
                                        oVirtualAllocation):
                # Once it's handled, stop trying other handlers
                break
        else:
            # Unable to handle (should not be possible!)
            raise AssertionError("Could not handle AV%s@0x%08X" %
                                 (sViolationTypeId, uAccessViolationAddress))
    oBugReport.sBugDescription += sViolationTypeNotes
    return oBugReport
Пример #5
0
    def foCreate(oProcess, uStowedExceptionInformationAddress):
        # Read STOWED_EXCEPTION_INFORMATION_V1 or STOWED_EXCEPTION_INFORMATION_V2 structure.
        # (See https://msdn.microsoft.com/en-us/library/windows/desktop/dn600343(v=vs.85).aspx)
        # Both start with a STOWED_EXCEPTION_INFORMATION_HEADER structure.
        # (See https://msdn.microsoft.com/en-us/library/windows/desktop/dn600342(v=vs.85).aspx)
        # STOWED_EXCEPTION_INFORMATION_HEADER = {
        #   ULONG     Size
        #   ULONG     Signature      // "SE01" (0x53453031), "SE02" (0x53453032)
        # }
        oStowedExceptionInformationHeader = oProcess.foReadStructureForAddress(
            cStructure=STOWED_EXCEPTION_INFORMATION_HEADER,
            uAddress=uStowedExceptionInformationAddress,
        )
        if oStowedExceptionInformationHeader.Signature == STOWED_EXCEPTION_INFORMATION_V1_SIGNATURE:
            cStowedExceptionInformation = {
                "x86": STOWED_EXCEPTION_INFORMATION_V1_32,
                "x64": STOWED_EXCEPTION_INFORMATION_V1_64,
            }[oProcess.sISA]
        else:
            assert oStowedExceptionInformationHeader.Signature == STOWED_EXCEPTION_INFORMATION_V2_SIGNATURE, \
                "Unexpected stowed exception signature 0x%X (expected 0x%X or 0x%X)" % \
                (oStowedExceptionInformationHeader.Signature, STOWED_EXCEPTION_INFORMATION_V1_SIGNATURE, \
                STOWED_EXCEPTION_INFORMATION_V2_SIGNATURE)
            cStowedExceptionInformation = {
                "x86": STOWED_EXCEPTION_INFORMATION_V2_32,
                "x64": STOWED_EXCEPTION_INFORMATION_V2_64,
            }[oProcess.sISA]
        assert oStowedExceptionInformationHeader.Size == SIZEOF(cStowedExceptionInformation), \
            "STOWED_EXCEPTION_INFORMATION structure is 0x%X bytes, but 0x%X was expected!?" % \
            (oStowedExceptionInformationHeader.Size, SIZEOF(cStowedExceptionInformation))
        oStowedExceptionInformation = oProcess.foReadStructureForAddress(
            cStructure=cStowedExceptionInformation,
            uAddress=uStowedExceptionInformationAddress,
        )
        uExceptionForm = oStowedExceptionInformation.ExceptionForm_ThreadId & 3
        uThreadId = (oStowedExceptionInformation.ExceptionForm_ThreadId
                     & 0xfffffffc) << 2
        # Handle structure
        sNestedExceptionTypeId = None
        oNestedException = None
        sWRTLanguageExceptionIUnkownClassName = None
        if (oStowedExceptionInformationHeader.Signature
                == STOWED_EXCEPTION_INFORMATION_V2_SIGNATURE
                and oStowedExceptionInformation.NestedExceptionType !=
                STOWED_EXCEPTION_NESTED_TYPE_NONE):
            uNestedExceptionAddress = POINTER_VALUE(
                oStowedExceptionInformation.NestedException)
            if oStowedExceptionInformation.NestedExceptionType == STOWED_EXCEPTION_NESTED_TYPE_WIN32:
                sNestedExceptionTypeId = "Win32"
                oNestedException = cException.foCreateFromMemory(
                    oProcess=oProcess,
                    uExceptionRecordAddress=uNestedExceptionAddress,
                )
            elif oStowedExceptionInformation.NestedExceptionType == STOWED_EXCEPTION_NESTED_TYPE_STOWED:
                sNestedExceptionTypeId = "Stowed"
                oNestedException = cStowedException.foCreate(
                    oProcess=oProcess,
                    uStowedExceptionAddress=uNestedExceptionAddress,
                )
            elif oStowedExceptionInformation.NestedExceptionType == STOWED_EXCEPTION_NESTED_TYPE_CLR:
                sNestedExceptionTypeId = "CLR"
                # TODO: find out how to trigger these so I can find out how to handle them.
            elif oStowedExceptionInformation.NestedExceptionType == STOWED_EXCEPTION_NESTED_TYPE_LEO:
                sNestedExceptionTypeId = "WRTLanguage"
                # These can be triggered using RoOriginateLanguageException. The "NestedException" contains a pointer to an
                # object that implements IUnknown. Apparently this object "contains all the information necessary recreate it
                # the exception a later point." (https://msdn.microsoft.com/en-us/library/dn302172(v=vs.85).aspx)
                # I have not been able to find more documentation for this, so this is based on reverse engineering.
                sWRTLanguageExceptionIUnkownClassName = fsGetCPPObjectClassNameFromVFTable(
                    oProcess=oProcess,
                    uCPPObjectAddress=uNestedExceptionAddress,
                )


#      elif oStowedExceptionInformation.NestedExceptionType == STOWED_EXCEPTION_NESTED_TYPE_LMAX:
            else:
                oDataVirtualAllocation = cVirtualAllocation(
                    oProcess.uId, uNestedExceptionAddress)
                uDataOffset = uNestedExceptionAddress - oDataVirtualAllocation.uStartAddress
                uDataSize = min(0x80,
                                oDataVirtualAllocation.uSize - uDataOffset)
                sData = ",".join([
                    "%02X" % uByte for uByte in oDataVirtualAllocation.
                    fauReadBytesForOffsetAndSize(uDataOffset, uDataSize)
                ])
                sNestedExceptionTypeId = "Type=0x%08X,Data@0x%08X:[%s]" % \
                    (oStowedExceptionInformation.NestedExceptionType, uNestedExceptionAddress, sData)
        # Handle the two different forms:
        if uExceptionForm == 1:
            oStowedException = cStowedException(
                iCode=oStowedExceptionInformation.ResultCode,
                uAddress=oStowedExceptionInformation.ExceptionAddress,
                pStackTrace=oStowedExceptionInformation.StackTrace,
                uStackTraceSize=oStowedExceptionInformation.StackTraceWords *
                oStowedExceptionInformation.StackTraceWordSize,
                sNestedExceptionTypeId=sNestedExceptionTypeId,
                oNestedException=oNestedException,
                sWRTLanguageExceptionIUnkownClassName=
                sWRTLanguageExceptionIUnkownClassName,
            )
        else:
            assert uExceptionForm == 2, \
                "Unexpected exception form %d" % uExceptionForm
            sErrorText = oProcess.fsReadNullTerminatedStringForAddress(
                uAddress=oStowedExceptionInformation.ErrorText,
                bUnicode=True,
            )
            oStowedException = cStowedException(
                iCode=oStowedExceptionInformation.ResultCode,
                sErrorText=sErrorText,
                sNestedExceptionTypeId=sNestedExceptionTypeId,
                oNestedException=oNestedException,
                sWRTLanguageExceptionIUnkownClassName=
                sWRTLanguageExceptionIUnkownClassName,
            )

        return oStowedException
def cBugReport_foAnalyzeException_Cpp(oBugReport, oProcess, oThread,
                                      oException):
    # Based on https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273/
    # Attempt to get the symbol of the virtual function table of the object that was thrown and add that the the type id:
    if oProcess.sISA == "x64":
        assert len(oException.auParameters) == 4, \
            "Expected a C++ Exception to have 4 parameters, got %d" % len(oException.auParameters)
        # On 64-bit systems, the exception information uses 32-bit offsets from a 64-bit base address.
        uBaseAddress = oException.auParameters[3]
    else:
        assert len(oException.auParameters) == 3, \
            "Expected a C++ Exception to have 3 parameters, got %d" % len(oException.auParameters)
        # On 32-bit systems, the exception information uses 32-bit addresses (== offsets from 0).
        uBaseAddress = 0
        # +-------+
        # | DW/QW | uUnknown1
        # +-------+
        # | DW/QW | uExceptionObjectAddress*            (may not be actual address)
        # +-------+
        # | DW/QW | uExceptionObjectDescriptionAddress
        # +-------+               |
        # | DW/QW | uBaseAddress* |                        (optional, 64-bit only!)
        # +-------+  |            |
        #            |    ,----- -'
        #            |    V
        #            |  +----+ EXCEPTION_OBJECT_DESCRIPTION_1
        #            |  | DW | uUnknown1
        #            |  +----+
        #            |  | DW | uUnknown2
        #            |  +----+
        #            |  | DW | uUnknown3
        #            |  +----+
        #            |  | DW | uOffsetOfPart2
        #            |  +----+  |
        #            |  :    :  |
        #            |          |
        #            |'--------.|
        #            |          V
        #            |        +----+ EXCEPTION_OBJECT_DESCRIPTION_2
        #            |        | DW | uUnknown1
        #            |        +----+
        #            |        | DW | uOffsetOfPart3
        #            |        +----+  |
        #            |        :    :  |
        #            |                |
        #            |'--------------.|
        #            |                V
        #            |              +----+ EXCEPTION_OBJECT_DESCRIPTION_3
        #            |              | DW | uUnknown1
        #            |              +----+
        #            |              | DW | uOffsetOfPart4
        #            |              +----+  |
        #            |              :    :  |
        #            |                      |
        #             '--------------------.|
        #                                   V
        #                                  +-------+ EXCEPTION_OBJECT_DESCRIPTION_4
        #                                  | DW/QW | uAddressOfVFTable
        #                                  +-------+
        #                                  | DW/QW | uUnknown1
        #                                  +-------+
        #                                  | CHARS | szClassName
        #                                  :       :
    # PART 1
    uExceptionObjectDescriptionAddress = oException.auParameters[2]
    oExceptionObjectDescriptionVirtualAllocation = cVirtualAllocation(
        oProcess.uId, uExceptionObjectDescriptionAddress)
    oExceptionObjectDescription = oExceptionObjectDescriptionVirtualAllocation.foReadStructureForOffset(
        EXCEPTION_OBJECT_DESCRIPTION_1,
        uExceptionObjectDescriptionAddress -
        oExceptionObjectDescriptionVirtualAllocation.uStartAddress,
    )
    # PART 2
    uAddressOfPart2 = uBaseAddress + oExceptionObjectDescription.uOffsetOfPart2
    oPart2VirtualAllocation = cVirtualAllocation(oProcess.uId, uAddressOfPart2)
    oPart2 = oPart2VirtualAllocation.foReadStructureForOffset(
        EXCEPTION_OBJECT_DESCRIPTION_2,
        uAddressOfPart2 - oPart2VirtualAllocation.uStartAddress,
    )
    # PART 3
    uAddressOfPart3 = uBaseAddress + oPart2.uOffsetOfPart3
    oPart3VirtualAllocation = cVirtualAllocation(oProcess.uId, uAddressOfPart3)
    oPart3 = oPart3VirtualAllocation.foReadStructureForOffset(
        EXCEPTION_OBJECT_DESCRIPTION_3,
        uAddressOfPart3 - oPart3VirtualAllocation.uStartAddress,
    )
    # PART 4
    uAddressOfPart4 = uBaseAddress + oPart3.uOffsetOfPart4
    oPart4VirtualAllocation = cVirtualAllocation(oProcess.uId, uAddressOfPart4)
    cStructureOfPart4 = {
        "x86": EXCEPTION_OBJECT_DESCRIPTION_4_32,
        "x64": EXCEPTION_OBJECT_DESCRIPTION_4_64,
    }[oProcess.sISA]
    oPart4 = oPart4VirtualAllocation.foReadStructureForOffset(
        cStructureOfPart4,
        uAddressOfPart4 - oPart4VirtualAllocation.uStartAddress,
    )
    # Extract decorated symbol name of class from part 4
    uAddressOfDecoratedClassName = uAddressOfPart4 + oPart4.fuOffsetOf(
        "szDecoratedClassName")
    sDecoratedClassName = oPart4VirtualAllocation.fsReadNullTerminatedStringForOffset(
        uAddressOfDecoratedClassName - oPart4VirtualAllocation.uStartAddress)
    sClassName = mDbgHelp.fsUndecorateSymbolName(sDecoratedClassName,
                                                 bNameOnly=True)
    # Get undecorated symbol name of class and add it to the exception:
    oBugReport.sBugTypeId += ":%s" % (sClassName or sDecoratedClassName)
    return oBugReport