Example #1
0
 def _fasReadOutput(oSelf):
   # Read until the debugger outputs an input prompt, or terminated.
   asLines = [];
   sCurrentLine = "";
   while 1:
     sChar = oSelf._oProcess.stdout.read(1);
     if sChar in ["\r", "\n", ""]:
       if sCurrentLine:
         if dxCrashInfoConfig.get("bOutputIO", False): print "cdb>%s" % repr(sCurrentLine)[1:-1];
         oSelf._asIO.append(sCurrentLine);
         asLines.append(sCurrentLine);
         sCurrentLine = "";
       if sChar == "":
         break;
     else:
       sCurrentLine += sChar;
       # Detect the prompt.
       oPromptMatch = re.match("^\d+:\d+(:x86)?> $", sCurrentLine);
       if oPromptMatch:
         if dxCrashInfoConfig.get("bOutputIO", False): print "cdb>%s" % repr(sCurrentLine)[1:-1];
         oSelf._asIO.append(sCurrentLine);
         return asLines;
   # Cdb stdout was closed: the process is terminating.
   assert oSelf._bExpectTermination, \
       "Cdb terminated unexpectedly! Last output:\r\n%s" % "\r\n".join(oSelf._asIO[-20:]);
   oSelf._oProcess.wait(); # wait for it to terminate completely.
   oSelf._fFinishedCallback(oSelf._oErrorReport);
   return None;
Example #2
0
 def _fasReadOutput(oSelf):
     # Read until the debugger outputs an input prompt, or terminated.
     asLines = []
     sCurrentLine = ""
     while 1:
         sChar = oSelf._oProcess.stdout.read(1)
         if sChar in ["\r", "\n", ""]:
             if sCurrentLine:
                 if dxCrashInfoConfig.get("bOutputIO", False):
                     print "cdb>%s" % repr(sCurrentLine)[1:-1]
                 oSelf._asIO.append(sCurrentLine)
                 asLines.append(sCurrentLine)
                 sCurrentLine = ""
             if sChar == "":
                 break
         else:
             sCurrentLine += sChar
             # Detect the prompt.
             oPromptMatch = re.match("^\d+:\d+(:x86)?> $", sCurrentLine)
             if oPromptMatch:
                 if dxCrashInfoConfig.get("bOutputIO", False):
                     print "cdb>%s" % repr(sCurrentLine)[1:-1]
                 oSelf._asIO.append(sCurrentLine)
                 return asLines
     # Cdb stdout was closed: the process is terminating.
     assert oSelf._bExpectTermination, \
         "Cdb terminated unexpectedly! Last output:\r\n%s" % "\r\n".join(oSelf._asIO[-20:])
     oSelf._oProcess.wait()
     # wait for it to terminate completely.
     oSelf._fFinishedCallback(oSelf._oErrorReport)
     return None
Example #3
0
  def __init__(oSelf, asApplicationCommandLine, auApplicationProcessIds, sApplicationISA, asSymbolServerURLs, \
      fApplicationStartedCallback, fErrorDetectedCallback, fFinishedCallback, fInternalExceptionCallback):
    oSelf._fApplicationStartedCallback = fApplicationStartedCallback;
    oSelf._fErrorDetectedCallback = fErrorDetectedCallback;
    oSelf._fFinishedCallback = fFinishedCallback;
    oSelf._fInternalExceptionCallback = fInternalExceptionCallback;
    oSelf._sApplicationISA = sApplicationISA;
    uSymbolOptions = sum([
      0x00000001, # SYMOPT_CASE_INSENSITIVE
      0x00000002, # SYMOPT_UNDNAME
      0x00000004, # SYMOPT_DEFERRED_LOAD
#      0x00000020, # SYMOPT_OMAP_FIND_NEAREST
#      0x00000040, # SYMOPT_LOAD_ANYTHING
      0x00000100, # SYMOPT_NO_UNQUALIFIED_LOADS
      0x00000200, # SYMOPT_FAIL_CRITICAL_ERRORS
      0x00000400, # SYMOPT_EXACT_SYMBOLS
      0x00000800, # SYMOPT_ALLOW_ABSOLUTE_SYMBOLS
      0x00010000, # SYMOPT_AUTO_PUBLICS
#      0x00020000, # SYMOPT_NO_IMAGE_SEARCH
      0x00080000, # SYMOPT_NO_PROMPTS
      0x80000000, # SYMOPT_DEBUG
    ]);
    asCommandLine = [sCdbBinaryPath, "-o", "-sflags", "0x%08X" % uSymbolOptions];
    # -o => debug child processes, -sflags 0xXXXXXXXX => symbol options:
    set_asSymbolServerURLs = set(asSymbolServerURLs + [sMicrosoftSymbolServerURL]);
    sSymbolsPath = ";".join(
      ["cache*%s" % sSymbolCachePath for sSymbolCachePath in dxCrashInfoConfig.get("asSymbolCachePaths", [])] +
      ["srv*%s" % sSymbolServerURL for sSymbolServerURL in set_asSymbolServerURLs]
    );
    asCommandLine.extend(["-y", sSymbolsPath]);
    if asApplicationCommandLine is not None:
      asCommandLine += asApplicationCommandLine;
    if auApplicationProcessIds is not None and len(auApplicationProcessIds) > 0:
      asCommandLine += ["-p", str(auApplicationProcessIds.pop(0))];
      oSelf._auAttachProcessIds = auApplicationProcessIds;
      oSelf._bResumeThreads = True;
    else:
      oSelf._auAttachProcessIds = [];
      oSelf._bResumeThreads = False;
    asCommandLine = [
      (sArg.find(" ") == -1 or sArg[0] == '"')        and sArg               or '"%s"' % sArg.replace('"', '\\"')
      for sArg in asCommandLine
    ];
    if dxCrashInfoConfig.get("bOutputCommandLine", False):
      print ",-- Cdb command line ".ljust(120, "-");
      print "| %s" % asCommandLine[0];
      for sArgument in asCommandLine[1:]:
        print "|   %s" % sArgument;
      print "`".ljust(120, "-");
    oSelf._asIO = [];
    oSelf._oErrorReport = None;
    oSelf._bExpectTermination = False;
    oSelf._oProcess = subprocess.Popen(args = " ".join(asCommandLine), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE);
    oSelf._oDebugApplicationThread = threading.Thread(target = oSelf._fDebugApplication);
    oSelf._oDebugApplicationThread.start();
