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;
コード例 #2
0
ファイル: cCrashInfo.py プロジェクト: NimdaKey/CrashInfo
 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;
コード例 #3
0
ファイル: cCrashInfo.py プロジェクト: NimdaKey/CrashInfo
    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