Example #4
0
 def foCreateFromAddress(cSelf, oCrashInfo, oProcess, pAddress, uSize):
   oSelf = cSelf(oProcess);
   uStackFramesCount = min(dxCrashInfoConfig.get("uMaxStackFramesCount", 50), uSize);
   # Execute twice, as the first time may trigger symbol loading, which outputs messages that make parsing harder.
   for x in xrange(2):
     asStack = oCrashInfo._fasSendCommandAndReadOutput("dps 0x%X L0x%X" % (pAddress, uStackFramesCount));
     if asStack is None: return None;
   # Here are some lines you might expect to parse:
   # |TODO put something here...
   uFrameNumber = 0;
   for sLine in asStack:
     oMatch = re.match(r"^\s*%s\s*$" % (
       r"(?:[0-9A-F`]+|\(Inline\))" r"\s+" # {stack_address || "(Inline)"} whitespace
       r"(?:[0-9A-F`]+|\-{8})"      r"\s+" # {ret_address || "--------"} whitespace
       "(?:"                               # either {
         r"(0x[0-9A-F`]+)"                 #   ("0x" address)
       "|"                                 # } or {
         r"(\w+)"                          #   (cdb_module_id)
         "(?:"                             #   either {
           "(\+0x[0-9A-F`]+)"              #     ("+0x" offset_in_module)
         "|"                               #   } or {
           r"!(.+?)([\+\-]0x[0-9A-F]+)?"   #     "!" (function_name) optional{({"+" || "-"} "0x" offset)}
         ")"                               #   }
       ")"                                 # }
     ), sLine, re.I);
     assert oMatch, "Unknown stack output: %s" % sLine;
     (sAddress, sCdbModuleId, sModuleOffset, sSymbol, sSymbolOffset) = oMatch.groups();
     uAddress = sAddress and int(sAddress.replace("`", ""), 16);
     uModuleOffset = sModuleOffset and int(sModuleOffset.replace("`", ""), 16);
     uSymbolOffset = sSymbolOffset and int(sSymbolOffset.replace("`", ""), 16);
     assert uFrameNumber < uStackFramesCount, \
         "Got more frames than requested";
     oSelf._fAddStackFrame(uFrameNumber, uAddress, sCdbModuleId, uModuleOffset, sSymbol, uSymbolOffset);
     uFrameNumber += 1;
   return oSelf;
Example #5
0
 def __init__(oSelf, uNumber, uAddress, oModule, uModuleOffset, oFunction, uFunctionOffset):
   oSelf.uNumber = uNumber;
   oSelf.uAddress = uAddress;
   oSelf.oModule = oModule;
   oSelf.uModuleOffset = uModuleOffset;
   oSelf.oFunction = oFunction;
   oSelf.uFunctionOffset = uFunctionOffset;
   if oSelf.oFunction:
     if oSelf.uFunctionOffset > 0:
       oSelf.sAddress = "%s + 0x%X" % (oSelf.oFunction.sName, oSelf.uFunctionOffset);
     elif oSelf.uFunctionOffset:
       oSelf.sAddress = "%s - 0x%X" % (oSelf.oFunction.sName, abs(oSelf.uFunctionOffset));
     else:
       oSelf.sAddress = oSelf.oFunction.sName;
     oSelf.sSimplifiedAddress = oSelf.oFunction.sSimplifiedName;
     if uFunctionOffset in xrange(dxCrashInfoConfig.get("uMaxFunctionOffset", 0xFFF)):
       oSelf.sHashAddress = oSelf.oFunction.sName;
     else:
       # The offset is negative or too large: this is the closest symbol, but probably not the correct symbol.
       # This probably means there are not enough symbols to distinguish different functions. The only thing that
       # can be done to create a unique stack hash for this frame is add the offset. Unfortunately, this offset will
       # likely be different in a different build of the same application, so the stack hash will be different as well.
       oSelf.sHashAddress = oSelf.sAddress;
       oSelf.sAddress += " (this symbol may not be correct)";
   elif oSelf.oModule:
     oSelf.sAddress = "%s + 0x%X" % (oSelf.oModule.sBinaryName, oSelf.uModuleOffset);
     oSelf.sSimplifiedAddress = "%s!(unknown)" % oSelf.oModule.sBinaryName;
     oSelf.sHashAddress = oSelf.oModule.sBinaryName;
   else:
     oSelf.sAddress = "0x%X" % oSelf.uAddress;
     oSelf.sSimplifiedAddress = "(unknown)";
     oSelf.sHashAddress = "";
Example #6
0
 def _fasSendCommandAndReadOutput(oSelf, sCommand):
   if dxCrashInfoConfig.get("bOutputIO", False): print "cdb<%s" % repr(sCommand)[1:-1];
   oSelf._asIO[-1] += sCommand;
   try:
     oSelf._oProcess.stdin.write("%s\r\n" % sCommand);
   except Exception, oException:
     assert oSelf._bExpectTermination, \
         "Cdb terminated unexpectedly! Last output:\r\n%s" % "\r\n".join(oSelf._asIO[-20:]);
     oSelf._oProcess.wait(); # wait for it to terminate completely.
     oSelf._fFinishedCallback(oSelf._oErrorReport);
     return None;
Example #7
0
 def foCreate(cSelf, oCrashInfo, oProcess):
   oSelf = cSelf(oProcess);
   uStackFramesCount = dxCrashInfoConfig.get("uMaxStackFramesCount", 50);
   # Execute twice, as the first time may trigger symbol loading, which outputs messages that make parsing harder.
   for x in xrange(2):
     asStack = oCrashInfo._fasSendCommandAndReadOutput("kn 0x%X" % uStackFramesCount);
     if asStack is None: return None;
   sHeader = asStack.pop(0);
   assert re.sub(r"\s+", " ", sHeader.strip()) in ["# ChildEBP RetAddr", "# Child-SP RetAddr Call Site"], \
       "Unknown stack header: %s" % repr(sHeader);
   # Here are some lines you might expect to parse:
   # |00 (Inline) -------- chrome_child!WTF::RawPtr<blink::Document>::operator*+0x11
   # |03 0082ec08 603e2568 chrome_child!blink::XMLDocumentParser::startElementNs+0x105
   # |33 0082fb50 0030d4ba chrome!wWinMain+0xaa
   # |23 0a8cc578 66629c9b 0x66cf592a
   # |13 0a8c9cc8 63ea124e IEFRAME!Ordinal231+0xb3c83
   # |36 0a19c854 77548e71 MSHTML+0x8d45e
   # |1b 0000008c`53b2c650 00007ffa`4631cfba ntdll!KiUserCallbackDispatcherContinue
   # |22 00000040`0597b770 00007ffa`36ddc0e3 0x40`90140fc3
   # |WARNING: Frame IP not in any known module. Following frames may be wrong.
   # |WARNING: Stack unwind information not available. Following frames may be wrong.
   # |Could not allocate memory for stack trace
   uFrameNumber = 0;
   for sLine in asStack:
     if re.match(r"^%s$" % "|".join([
         "WARNING: Frame IP not in any known module\. Following frames may be wrong\.",
         "WARNING: Stack unwind information not available\. Following frames may be wrong\.",
         "Could not allocate memory for stack trace",
     ]), sLine):
       continue;
     oMatch = re.match(r"^\s*%s\s*$" % (
       r"([0-9A-F]+)"               r"\s+" # frame_number whitespace
       r"(?:[0-9A-F`]+|\(Inline\))" r"\s+" # {stack_address || "(Inline)"} whitespace
       r"(?:[0-9A-F`]+|\-{8})"      r"\s+" # {ret_address || "--------"} whitespace
       "(?:"                               # either {
         r"(0x[0-9A-F`]+)"                 #   ("0x" address)
       "|"                                 # } or {
         r"(\w+)"                          #   (cdb_module_id)
         "(?:"                             #   either {
           "(\+0x[0-9A-F]+)"               #     ("+0x" offset_in_module)
         "|"                               #   } or {
           r"!(.+?)([\+\-]0x[0-9A-F]+)?"   #     "!" (function_name) optional{(["+" || "-"] "0x" offset)}
         ")"                               #   }
       ")"                                 # }
     ), sLine, re.I);
     assert oMatch, "Unknown stack output: %s" % repr(sLine);
     (sFrameNumber, sAddress, sCdbModuleId, sModuleOffset, sSymbol, sSymbolOffset) = oMatch.groups();
     assert uFrameNumber == int(sFrameNumber, 16), "Unexpected frame number: %s vs %d" % (sFrameNumber, uFrameNumber);
     uAddress = sAddress and int(sAddress.replace("`", ""), 16);
     uModuleOffset = sModuleOffset is not None and int(sModuleOffset.replace("`", ""), 16);
     uSymbolOffset = sSymbolOffset is not None and int(sSymbolOffset.replace("`", ""), 16);
     oSelf._fAddStackFrame(uFrameNumber, uAddress, sCdbModuleId, uModuleOffset, sSymbol, uSymbolOffset);
     uFrameNumber += 1;
   return oSelf;
Example #8
0
 def _fAddStackFrame(oSelf, uNumber, uAddress, sCdbModuleId, uModuleOffset, sSymbol, uSymbolOffset):
   # frames must be created in order:
   assert uNumber == len(oSelf.aoFrames), \
       "Unexpected frame number %d vs %d" % (uNumber, len(oSelf.aoFrames));
   uMaxStackFramesCount = dxCrashInfoConfig.get("uMaxStackFramesCount", 50);
   assert uNumber < uMaxStackFramesCount, \
       "Unexpected frame number %d (max %d)" % (uNumber, uMaxStackFramesCount);
   if uNumber == uMaxStackFramesCount - 1:
     oSelf.bPartialStack = True; # We leave the last one out so we can truely say there are more.
   else:
     oModule = sCdbModuleId and oSelf.oProcess.foGetModule(sCdbModuleId);
     oFunction = oModule and sSymbol and oModule.foGetOrCreateFunction(sSymbol);
     oSelf.aoFrames.append(cStackFrame(uNumber, uAddress, oModule, uModuleOffset, oFunction, uSymbolOffset));
Example #9
0
 def _fasSendCommandAndReadOutput(oSelf, sCommand):
     if dxCrashInfoConfig.get("bOutputIO", False):
         print "cdb<%s" % repr(sCommand)[1:-1]
     oSelf._asIO[-1] += sCommand
     try:
         oSelf._oProcess.stdin.write("%s\r\n" % sCommand)
     except Exception, oException:
         assert oSelf._bExpectTermination, \
             "Cdb terminated unexpectedly! Last output:\r\n%s" % "\r\n".join(oSelf._asIO[-20:])
         oSelf._oProcess.wait()
         # wait for it to terminate completely.
         oSelf._fFinishedCallback(oSelf._oErrorReport)
         return None
Example #10
0
def fsGetAddressId(uAddress):
  for (uBaseAddress, sId) in dsId_uAddress.items():
    iOffset = uAddress - uBaseAddress;
    if iOffset == 0:
      return sId;
    uMaxAddressOffset = dxCrashInfoConfig.get("uMaxAddressOffset", 0xFFF);
    if iOffset > uMaxAddressOffset: # Maybe this is wrapping:
      iOffset -= 0x100000000;
    elif iOffset < -uMaxAddressOffset: # Maybe this is wrapping:
      iOffset += 0x100000000;
    uOffset = abs(iOffset);
    if uOffset < uMaxAddressOffset:
      return "%s%s0x%X" % (sId, iOffset < 0 and "-" or "+", uOffset);
  return "Arbitrary";
def ftsGetAddressIdAndDescription(uAddress):
  for (uBaseAddress, (sId, sDescription)) in dsId_uAddress.items():
    iOffset = uAddress - uBaseAddress;
    if iOffset == 0:
      return sId;
    uMaxAddressOffset = dxCrashInfoConfig.get("uMaxAddressOffset", 0xFFF);
    if iOffset > uMaxAddressOffset: # Maybe this is wrapping:
      iOffset -= 0x100000000;
    elif iOffset < -uMaxAddressOffset: # Maybe this is wrapping:
      iOffset += 0x100000000;
    uOffset = abs(iOffset);
    if uOffset < uMaxAddressOffset:
      return "%s%s0x%X" % (sId, iOffset < 0 and "-" or "+", uOffset), sDescription;
  return "Arbitrary", "an invalid pointer was used";
Example #12
0
 def foCreateFromAddress(cSelf, oCrashInfo, oProcess, pAddress, uSize):
     oSelf = cSelf(oProcess)
     uStackFramesCount = min(
         dxCrashInfoConfig.get("uMaxStackFramesCount", 50), uSize)
     # Execute twice, as the first time may trigger symbol loading, which outputs messages that make parsing harder.
     for x in xrange(2):
         asStack = oCrashInfo._fasSendCommandAndReadOutput(
             "dps 0x%X L0x%X" % (pAddress, uStackFramesCount))
         if asStack is None: return None
     # Here are some lines you might expect to parse:
     # |TODO put something here...
     uFrameNumber = 0
     for sLine in asStack:
         oMatch = re.match(
             r"^\s*%s\s*$" % (
                 r"(?:[0-9A-F`]+|\(Inline\))"
                 r"\s+"  # {stack_address || "(Inline)"} whitespace
                 r"(?:[0-9A-F`]+|\-{8})"
                 r"\s+"  # {ret_address || "--------"} whitespace
                 "(?:"  # either {
                 r"(0x[0-9A-F`]+)"  #   ("0x" address)
                 "|"  # } or {
                 r"(\w+)"  #   (cdb_module_id)
                 "(?:"  #   either {
                 "(\+0x[0-9A-F`]+)"  #     ("+0x" offset_in_module)
                 "|"  #   } or {
                 r"!(.+?)([\+\-]0x[0-9A-F]+)?"  #     "!" (function_name) optional{({"+" || "-"} "0x" offset)}
                 ")"  #   }
                 ")"  # }
             ),
             sLine,
             re.I)
         assert oMatch, "Unknown stack output: %s" % sLine
         (sAddress, sCdbModuleId, sModuleOffset, sSymbol,
          sSymbolOffset) = oMatch.groups()
         uAddress = sAddress and int(sAddress.replace("`", ""), 16)
         uModuleOffset = sModuleOffset and int(
             sModuleOffset.replace("`", ""), 16)
         uSymbolOffset = sSymbolOffset and int(
             sSymbolOffset.replace("`", ""), 16)
         assert uFrameNumber < uStackFramesCount, \
             "Got more frames than requested"
         oSelf._fAddStackFrame(uFrameNumber, uAddress, sCdbModuleId,
                               uModuleOffset, sSymbol, uSymbolOffset)
         uFrameNumber += 1
     return oSelf
Example #13
0
 def _fAddStackFrame(oSelf, uNumber, uAddress, sCdbModuleId, uModuleOffset,
                     sSymbol, uSymbolOffset):
     # frames must be created in order:
     assert uNumber == len(oSelf.aoFrames), \
         "Unexpected frame number %d vs %d" % (uNumber, len(oSelf.aoFrames))
     uMaxStackFramesCount = dxCrashInfoConfig.get("uMaxStackFramesCount",
                                                  50)
     assert uNumber < uMaxStackFramesCount, \
         "Unexpected frame number %d (max %d)" % (uNumber, uMaxStackFramesCount)
     if uNumber == uMaxStackFramesCount - 1:
         oSelf.bPartialStack = True
         # We leave the last one out so we can truely say there are more.
     else:
         oModule = sCdbModuleId and oSelf.oProcess.foGetModule(sCdbModuleId)
         oFunction = oModule and sSymbol and oModule.foGetOrCreateFunction(
             sSymbol)
         oSelf.aoFrames.append(
             cStackFrame(uNumber, uAddress, oModule, uModuleOffset,
                         oFunction, uSymbolOffset))
Example #14
0
 def __init__(oSelf, uNumber, uAddress, oModule, uModuleOffset, oFunction,
              uFunctionOffset):
     oSelf.uNumber = uNumber
     oSelf.uAddress = uAddress
     oSelf.oModule = oModule
     oSelf.uModuleOffset = uModuleOffset
     oSelf.oFunction = oFunction
     oSelf.uFunctionOffset = uFunctionOffset
     if oSelf.oFunction:
         if oSelf.uFunctionOffset > 0:
             oSelf.sAddress = "%s + 0x%X" % (oSelf.oFunction.sName,
                                             oSelf.uFunctionOffset)
         elif oSelf.uFunctionOffset:
             oSelf.sAddress = "%s - 0x%X" % (oSelf.oFunction.sName,
                                             abs(oSelf.uFunctionOffset))
         else:
             oSelf.sAddress = oSelf.oFunction.sName
         oSelf.sSimplifiedAddress = oSelf.oFunction.sSimplifiedName
         if uFunctionOffset in xrange(
                 dxCrashInfoConfig.get("uMaxFunctionOffset", 0xFFF)):
             oSelf.sHashAddress = oSelf.oFunction.sName
         else:
             # The offset is negative or too large: this is the closest symbol, but probably not the correct symbol.
             # This probably means there are not enough symbols to distinguish different functions. The only thing that
             # can be done to create a unique stack hash for this frame is add the offset. Unfortunately, this offset will
             # likely be different in a different build of the same application, so the stack hash will be different as well.
             oSelf.sHashAddress = oSelf.sAddress
             oSelf.sAddress += " (this symbol may not be correct)"
     elif oSelf.oModule:
         oSelf.sAddress = "%s + 0x%X" % (oSelf.oModule.sBinaryName,
                                         oSelf.uModuleOffset)
         oSelf.sSimplifiedAddress = "%s!(unknown)" % oSelf.oModule.sBinaryName
         oSelf.sHashAddress = oSelf.oModule.sBinaryName
     else:
         oSelf.sAddress = "0x%X" % oSelf.uAddress
         oSelf.sSimplifiedAddress = "(unknown)"
         oSelf.sHashAddress = ""
Example #15
0
 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;
Example #16
0
  def foCreateFromException(cSelf, oException, asCdbIO):
    # Get initial exception type id and description
    oTopFrame = len(oException.oStack.aoFrames) > 0 and oException.oStack.aoFrames[0] or None;
    # Get application id.
    sApplicationId = oException.oProcess.sBinaryName;
    # See if its in a "special" exception and rewrite the exception type id accordingly.
    if oTopFrame and oTopFrame.oFunction:
      sTypeId = fsGetSpecialExceptionTypeId(oException.sTypeId, oTopFrame) \
                or oException.sTypeId; # in case there is no special type id.
    else:
      sTypeId = oException.sTypeId;
    # Find out which frame should be the "main" frame and get stack id.
    # * Stack exhaustion can be caused by recursive function calls, where one or more functions repeatedly call
    #   themselves. If possible, this is detected, and the alphabetically first functions is chosen as the main function
    #   The stack hash is created using only the looping functions.
    #   ^^^^ THIS IS NOT YET IMPLEMENTED ^^^
    # * Plenty of exceptions get thrown by special functions, eg. kernel32!DebugBreak, which are not relevant to the
    #   exception. These are ignored and the calling function is used as the "main" frame).
    
      
    oMainFrame = None;
    uIgnoredFramesHashed = 0;
    uFramesHashed = 0;
    asStack = [];
    sStackId = "";
    oIgnoredFramesHasher = hashlib.md5();
    oSkippedFramesHasher = hashlib.md5();
    for oFrame in oException.oStack.aoFrames:
      if (uFramesHashed - uIgnoredFramesHashed == dxCrashInfoConfig.get("uStackHashFramesCount", 3)):
        asStack.append("   %s" % oFrame.sAddress);
        continue;
      if oMainFrame is None:
        if fbIsIrrelevantTopFrame(sTypeId, oException.uCode, oFrame):
          uIgnoredFramesHashed += 1;
          uFramesHashed += 1;
          asStack.append(" ~ %s" % oFrame.sAddress);
          oIgnoredFramesHasher.update(oFrame.sHashAddress);
          continue; # This frame is irrelevant in the context of this exception type.
        if uIgnoredFramesHashed > 0:
          sStackId += "%02X~" % ord(oIgnoredFramesHasher.digest()[0]);
        oMainFrame = oFrame;
      if oFrame.oFunction:
        oHasher = hashlib.md5();
        oHasher.update(oFrame.sHashAddress);
        sStackId += "%02X" % ord(oHasher.digest()[0]);
        asStack.append(" * %s" % oFrame.sAddress);
      elif oFrame.oModule:
        oHasher = hashlib.md5();
        oHasher.update(oFrame.sHashAddress);
        sStackId += "(%02X)" % ord(oHasher.digest()[0]);
        asStack.append(" ? %s" % oFrame.sAddress);
      else:
        sStackId += "--";
        asStack.append(" - %s" % oFrame.sAddress);
      uFramesHashed += 1;
    if uFramesHashed == 0:
      sStackId = "#";
    if oException.oStack.bPartialStack:
      asStack.append("   ...");
    # Get the main stack frame's simplified address as the id.
    sFunctionId = oMainFrame and oMainFrame.sSimplifiedAddress or "(no stack)";
    # Combine the various ids into a unique exception id
    sId = " ".join([sApplicationId, sTypeId, sStackId, sFunctionId]);
    
    # Get the description 
    sLocationDescription = oTopFrame and oTopFrame.sAddress or "(no stack)";
    sDescription = "%s in %s" % (oException.sDescription, sLocationDescription);
    
    sSecurityImpact = oException.sSecurityImpact;
    
    # Create HTML details
    sHTMLDetails = """
<!doctype html>
<html>
  <head>
    <style>
      div {
        color: white;
        background: black;
        padding: 5px;
        margin-bottom: 1em;
      }
      code {
        margin-bottom: 1em;
      }
    </style>
    <title>%s</title>
  </head>
  <body>
    <div>%s</div>
    <code>%s</code>
    <div>Debugger input/output</div>
    <code>%s</code>
  </body>
</html>""".strip() % (
      fsHTMLEncode(sId),
      fsHTMLEncode(sDescription),
      "".join(["%s<br/>" % fsHTMLEncode(x) for x in asStack]),
      "".join(["%s<br/>" % fsHTMLEncode(x) for x in asCdbIO])
    );
    return cSelf(sId, sDescription, sSecurityImpact, sHTMLDetails);
Example #17
0
 def __init__(oSelf, asApplicationCommandLine, auApplicationProcessIds, sApplicationISA, asSymbolServerURLs, \
     fApplicationStartedCallback, fErrorDetectedCallback, fFinishedCallback, fInternalExceptionCallback):
     oSelf._fApplicationStartedCallback = fApplicationStartedCallback
     oSelf._fErrorDetectedCallback = fErrorDetectedCallback
     oSelf._fFinishedCallback = fFinishedCallback
     oSelf._fInternalExceptionCallback = fInternalExceptionCallback
     oSelf._sApplicationISA = sApplicationISA
     uSymbolOptions = sum([
         0x00000001,  # SYMOPT_CASE_INSENSITIVE
         0x00000002,  # SYMOPT_UNDNAME
         0x00000004,  # SYMOPT_DEFERRED_LOAD
         #      0x00000020, # SYMOPT_OMAP_FIND_NEAREST
         #      0x00000040, # SYMOPT_LOAD_ANYTHING
         0x00000100,  # SYMOPT_NO_UNQUALIFIED_LOADS
         0x00000200,  # SYMOPT_FAIL_CRITICAL_ERRORS
         0x00000400,  # SYMOPT_EXACT_SYMBOLS
         0x00000800,  # SYMOPT_ALLOW_ABSOLUTE_SYMBOLS
         0x00010000,  # SYMOPT_AUTO_PUBLICS
         #      0x00020000, # SYMOPT_NO_IMAGE_SEARCH
         0x00080000,  # SYMOPT_NO_PROMPTS
         0x80000000,  # SYMOPT_DEBUG
     ])
     asCommandLine = [
         sCdbBinaryPath, "-o", "-sflags",
         "0x%08X" % uSymbolOptions
     ]
     # -o => debug child processes, -sflags 0xXXXXXXXX => symbol options:
     set_asSymbolServerURLs = set(asSymbolServerURLs +
                                  [sMicrosoftSymbolServerURL])
     sSymbolsPath = ";".join([
         "cache*%s" % sSymbolCachePath
         for sSymbolCachePath in dxCrashInfoConfig.get(
             "asSymbolCachePaths", [])
     ] + [
         "srv*%s" % sSymbolServerURL
         for sSymbolServerURL in set_asSymbolServerURLs
     ])
     asCommandLine.extend(["-y", sSymbolsPath])
     if asApplicationCommandLine is not None:
         asCommandLine += asApplicationCommandLine
     if auApplicationProcessIds is not None and len(
             auApplicationProcessIds) > 0:
         asCommandLine += ["-p", str(auApplicationProcessIds.pop(0))]
         oSelf._auAttachProcessIds = auApplicationProcessIds
         oSelf._bResumeThreads = True
     else:
         oSelf._auAttachProcessIds = []
         oSelf._bResumeThreads = False
     asCommandLine = [(sArg.find(" ") == -1 or sArg[0] == '"') and sArg
                      or '"%s"' % sArg.replace('"', '\\"')
                      for sArg in asCommandLine]
     if dxCrashInfoConfig.get("bOutputCommandLine", False):
         print ",-- Cdb command line ".ljust(120, "-")
         print "| %s" % asCommandLine[0]
         for sArgument in asCommandLine[1:]:
             print "|   %s" % sArgument
         print "`".ljust(120, "-")
     oSelf._asIO = []
     oSelf._oErrorReport = None
     oSelf._bExpectTermination = False
     oSelf._oProcess = subprocess.Popen(args=" ".join(asCommandLine),
                                        stdin=subprocess.PIPE,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
     oSelf._oDebugApplicationThread = threading.Thread(
         target=oSelf._fDebugApplication)
     oSelf._oDebugApplicationThread.start()
Example #18
0
    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
Example #19
0
 def foCreate(cSelf, oCrashInfo, oProcess):
     oSelf = cSelf(oProcess)
     uStackFramesCount = dxCrashInfoConfig.get("uMaxStackFramesCount", 50)
     # Execute twice, as the first time may trigger symbol loading, which outputs messages that make parsing harder.
     for x in xrange(2):
         asStack = oCrashInfo._fasSendCommandAndReadOutput(
             "kn 0x%X" % uStackFramesCount)
         if asStack is None: return None
     sHeader = asStack.pop(0)
     assert re.sub(r"\s+", " ", sHeader.strip()) in ["# ChildEBP RetAddr", "# Child-SP RetAddr Call Site"], \
         "Unknown stack header: %s" % repr(sHeader)
     # Here are some lines you might expect to parse:
     # |00 (Inline) -------- chrome_child!WTF::RawPtr<blink::Document>::operator*+0x11
     # |03 0082ec08 603e2568 chrome_child!blink::XMLDocumentParser::startElementNs+0x105
     # |33 0082fb50 0030d4ba chrome!wWinMain+0xaa
     # |23 0a8cc578 66629c9b 0x66cf592a
     # |13 0a8c9cc8 63ea124e IEFRAME!Ordinal231+0xb3c83
     # |36 0a19c854 77548e71 MSHTML+0x8d45e
     # |1b 0000008c`53b2c650 00007ffa`4631cfba ntdll!KiUserCallbackDispatcherContinue
     # |22 00000040`0597b770 00007ffa`36ddc0e3 0x40`90140fc3
     # |WARNING: Frame IP not in any known module. Following frames may be wrong.
     # |WARNING: Stack unwind information not available. Following frames may be wrong.
     # |Could not allocate memory for stack trace
     uFrameNumber = 0
     for sLine in asStack:
         if re.match(
                 r"^%s$" % "|".join([
                     "WARNING: Frame IP not in any known module\. Following frames may be wrong\.",
                     "WARNING: Stack unwind information not available\. Following frames may be wrong\.",
                     "Could not allocate memory for stack trace",
                 ]), sLine):
             continue
         oMatch = re.match(
             r"^\s*%s\s*$" % (
                 r"([0-9A-F]+)"
                 r"\s+"  # frame_number whitespace
                 r"(?:[0-9A-F`]+|\(Inline\))"
                 r"\s+"  # {stack_address || "(Inline)"} whitespace
                 r"(?:[0-9A-F`]+|\-{8})"
                 r"\s+"  # {ret_address || "--------"} whitespace
                 "(?:"  # either {
                 r"(0x[0-9A-F`]+)"  #   ("0x" address)
                 "|"  # } or {
                 r"(\w+)"  #   (cdb_module_id)
                 "(?:"  #   either {
                 "(\+0x[0-9A-F]+)"  #     ("+0x" offset_in_module)
                 "|"  #   } or {
                 r"!(.+?)([\+\-]0x[0-9A-F]+)?"  #     "!" (function_name) optional{(["+" || "-"] "0x" offset)}
                 ")"  #   }
                 ")"  # }
             ),
             sLine,
             re.I)
         assert oMatch, "Unknown stack output: %s" % repr(sLine)
         (sFrameNumber, sAddress, sCdbModuleId, sModuleOffset, sSymbol,
          sSymbolOffset) = oMatch.groups()
         assert uFrameNumber == int(
             sFrameNumber,
             16), "Unexpected frame number: %s vs %d" % (sFrameNumber,
                                                         uFrameNumber)
         uAddress = sAddress and int(sAddress.replace("`", ""), 16)
         uModuleOffset = sModuleOffset is not None and int(
             sModuleOffset.replace("`", ""), 16)
         uSymbolOffset = sSymbolOffset is not None and int(
             sSymbolOffset.replace("`", ""), 16)
         oSelf._fAddStackFrame(uFrameNumber, uAddress, sCdbModuleId,
                               uModuleOffset, sSymbol, uSymbolOffset)
         uFrameNumber += 1
     return oSelf
Example #20
0
    def foCreateFromException(cSelf, oException, asCdbIO):
        # Get initial exception type id and description
        oTopFrame = len(oException.oStack.aoFrames
                        ) > 0 and oException.oStack.aoFrames[0] or None
        # Get application id.
        sApplicationId = oException.oProcess.sBinaryName
        # See if its in a "special" exception and rewrite the exception type id accordingly.
        if oTopFrame and oTopFrame.oFunction:
            sTypeId = fsGetSpecialExceptionTypeId(oException.sTypeId, oTopFrame) \
                      or oException.sTypeId
            # in case there is no special type id.
        else:
            sTypeId = oException.sTypeId
        # Find out which frame should be the "main" frame and get stack id.
        # * Stack exhaustion can be caused by recursive function calls, where one or more functions repeatedly call
        #   themselves. If possible, this is detected, and the alphabetically first functions is chosen as the main function
        #   The stack hash is created using only the looping functions.
        #   ^^^^ THIS IS NOT YET IMPLEMENTED ^^^
        # * Plenty of exceptions get thrown by special functions, eg. kernel32!DebugBreak, which are not relevant to the
        #   exception. These are ignored and the calling function is used as the "main" frame).

        oMainFrame = None
        uIgnoredFramesHashed = 0
        uFramesHashed = 0
        asStack = []
        sStackId = ""
        oIgnoredFramesHasher = hashlib.md5()
        oSkippedFramesHasher = hashlib.md5()
        for oFrame in oException.oStack.aoFrames:
            if (uFramesHashed - uIgnoredFramesHashed == dxCrashInfoConfig.get(
                    "uStackHashFramesCount", 3)):
                asStack.append("   %s" % oFrame.sAddress)
                continue
            if oMainFrame is None:
                if fbIsIrrelevantTopFrame(sTypeId, oException.uCode, oFrame):
                    uIgnoredFramesHashed += 1
                    uFramesHashed += 1
                    asStack.append(" ~ %s" % oFrame.sAddress)
                    oIgnoredFramesHasher.update(oFrame.sHashAddress)
                    continue
                    # This frame is irrelevant in the context of this exception type.
                if uIgnoredFramesHashed > 0:
                    sStackId += "%02X~" % ord(oIgnoredFramesHasher.digest()[0])
                oMainFrame = oFrame
            if oFrame.oFunction:
                oHasher = hashlib.md5()
                oHasher.update(oFrame.sHashAddress)
                sStackId += "%02X" % ord(oHasher.digest()[0])
                asStack.append(" * %s" % oFrame.sAddress)
            elif oFrame.oModule:
                oHasher = hashlib.md5()
                oHasher.update(oFrame.sHashAddress)
                sStackId += "(%02X)" % ord(oHasher.digest()[0])
                asStack.append(" ? %s" % oFrame.sAddress)
            else:
                sStackId += "--"
                asStack.append(" - %s" % oFrame.sAddress)
            uFramesHashed += 1
        if uFramesHashed == 0:
            sStackId = "#"
        if oException.oStack.bPartialStack:
            asStack.append("   ...")
        # Get the main stack frame's simplified address as the id.
        sFunctionId = oMainFrame and oMainFrame.sSimplifiedAddress or "(no stack)"
        # Combine the various ids into a unique exception id
        sId = " ".join([sApplicationId, sTypeId, sStackId, sFunctionId])

        # Get the description
        sLocationDescription = oTopFrame and oTopFrame.sAddress or "(no stack)"
        sDescription = "%s in %s" % (oException.sDescription,
                                     sLocationDescription)

        sSecurityImpact = oException.sSecurityImpact

        # Create HTML details
        sHTMLDetails = """
<!doctype html>
<html>
  <head>
    <style>
      div {
        color: white;
        background: black;
        padding: 5px;
        margin-bottom: 1em;
      }
      code {
        margin-bottom: 1em;
      }
    </style>
    <title>%s</title>
  </head>
  <body>
    <div>%s</div>
    <code>%s</code>
    <div>Debugger input/output</div>
    <code>%s</code>
  </body>
</html>""".strip() % (fsHTMLEncode(sId), fsHTMLEncode(sDescription), "".join([
            "%s<br/>" % fsHTMLEncode(x) for x in asStack
        ]), "".join(["%s<br/>" % fsHTMLEncode(x) for x in asCdbIO]))
        return cSelf(sId, sDescription, sSecurityImpact, sHTMLDetails)