Beispiel #1
0
def fPrintLogo():
  # We will use the above ASCII and color data to create a list of arguments
  # that can be passed to oConsole.fPrint in order to output the logo in color:
  oConsole.fLock();
  try:
    for uLineIndex in xrange(len(asBugIdLogo)):
      uCurrentColor = NORMAL;
      bUnderlined = False;
      asBugIdLogoPrintArguments = [""];
      sCharsLine = asBugIdLogo[uLineIndex];
      sColorsLine = asBugIdLogoColors[uLineIndex];
      uColorIndex = 0;
      for uColumnIndex in xrange(len(sCharsLine)):
        sColor = sColorsLine[uColorIndex];
        uColorIndex += 1;
        if sColor == "_":
          bUnderlined = not bUnderlined;
          sColor = sColorsLine[uColorIndex];
          uColorIndex += 1;
        uColor = (sColor != " " and (0x0F00 + long(sColor, 16)) or NORMAL) + (bUnderlined and UNDERLINE or 0);
        if uColor != uCurrentColor:
          asBugIdLogoPrintArguments.extend([uColor, ""]);
          uCurrentColor = uColor;
        sChar = sCharsLine[uColumnIndex];
        asBugIdLogoPrintArguments[-1] += sChar;
      oConsole.fPrint(*asBugIdLogoPrintArguments);
  finally:
    oConsole.fUnlock();
Beispiel #2
0
def fVersionCheck():
    import os, sys
    axModules = [
        ("BugId", "__main__", oVersionInformation),
        ("cBugId", "cBugId", cBugId.oVersionInformation),
        ("mFileSystem", "mFileSystem", mFileSystem.oVersionInformation),
        ("mWindowsAPI", "mWindowsAPI", mWindowsAPI.oVersionInformation),
        ("oConsole", "oConsole", oConsole.oVersionInformation),
    ]
    uCounter = 0
    for (sModuleName, sSysModuleName, oModuleVersionInformation) in axModules:
        assert sModuleName == oModuleVersionInformation.sProjectName, \
            "Module %s reports that it is called %s" % (sModuleName, oModuleVersionInformation.sProjectName)
        sInstallationPath = os.path.dirname(
            sys.modules[sSysModuleName].__file__)
        oConsole.fPrint("+ ", INFO, oModuleVersionInformation.sProjectName, NORMAL, " version: ", INFO, \
            oModuleVersionInformation.sCurrentVersion, NORMAL, ", installed at ", INFO, sInstallationPath, NORMAL, ".")
        oConsole.fProgressBar(uCounter * 1.0 / len(axModules), \
            "* Checking %s for updates..." % oModuleVersionInformation.sProjectName)
        if oModuleVersionInformation.bPreRelease:
            oConsole.fPrint("  + You are running a ", HILITE, "pre-release",
                            NORMAL, " version. ",
                            "The latest release version is ", INFO,
                            oModuleVersionInformation.sLatestVersion, NORMAL,
                            ".")
        elif not oModuleVersionInformation.bUpToDate:
            oConsole.fPrint("  + Version ", INFO,
                            oModuleVersionInformation.sLatestVersion, NORMAL,
                            " is available at ", INFO,
                            oModuleVersionInformation.sUpdateURL, NORMAL, ".")
        uCounter += 1
    oConsole.fPrint("+ Windows version: ", INFO,
                    str(mWindowsAPI.oWindowsVersion), NORMAL, ".")
    oConsole.fPrint()
Beispiel #3
0
def fApplyConfigSetting(sSettingName, xValue, sIndentation):
    asGroupNames = sSettingName.split(".")
    # last element is not a group name
    sFullName = ".".join(asGroupNames)
    sSettingName = asGroupNames.pop()
    # so pop it.
    dxConfigGroup = dxConfig
    asHandledGroupNames = []
    for sGroupName in asGroupNames:
        asHandledGroupNames.append(sGroupName)
        assert sGroupName in dxConfigGroup, \
            "Unknown config group %s in setting name %s." % (repr(".".join(asHandledGroupNames)), repr(sFullName))
        dxConfigGroup = dxConfigGroup.get(sGroupName, {})
    assert sSettingName in dxConfigGroup, \
        "Unknown setting name %s%s." % (sSettingName, \
            len(asHandledGroupNames) > 0 and " in config group %s" % ".".join(asHandledGroupNames) or "")
    if json.dumps(dxConfigGroup[sSettingName]) == json.dumps(xValue):
        if not gbQuiet:
            oConsole.fPrint(sIndentation, "* The default value for config setting ", HILITE, sFullName, NORMAL, \
                " is ", json.dumps(dxConfigGroup[sSettingName]), ".")
    else:
        if not gbQuiet:
            oConsole.fPrint(sIndentation, "+ Changed config setting ", HILITE, sFullName, NORMAL, \
                " from ", HILITE, repr(dxConfigGroup[sSettingName]), NORMAL, " to ", INFO, repr(xValue), NORMAL, ".")
        dxConfigGroup[sSettingName] = xValue
Beispiel #4
0
def fMainProcessTerminatedHandler(oBugId, uProcessId, sBinaryName, sCommandLine):
  if not gbQuiet:
    oConsole.fPrint("* Process ", INFO, "%d" % uProcessId, NORMAL, "/", INFO , "0x%X" % uProcessId, NORMAL, \
        " (", INFO, sBinaryName, NORMAL, "): Terminated ", INFO, sCommandLine or "<command line unknown>");
  if dxConfig["bApplicationTerminatesWithMainProcess"]:
    oConsole.fStatus(INFO, "* BugId is stopping...");
    oBugId.fStop();
Beispiel #5
0
def fNewProcessHandler(oBugId, oProcess):
  global gasAttachToProcessesForBinaryNames;
  if not gbQuiet:
    oConsole.fPrint("* New process ", INFO, "%d" % oProcess.uId, NORMAL, "/", INFO , "0x%X" % oProcess.uId, NORMAL, ": ", INFO, oProcess.sBinaryName);
  # Now is a good time to look for additional binaries that may need to be debugged as well.
  if gasAttachToProcessesForBinaryNames:
    oBugId.fAttachToProcessesForBinaryNames(gasAttachToProcessesForBinaryNames);
Beispiel #6
0
def fTestNetworkConnect(oProcess, sProcessType, sNetworkType, sIPAddress, uPort, sSandBoxToolsPath):
  sTestType = "network %s connect" % sNetworkType;
  if fbHaveResults(sProcessType, sTestType):
    oConsole.fPrint("Testing %s already completed" % sTestType);
    return;
  oConsole.fPrint("Testing ", sNetworkType, " network connection for process ", INFO, str(oProcess.uId), NORMAL, " running ", INFO, oProcess.sBinaryName);
  oTestProcess = subprocess.Popen(
    "\"%s\\CheckNetworkAccess\\bin\\Debug\\CheckNetworkAccess.exe\" -p %d %s %d" % (sSandBoxToolsPath, oProcess.uId, sIPAddress, uPort),
    stdout = subprocess.PIPE,
    stderr = subprocess.PIPE,
  );
  (sStdOut, sStdErr) = oTestProcess.communicate();
  assert not sStdErr, "Failed:\r\n%s" % sStdErr;
  if sStdOut in [
    "** Opened Connection **\r\n",
    "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond %s:%d\r\n" % (sIPAddress, uPort),
    "No connection could be made because the target machine actively refused it %s:%d\r\n" % (sIPAddress, uPort),
  ]:
    # Connection was made
    fWriteResults(sProcessType, sTestType, False, "This process is able to open a connection on the %s to %s:%d." % (sNetworkType, sIPAddress, uPort));
  elif sStdOut in [
    "Unknown error (0x271d)\r\n",
    "(0xC0000022) - {Access Denied}\r\nA process has requested access to an object, but has not been granted those access rights.\r\n",
    "An attempt was made to access a socket in a way forbidden by its access permissions %s:%d\r\n" % (sIPAddress, uPort),
  ]:
    fWriteResults(sProcessType, sTestType, True);
  else:
    raise AssertionError("Unknown test output: %s" % sStdOut);
Beispiel #7
0
def fFailedToApplyApplicationMemoryLimitsCallback(oBugId, oProcess, bIsMainProcess):
  global gbFailedToApplyMemoryLimitsErrorShown, gbQuiet, gbVerbose;
  if not gbQuiet:
    fPrintMessageForProcess("-", oProcess, bIsMainProcess,
        ERROR_INFO, "Cannot apply application memory limits");
    gbFailedToApplyMemoryLimitsErrorShown = True;
    if not gbVerbose:
      oConsole.fPrint("  Any additional failures to apply memory limits to processess will not be shown.");
Beispiel #8
0
def fPrintMessageForProcess(sHeaderChar, uProcessId, sBinaryName, bIsMainProcess, *asMessage):
  oConsole.fPrint(
    sHeaderChar, " ", bIsMainProcess and "Main" or "Sub", " process ",
    INFO, "%d" % uProcessId, NORMAL, "/", INFO , "0x%X" % uProcessId, NORMAL,
    " (", INFO, sBinaryName, NORMAL, "): ",
    *asMessage,
    uConvertTabsToSpaces = 8
  );
Beispiel #9
0
def fFailedToApplyProcessMemoryLimitsCallback(oBugId, uProcessId, sBinaryName, sCommandLine, bIsMainProcess):
  global gbFailedToApplyMemoryLimitsErrorShown, gbVerbose;
  if gbVerbose or not gbFailedToApplyMemoryLimitsErrorShown:
    fPrintMessageForProcess("-", uProcessId, sBinaryName, bIsMainProcess,
        ERROR_INFO, "Cannot apply process memory limits");
    gbFailedToApplyMemoryLimitsErrorShown = True;
    if not gbVerbose:
      oConsole.fPrint("  Any additional failures to apply memory limits to processess will not be shown.");
Beispiel #10
0
def fNewProcessHandler(oBugId, uProcessId, sBinaryName, sCommandLine):
  global gasAttachToProcessesForExecutableNames;
  if not gbQuiet:
    oConsole.fPrint("* Process ", INFO, "%d" % uProcessId, NORMAL, "/", INFO , "0x%X" % uProcessId, NORMAL, \
        " (", INFO, sBinaryName, NORMAL, "): Started ", INFO, sCommandLine or "<command line unknown>");
  # Now is a good time to look for additional binaries that may need to be debugged as well.
  if gasAttachToProcessesForExecutableNames:
    oBugId.fAttachToProcessesForExecutableNames(*gasAttachToProcessesForExecutableNames);
Beispiel #11
0
def fFailedToDebugApplicationHandler(oBugId, sErrorMessage):
    global gbAnErrorOccured
    gbAnErrorOccured = True
    oConsole.fPrint(ERROR, "-" * 80)
    oConsole.fPrint(ERROR, "- Failed to debug the application:")
    for sLine in sErrorMessage.split("\n"):
        oConsole.fPrint(ERROR, "  ", sLine.rstrip("\r"))
    oConsole.fPrint(ERROR, "-" * 80)
    oConsole.fPrint()
Beispiel #12
0
def fFailedToDebugApplicationCallback(oBugId, sErrorMessage):
  global gbAnErrorOccured;
  gbAnErrorOccured = True;
  oConsole.fLock();
  try:
    oConsole.fPrint(ERROR, "-" * 80);
    oConsole.fPrint(ERROR, "- Failed to debug the application:");
    for sLine in sErrorMessage.split("\n"):
      oConsole.fPrint(ERROR, "  ", sLine.rstrip("\r"));
    oConsole.fPrint(ERROR, "-" * 80);
    oConsole.fPrint();
  finally:
    oConsole.fUnlock();
Beispiel #13
0
def fPrintLogo():
    # We will use the above ASCII and color data to create a list of arguments
    # that can be passed to oConsole.fPrint in order to output the logo in color:
    oConsole.fLock()
    try:
        for uLineIndex in xrange(len(asBugIdLogo)):
            iLastColor = -1
            asBugIdLogoPrintArguments = [""]
            sCharsLine = asBugIdLogo[uLineIndex]
            sColorsLine = asBugIdLogoColors[uLineIndex]
            for uColumnIndex in xrange(len(sCharsLine)):
                sColor = sColorsLine[uColumnIndex]
                iColor = sColor == " " and -1 or int("F0" + sColor, 16)
                if iColor != iLastColor:
                    asBugIdLogoPrintArguments.extend([iColor, ""])
                    iColor = iLastColor
                sChar = sCharsLine[uColumnIndex]
                asBugIdLogoPrintArguments[-1] += sChar
            oConsole.fPrint(*asBugIdLogoPrintArguments)
        if dxConfig["bShowLicenseAndDonationInfo"]:
            oConsole.fPrint()
            for asLicenseAndDonationInfoPrintArguments in aasLicenseAndDonationInfoPrintArguments:
                oConsole.fPrint(*asLicenseAndDonationInfoPrintArguments)
            oConsole.fPrint()
    finally:
        oConsole.fUnlock()
Beispiel #14
0
def fPrintLogo():
    # We will use the above ASCII and color data to create a list of arguments
    # that can be passed to oConsole.fPrint in order to output the logo in color:
    for uLineIndex in xrange(len(asBugIdLogo)):
        iLastColor = -1
        asBugIdLogoPrintArgument = [""]
        sCharsLine = asBugIdLogo[uLineIndex]
        sColorsLine = asBugIdLogoColors[uLineIndex]
        for uColumnIndex in xrange(len(sCharsLine)):
            sColor = sColorsLine[uColumnIndex]
            iColor = sColor == " " and -1 or int(sColor, 16)
            if iColor != iLastColor:
                asBugIdLogoPrintArgument.extend([iColor, ""])
                iColor = iLastColor
            sChar = sCharsLine[uColumnIndex]
            asBugIdLogoPrintArgument[-1] += sChar
        oConsole.fPrint(*asBugIdLogoPrintArguments)
Beispiel #15
0
def fMainProcessTerminatedHandler(oBugId, uProcessId, sBinaryName):
    if dxConfig["bApplicationTerminatesWithMainProcess"]:
        oConsole.fPrint("+ T+%.1f One of the main processes (id %d/0x%X, %s) has terminated." % \
            (oBugId.fnApplicationRunTime(), uProcessId, uProcessId, sBinaryName))
        oConsole.fPrint()
        oConsole.fStatus(INFO, "* BugId is stopping...")
        oBugId.fStop()
    elif not gbQuiet:
        oConsole.fPrint("+ T+%.1f One of the main processes (id %d/0x%X, %s) has terminated." % \
            (oBugId.fnApplicationRunTime(), uProcessId, uProcessId, sBinaryName))
        oConsole.fPrint()
Beispiel #16
0
def fbApplyConfigSetting(sSettingName, xValue, sIndentation): # sIndentation is None means no output!
  asGroupNames = sSettingName.split("."); # last element is not a group name
  sFullName = ".".join(asGroupNames);
  sSettingName = asGroupNames.pop();          # so pop it.
  dxConfigGroup = dxConfig;
  asHandledGroupNames = [];
  for sGroupName in asGroupNames:
    asHandledGroupNames.append(sGroupName);
    if sGroupName not in dxConfigGroup:
      oConsole.fPrint(
        ERROR, "Unknown config group ",
        ERROR_INFO, ".".join(asHandledGroupNames),
        ERROR, " in setting name ",
        ERROR_INFO, sFullName,
        ERROR, ".",
      );
      return False;
    dxConfigGroup = dxConfigGroup.get(sGroupName, {});
  if sSettingName not in dxConfigGroup:
    if len(asHandledGroupNames) > 0:
      oConsole.fPrint(
        ERROR, "Unknown setting name ",
        ERROR_INFO, sSettingName,
        ERROR, " in config group ",
        ERROR_INFO, ".".join(asHandledGroupNames),
        ERROR, ".",
      );
    else:
      oConsole.fPrint(
        ERROR, "Unknown setting name ",
        ERROR_INFO, sSettingName,
        ERROR, ".",
      );
    return False;
  if json.dumps(dxConfigGroup[sSettingName]) == json.dumps(xValue):
    if sIndentation is not None:
      oConsole.fPrint(sIndentation, "* The default value for config setting ", HILITE, sFullName, NORMAL, \
          " is ", json.dumps(dxConfigGroup[sSettingName]), ".");
  else:
    if sIndentation is not None:
      oConsole.fPrint(sIndentation, "+ Changed config setting ", HILITE, sFullName, NORMAL, \
          " from ", HILITE, repr(dxConfigGroup[sSettingName]), NORMAL, " to ", INFO, repr(xValue), NORMAL, ".");
    dxConfigGroup[sSettingName] = xValue;
  return True;
Beispiel #17
0
def fPrintMessageForProcess(sHeaderChar, oProcess, bIsMainProcess, *asMessage):
  # oProcess is a mWindowsAPI.cProcess or derivative.
  if sHeaderChar is None:
    # Just blanks for the header (used for multi-line output to reduce redundant output).
    oConsole.fPrint(
      " ", " ", bIsMainProcess and "    " or "   ", "         ",
      " " * len("%d" % oProcess.uId), " ", " " * len("0x%X" % oProcess.uId),
      "  ", " " * len(oProcess.sBinaryName), "   ",
      *asMessage,
      uConvertTabsToSpaces = 8
    );
  else:
    oConsole.fPrint(
      sHeaderChar, " ", bIsMainProcess and "Main" or "Sub", " process ",
      INFO, "%d" % oProcess.uId, NORMAL, "/", INFO , "0x%X" % oProcess.uId, NORMAL,
      " (", INFO, oProcess.sBinaryName, NORMAL, "): ",
      *asMessage,
      uConvertTabsToSpaces = 8
    );
Beispiel #18
0
def fFailedToDebugApplicationCallback(oBugId, sErrorMessage):
  global gbAnErrorOccured;
  gbAnErrorOccured = True;
  oConsole.fLock();
  try:
    oConsole.fPrint(ERROR, u"\u250C\u2500", ERROR_INFO, " Failed to debug the application ", ERROR, sPadding = u"\u2500");
    for sLine in sErrorMessage.split("\n"):
      oConsole.fPrint(ERROR, u"\u2502 ", ERROR_INFO, sLine.rstrip("\r"));
    oConsole.fPrint(ERROR, u"\u2514", sPadding = u"\u2500");
    oConsole.fPrint();
  finally:
    oConsole.fUnlock();
Beispiel #19
0
def fTestNetworkListen(oProcess, sProcessType, sNetworkType, sIPAddress, uPort, sSandBoxToolsPath):
  sTestType = "network %s listen" % sNetworkType;
  if fbHaveResults(sProcessType, sTestType):
    oConsole.fPrint("Testing %s already completed" % sTestType);
    return;
  oConsole.fPrint("Testing ", sNetworkType, " network listen for process ", INFO, str(oProcess.uId), NORMAL, " running ", INFO, oProcess.sBinaryName);
  oTestProcess = subprocess.Popen(
    "\"%s\\CheckNetworkAccess\\bin\\Debug\\CheckNetworkAccess.exe\" -p %d -l %s %d" % (sSandBoxToolsPath, oProcess.uId, sIPAddress, uPort),
    stdout = subprocess.PIPE,
    stderr = subprocess.PIPE,
  );
  # The process is currently suspended, so it can't actually accept a connection.
  # This means the test will hang if it can listen on a port, even if we try to
  # connect to the port. Instead, we will use a timer to terminate the test
  # after five seconds and see if it outputs an error or not.
  abConnectionAccepted = []; # Weird hack to be able to export info from another thread.
  def fConnect():
    oConsole.fStatus("fConnect: Attempting to connect to ", INFO, sIPAddress, NORMAL, ":", INFO, str(uPort), NORMAL, "...");
    try:
      socket.create_connection((sIPAddress, uPort), 5);
    except socket.error as oException:
      abConnectionAccepted.append(False);
    else:
      abConnectionAccepted.append(True);
    oTestProcess.terminate();
  oConnectThread = threading.Thread(target=fConnect);
  oConnectThread.start();
  (sStdOut, sStdErr) = oTestProcess.communicate();
  assert not sStdErr, "Failed:\r\n%s" % sStdErr;
  oConnectThread.join();
  bConnectionAccepted = abConnectionAccepted[0];
  if bConnectionAccepted:
    fWriteResults(sProcessType, sTestType, False, "+ This process is able to listen for connections on the %s at %s:%d.\r\n%s" % (sNetworkType, sIPAddress, uPort, sStdOut));
  elif sStdOut in [
    "Make a connection to %s:%d\r\n" % (sIPAddress, uPort), # It thinks it can accept connections, but it never will.
    "Unknown error (0x271d)\r\n",
    "An attempt was made to access a socket in a way forbidden by its access permissions\r\n",
    "An attempt was made to access a socket in a way that was forbidden by its access permissions\r\n",
    "(0xC0000022) - {Access Denied}\r\nA process has requested access to an object, but has not been granted those access rights.\r\n",
  ]:
    fWriteResults(sProcessType, sTestType, True);
  else:
    raise AssertionError("Unknown test output: %s" % sStdOut);
Beispiel #20
0
def fPrintProductDetails(oProductDetails, bIsMainProduct, bShowInstallationFolders):
  oConsole.fPrint(*(
    [
      u"\u2502 \u2219 ", bIsMainProduct and HILITE or INFO, oProductDetails.sProductName,
      NORMAL, " version: ", INFO, str(oProductDetails.oProductVersion),
    ] + (
      bShowInstallationFolders and [
        NORMAL, " installed at ", INFO, oProductDetails.sInstallationFolderPath,
      ] or [ ]
    ) + (
      not oProductDetails.oLicense and (
        (oProductDetails.bHasTrialPeriod and oProductDetails.bInTrialPeriod) and [
          NORMAL, " ", WARNING, "(in trial period)",
        ] or [
          NORMAL, " ", ERROR, "(no valid license found)",
        ]
      ) or []
    ) + [
      NORMAL, ".",
    ]
  ));
  if oProductDetails.oLatestProductVersion:
    if oProductDetails.bVersionIsPreRelease:
      oConsole.fPrint(
        u"\u2502     You are running a ", HILITE, "pre-release", NORMAL, " version:",
        " the latest released version is ", INFO, str(oProductDetails.oLatestProductVersion), NORMAL, ".",
      );
    elif not oProductDetails.bVersionIsUpToDate:
      oConsole.fPrint(
        u"\u2502     You are running an ", WARNING, "old", NORMAL, " version:",
        " the latest released version is ", HILITE, str(oProductDetails.oLatestProductVersion), NORMAL, ",",
        " available at ", HILITE, oProductDetails.oRepository.sLatestVersionURL, NORMAL, ".",
      );
Beispiel #21
0
 def fResponseTimeoutServerThread():
   (oClientSocket, (sClientIP, uClientPort)) = oResponseTimeoutServerSocket.accept();
   oConsole.fPrint("Response timeout server receiving request...");
   oClientSocket.recv(0x1000);
   oConsole.fPrint("Response timeout server is sleeping to avoid sending a response...");
   oServersShouldBeRunningLock.acquire();
   oServersShouldBeRunningLock.release();
   oConsole.fPrint("Response timeout thread terminated.");
Beispiel #22
0
 def fOutputEventDetails(oWithCallbacks, *txArguments,
                         **dxArguments):
     oConsole.fPrint(sWithCallbacks or str(oWithCallbacks), " => ",
                     repr(sEventName))
     for xValue in txArguments:
         oConsole.fPrint("  ", str(xValue))
     for (sName, xValue) in dxArguments.items():
         oConsole.fPrint("  ", sName, " = ", str(xValue))
Beispiel #23
0
 def fOutOfBandDataServerThread():
   (oClientSocket, (sClientIP, uClientPort)) = oOutOfBandDataServerSocket.accept();
   oConsole.fPrint("Out-of-band data server receiving request...");
   oClientSocket.recv(0x1000);
   oConsole.fPrint("Out-of-band data server sending valid response with out-of-band data...");
   oClientSocket.send(oResponse.fsSerialize() + "X");
   oConsole.fPrint("Out-of-band data server thread terminated.");
   oClientSocket.close();
Beispiel #24
0
 def fConnectionShutdownServerThread():
   (oClientSocket, (sClientIP, uClientPort)) = oConnectionShutdownServerSocket.accept();
   oConsole.fPrint("Shutdown server is shutting down the connection for writing...");
   oClientSocket.shutdown(socket.SHUT_WR);
   oConsole.fPrint("Shutdown server is sleeping to keep the connection open....");
   oServersShouldBeRunningLock.acquire();
   oServersShouldBeRunningLock.release();
   oConsole.fPrint("Shutdown server thread terminated.");
def fTestProcessAccess(oProcess, sProcessType, sSandBoxToolsPath):
    sTestType = "process access"
    if fbHaveResults(sProcessType, sTestType):
        oConsole.fPrint("Testing %s already completed" % sTestType)
        return
    oConsole.fPrint("Testing process access for process ", INFO,
                    str(oProcess.uId), NORMAL, " running ", INFO,
                    oProcess.sBinaryName)
    oTestProcess = subprocess.Popen(
        "\"%s\\CheckProcessAccess\\bin\\Debug\\CheckProcessAccess.exe\" -p %d"
        % (sSandBoxToolsPath, oProcess.uId),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    (sStdOut, sStdErr) = oTestProcess.communicate()
    assert not sStdErr, "Failed:\r\n%s" % sStdErr
    if sStdOut == "":
        fWriteResults(sProcessType, sTestType, True)
    else:
        fWriteResults(
            sProcessType, sTestType, False,
            "This process is able to access the following processes:\r\n%s" %
            sStdOut)
Beispiel #26
0
def fLicenseErrorsCallback(oBugId, asErrors):
  # These should have been reported before cBugId was even instantiated, so this is kind of unexpected.
  # But rather than raise AssertionError("NOT REACHED"), we'll report the license error gracefully:
  global gbAnErrorOccured;
  gbAnErrorOccured = True;
  oConsole.fLock();
  try:
    oConsole.fPrint(ERROR, u"\u250C\u2500", ERROR_INFO, " Software license error ", ERROR, sPadding = u"\u2500");
    for sError in asErrors:
      oConsole.fPrint(ERROR, u"\u2502 ", ERROR_INFO, sError);
    oConsole.fPrint(ERROR, u"\u2514", sPadding = u"\u2500");
  finally:
    oConsole.fUnlock();
  os._exit(5);
def fTestAutomaticProxyClient(oCertificateStore, nEndWaitTimeoutInSeconds,
                              f0LogEvents):
    oConsole.fPrint(
        "\xFE\xFE\xFE\xFE Creating a cHTTPClientUsingAutomaticProxyServer instance... ",
        sPadding="\xFE")
    oHTTPClient = cHTTPClientUsingAutomaticProxyServer(
        bAllowUnverifiableCertificatesForProxy=True,
        o0zCertificateStore=oCertificateStore,
        n0zConnectTimeoutInSeconds=
        1,  # Make sure connection attempts time out quickly to trigger a timeout exception.
    )
    if f0LogEvents: f0LogEvents(oHTTPClient, "oHTTPClient")

    oConsole.fPrint(
        "\xFE\xFE\xFE\xFE Running client tests through automatic proxy server... ",
        sPadding="\xFE")
    fTestClient(oHTTPClient, oCertificateStore, nEndWaitTimeoutInSeconds)

    oConsole.fPrint(
        "\xFE\xFE\xFE\xFE Stopping cHTTPClientUsingAutomaticProxyServer instance... ",
        sPadding="\xFE")
    oHTTPClient.fStop()
    assert oHTTPClient.fbWait(nEndWaitTimeoutInSeconds), \
        "cHTTPClientUsingAutomaticProxyServer instance did not stop in time"
Beispiel #28
0
def fPrintUsage(asApplicationKeywords):
    oConsole.fLock()
    try:
        oConsole.fPrint(HILITE, "Usage:")
        oConsole.fPrint()
        oConsole.fPrint(
            INFO,
            "  BugId.py [options] \"path\\to\\binary.exe\" [-- argument [argument [...]]]"
        )
        oConsole.fPrint(
            "    Start the binary in the debugger with the provided arguments."
        )
        oConsole.fPrint()
        oConsole.fPrint(INFO, "  BugId.py [options] --pids=pid[,pid[...]]")
        oConsole.fPrint(
            "    Attach debugger to the process(es) provided in the list. The processes must"
        )
        oConsole.fPrint(
            "    all have been suspended, as they will be resumed by the debugger."
        )
        oConsole.fPrint()
        oConsole.fPrint(
            INFO,
            "  BugId.py [options] --package=[full package name] [-- argument]")
        oConsole.fPrint(
            "    Start and debug the Universal Windows App specified through its package"
        )
        oConsole.fPrint("    name with the provided argument.")
        oConsole.fPrint()
        oConsole.fPrint(
            INFO,
            "  BugId.py [options] application [options] [-- argument [argument [...]]]"
        )
        oConsole.fPrint(
            "    (Where \"application\" is a known application keyword, see below)"
        )
        oConsole.fPrint(
            "    BugId has a list of known applications for which it has specific settings,"
        )
        oConsole.fPrint(
            "    can automatically find the binary on your system, or knows the package name,"
        )
        oConsole.fPrint(
            "    and knows what arguments you will probably want to supply. This makes it a"
        )
        oConsole.fPrint(
            "    lot easier to start an application and apply common settings."
        )
        oConsole.fPrint(
            "    You may be able to overwrite some of these settings by providing different"
        )
        oConsole.fPrint(
            "    values after the keyword. You can also provide a binary path after the"
        )
        oConsole.fPrint(
            "    keyword to use a different binary than BugId detects, or if BugId is unable."
        )
        oConsole.fPrint("    to detect the binary on your system.")
        oConsole.fPrint()
        oConsole.fPrint(HILITE, "Options:")
        oConsole.fPrint(INFO, "  -h, --help")
        oConsole.fPrint("    This cruft.")
        oConsole.fPrint(INFO, "  -q, --quiet")
        oConsole.fPrint("    Output only essential information.")
        oConsole.fPrint(INFO, "  -v, --verbose")
        oConsole.fPrint(
            "    Output all commands send to cdb.exe and everything it outputs in return."
        )
        oConsole.fPrint("    Note that -q and -v are not mutually exclusive.")
        oConsole.fPrint(INFO, "  -f, --fast")
        oConsole.fPrint(
            "    Create no HTML report, do not use symbols. This is an alias for:"
        )
        oConsole.fPrint("        ", HILITE, "--bGenerateReportHTML=false")
        oConsole.fPrint("        ", HILITE, "--cBugId.asSymbolServerURLs=[]")
        oConsole.fPrint("        ", HILITE,
                        "--cBugId.bUse_NT_SYMBOL_PATH=false")
        oConsole.fPrint(INFO, "  -r, --repeat")
        oConsole.fPrint(
            "    Restart the application to run another test as soon as the application is"
        )
        oConsole.fPrint(
            "    terminated. Useful when testing the reliability of a repro, detecting the"
        )
        oConsole.fPrint(
            "    various crashes a non-deterministic repro can cause or while making "
        )
        oConsole.fPrint(
            "    modifications to the repro in order to test how they affect the crash."
        )
        oConsole.fPrint(
            "    A statistics file is created or updated after each run that contains the"
        )
        oConsole.fPrint(
            "    number of occurances of each Bug Id that was detected.")
        oConsole.fPrint(INFO, "  --isa=x86|x64")
        oConsole.fPrint(
            "    Use the x86 or x64 version of cdb to debug the application. The default is"
        )
        oConsole.fPrint(
            "    to use the ISA* of the OS. Applications build to run on x86 systems can be"
        )
        oConsole.fPrint(
            "    debugged using the x64 version of cdb, and you are strongly encouraged to "
        )
        oConsole.fPrint(
            "    do so. But you can use the x86 debugger to debug x86 application if you"
        )
        oConsole.fPrint("    want to. (ISA = Instruction Set Architecture)")
        oConsole.fPrint(INFO, "  --version")
        oConsole.fPrint(
            "    Show cBugId version and that of its sub-modules and check for updates."
        )
        oConsole.fPrint()
        oConsole.fPrint(
            "Options also include any of the settings in dxConfig.py; you can specify them"
        )
        oConsole.fPrint("using ", HILITE, "--[name]=[JSON value]", NORMAL,
                        ". Here are some examples:")
        oConsole.fPrint(INFO, "  --bGenerateReportHTML=false")
        oConsole.fPrint(
            "    Do not save a HTML formatted crash report. This should make BugId run"
        )
        oConsole.fPrint(
            "    faster and use less RAM, as it does not need to gather and process the"
        )
        oConsole.fPrint("    information needed for the HTML report.")
        oConsole.fPrint(
            "    If you only need to confirm a crash can be reproduced, you may want to use"
        )
        oConsole.fPrint(
            "    this: it can make the process of analyzing a crash a lot faster. But if"
        )
        oConsole.fPrint(
            "    no local or cached symbols are available, you'll get less information"
        )
        oConsole.fPrint(INFO, "  \"--sReportFolderPath=\\\"BugId\\\"\"")
        oConsole.fPrint(
            "    Save report to the specified folder, in this case \"BugId\". The quotes"
        )
        oConsole.fPrint(
            "    mess is needed because of the Windows quirck explained below."
        )
        oConsole.fPrint("The remaining dxConfig settings are:")
        for sSettingName in sorted(dxConfig.keys()):
            if sSettingName not in [
                    "bGenerateReportHTML", "sReportFolderPath", "cBugId"
            ]:
                xSettingValue = dxConfig[sSettingName]
                oConsole.fPrint("  ", INFO, "--", sSettingName,
                                NORMAL, " (default value: ", HILITE,
                                str(xSettingValue), NORMAL, ")")
        oConsole.fPrint("See ", HILITE, "dxConfig.py", NORMAL,
                        " for details on each setting.")
        oConsole.fPrint()
        oConsole.fPrint(
            "You can also adjust cBugId specific settings, such as:")
        oConsole.fPrint(INFO, "  --cBugId.bSaveDump=true")
        oConsole.fPrint("    Save a debug dump file when a crash is detected.")
        oConsole.fPrint(
            INFO,
            "  --cBugId.asSymbolServerURLs=[\"http://msdl.microsoft.com/download/symbols\"]"
        )
        oConsole.fPrint(
            "    Use http://msdl.microsoft.com/download/symbols as a symbol server."
        )
        oConsole.fPrint(INFO,
                        "  --cBugId.asSymbolCachePaths=[\"C:\\Symbols\"]")
        oConsole.fPrint("    Use C:\\Symbols to cache symbol files.")
        oConsole.fPrint()
        oConsole.fPrint("See ", HILITE, "cBugId\\dxConfig.py", NORMAL,
                        " for details on all available settings.")
        oConsole.fPrint(
            "All values must be valid JSON of the appropriate type. No checks are made to"
        )
        oConsole.fPrint(
            "ensure this! Providing illegal values may result in exceptions at any time"
        )
        oConsole.fPrint("during execution. You have been warned!")
        oConsole.fPrint()
        oConsole.fPrint(
            "Note that you may need to do a bit of \"quote-juggling\" because Windows likes"
        )
        oConsole.fPrint(
            "to eat quotes for no obvious reason. So, if you want to specify --a=\"b\", you"
        )
        oConsole.fPrint(
            "will need to use \"--a=\\\"b\\\"\", or BugId will see --a=b and `b` is not valid"
        )
        oConsole.fPrint("JSON.")
        oConsole.fPrint()
        oConsole.fPrint("Known application keywords:")
        asLine = ["  "]
        uLineLength = 2
        for sApplicationKeyword in asApplicationKeywords:
            if uLineLength > 2:
                if uLineLength + 2 + len(sApplicationKeyword) + 2 > 80:
                    asLine += [NORMAL, ","]
                    oConsole.fPrint(*asLine)
                    asLine = ["  "]
                    uLineLength = 2
                else:
                    asLine += [NORMAL, ", "]
                    uLineLength += 2
            asLine += [INFO, sApplicationKeyword]
            uLineLength += len(sApplicationKeyword)
        asLine += [NORMAL, "."]
        oConsole.fPrint(*asLine)
        oConsole.fPrint()
        oConsole.fPrint(
            "Run ", HILITE, "BugId.py application?", NORMAL,
            " for an overview of the application specific command")
        oConsole.fPrint("line arguments and settings.")
        oConsole.fPrint()
        oConsole.fPrint(
            "BugId will set it errorlevel/exit code to one of the following values:"
        )
        oConsole.fPrint("  ", INFO, "0", NORMAL,
                        " = BugId successfully ran the application ", HILITE,
                        "without detecting a bug", NORMAL, ".")
        oConsole.fPrint("  ", INFO, "1", NORMAL,
                        " = BugId successfully ran the application and ",
                        HILITE, "detected a bug", NORMAL, ".")
        oConsole.fPrint(
            "  ", INFO, "2", NORMAL,
            " = BugId was unable to parse the command-line arguments provided."
        )
        oConsole.fPrint(
            "  ", INFO, "3", NORMAL,
            " = BugId ran into an internal error: pleace report the details!")
        oConsole.fPrint(
            "  ", INFO, "4", NORMAL,
            " = BugId was unable to start or attach to the application.")
    finally:
        oConsole.fUnlock()
def fPrintExceptionInformation(oException, oTraceback):
    fConsoleOutputExceptionDetails(oException, o0Traceback=oTraceback)
    oConsole.fLock()
    try:
        oConsole.fPrint()
        oConsole.fPrint(
            "Please report the above details at the below web-page so it can be addressed:"
        )
        oConsole.fPrint(INFO,
                        "    https://github.com/SkyLined/BugId/issues/new")
        oConsole.fPrint(
            "If you do not have a github account, or you want to report this issue"
        )
        oConsole.fPrint("privately, you can also send an email to:")
        oConsole.fPrint(INFO, "    [email protected]")
        oConsole.fPrint()
        oConsole.fPrint(
            "In your report, please copy the information about the exception reported"
        )
        oConsole.fPrint(
            "above, as well as the stack trace and BugId version information. This makes"
        )
        oConsole.fPrint(
            "it easier to determine the cause of this issue and makes for faster fixes."
        )
        oConsole.fPrint()
        if not any([
                sVerbose in sys.argv[1:]
                for sVerbose in ["-v", "/v", "-V", "/V", "--verbose=true"]
        ]):
            oConsole.fPrint(
                "If you can reproduce the issue, it would help a lot if you can run BugId in"
            )
            oConsole.fPrint("verbose mode by adding the ", INFO, "--verbose",
                            NORMAL, " command-line argument.")
            oConsole.fPrint("as in: ", HILITE, "BugId -v ",
                            " ".join(sys.argv[1:]))
            oConsole.fPrint()
        fPrintVersionInformation(
            bCheckForUpdates=False,
            bCheckAndShowLicenses=False,
            bShowInstallationFolders=False,
        )
        oConsole.fPrint("Thank you in advance for helping to improve BugId!")
    finally:
        oConsole.fUnlock()
def fPrintUsageInformation(asApplicationKeywords):
    oConsole.fLock()
    try:
        oConsole.fPrint(HILITE, "Usage:")
        oConsole.fPrint()
        oConsole.fPrint(
            INFO,
            "  BugId.py [options] <target> [options] [-- argument [argument [...]]]"
        )
        oConsole.fPrint()
        oConsole.fPrint(HILITE, "Targets:")
        oConsole.fPrint(INFO, "  \"path\\to\\binary.exe\"")
        oConsole.fPrint(
            "    Start the given binary in the debugger with the given arguments."
        )
        oConsole.fPrint(INFO, "  --pids=pid[,pid[...]]")
        oConsole.fPrint(
            "    Attach debugger to the process(es) provided in the list. The processes ",
            HILITE, "must")
        oConsole.fPrint(
            "    all have been suspended, as they will be resumed by the debugger."
        )
        oConsole.fPrint(
            "    Arguments cannot be provided for obvious reasons.")
        oConsole.fPrint(INFO, "  --uwp-app=<package name>[!<application id>]")
        oConsole.fPrint(
            "    Start and debug a Universal Windows Platform App identified by the given"
        )
        oConsole.fPrint(
            "    package name and application id. If no application id is provided and the"
        )
        oConsole.fPrint(
            "    package exposes only one if, it will default to that id. Note that only"
        )
        oConsole.fPrint(
            "    a single argument can be passed to a UWP App; additional arguments will be"
        )
        oConsole.fPrint("    silently ignored.")
        oConsole.fPrint(INFO, "  <known application keyword>")
        oConsole.fPrint(
            "    BugId has a list of known targets that are identified by a keyword. You can"
        )
        oConsole.fPrint(
            "    use such a keyword to have BugId try to automatically find the binary or"
        )
        oConsole.fPrint(
            "    determine the package name and id for that application, optionally apply"
        )
        oConsole.fPrint(
            "    application specific settings and provide default arguments. This makes it"
        )
        oConsole.fPrint(
            "    easier to run these applications without having to manually provide these."
        )
        oConsole.fPrint(
            "    You can optioanlly override any default settings by providing them *after*"
        )
        oConsole.fPrint(
            "    the keyword. You can also provide the path to the application binary after"
        )
        oConsole.fPrint(
            "    the keyword to use a different binary than the one BugId automatically"
        )
        oConsole.fPrint(
            "    detects, or if BugId is unable to detect the binary on your system."
        )
        oConsole.fPrint()
        oConsole.fPrint(HILITE, "Options:")

        oConsole.fPrint(INFO, "  -c, --collateral[=number of bugs]")
        oConsole.fPrint(
            "    When the specified number of bugs is larger than 1 (default 5), BugId will"
        )
        oConsole.fPrint(
            "    go into \"collateral bug handling\" mode. This means that after certain"
        )
        oConsole.fPrint(
            "    access violation bugs are reported, it will attempt to \"fake\" that the"
        )
        oConsole.fPrint(
            "    instruction that caused the exception succeeded without triggering an"
        )
        oConsole.fPrint(
            "    exception. For read operations, it will set the destination register to a"
        )
        oConsole.fPrint(
            "    tainted value (0x41414141...). For write operations, it will simply step"
        )
        oConsole.fPrint(
            "    over the instruction. It will do this for up to the specified number of"
        )
        oConsole.fPrint("    bugs.")
        oConsole.fPrint(
            "    The upshot of this is that you can get an idea of what would happen if"
        )
        oConsole.fPrint(
            "    you were able to control the bad read/write operation. This can be usedful"
        )
        oConsole.fPrint(
            "    when determining if a particular vulnerability is theoretically exploitable"
        )
        oConsole.fPrint(
            "    or not. E.g. it might show that nothing else happens, that the application"
        )
        oConsole.fPrint(
            "    crashes unavoidably and immediately, both of which indicate that the issue"
        )
        oConsole.fPrint(
            "    is not exploitable. It might also show that reading from or writing to"
        )
        oConsole.fPrint(
            "    otherwise inaccessible parts of memory or controlling execution flow is"
        )
        oConsole.fPrint(
            "    potentially possible, indicating it is exploitable.")

        oConsole.fPrint(INFO, "  -d, --dump")
        oConsole.fPrint("    Save a mini crash dump when a crash is detected.")

        oConsole.fPrint(INFO, "  --full-dump")
        oConsole.fPrint("    Save a full crash dump when a crash is detected.")

        oConsole.fPrint(INFO, "  -f, --fast")
        oConsole.fPrint(
            "    Create no HTML report, do not use symbols. This is an alias for:"
        )
        oConsole.fPrint("        ", INFO, "--bGenerateReportHTML=false")
        oConsole.fPrint("        ", INFO, "--cBugId.asSymbolServerURLs=[]")
        oConsole.fPrint("        ", INFO, "--cBugId.bUse_NT_SYMBOL_PATH=false")

        oConsole.fPrint(INFO, "  -h, --help")
        oConsole.fPrint("    This cruft.")

        oConsole.fPrint(INFO, "  -I [arguments]")
        oConsole.fPrint(
            "    Install as the default JIT debugger on the system. This allows BugId to"
        )
        oConsole.fPrint(
            "    generate a report whenever an application crashes.")
        oConsole.fPrint(
            "    All arguments after -I will be passed to BugId whenever it is started as"
        )
        oConsole.fPrint(
            "    the JIT debugger. It might be useful to add arguments such as \"--pause\""
        )
        oConsole.fPrint(
            "    to leave the BugId window open after it generated a report, \"--full-dump\""
        )
        oConsole.fPrint(
            "    to generate a full memory dump and \"--reports=<path>\" to have the reports"
        )
        oConsole.fPrint(
            "    stored in a specific folder. Otherwise, the location where reports and"
        )
        oConsole.fPrint("    other output are stored is not known.")

        oConsole.fPrint(INFO, "  --isa=x86|x64")
        oConsole.fPrint(
            "    Use the x86 or x64 version of cdb to debug the application. The default is"
        )
        oConsole.fPrint(
            "    to use the ISA* of the OS. Applications build to run on x86 systems can be"
        )
        oConsole.fPrint(
            "    debugged using the x64 version of cdb, and you are strongly encouraged to "
        )
        oConsole.fPrint(
            "    do so. But you can use the x86 debugger to debug x86 application if you"
        )
        oConsole.fPrint("    want to. (ISA = Instruction Set Architecture)")

        oConsole.fPrint(INFO, "  -p, --pause")
        oConsole.fPrint(
            "    Always wait for the user to press ENTER before terminating at the end."
        )

        oConsole.fPrint(INFO, "  -q, --quiet")
        oConsole.fPrint("    Output only essential information.")

        oConsole.fPrint(INFO, "  -r, --repeat[=number of loops]")
        oConsole.fPrint(
            "    Restart the application to run another test as soon as the application is"
        )
        oConsole.fPrint(
            "    terminated. Useful when testing the reliability of a repro, detecting the"
        )
        oConsole.fPrint(
            "    various crashes a non-deterministic repro can cause or while making "
        )
        oConsole.fPrint(
            "    modifications to the repro in order to test how they affect the crash."
        )
        oConsole.fPrint(
            "    A statistics file is created or updated after each run that contains the"
        )
        oConsole.fPrint(
            "    number of occurances of each Bug Id that was detected. If a number is"
        )
        oConsole.fPrint(
            "    provided, the application will be run that many times. Otherwise the"
        )
        oConsole.fPrint("    application will be run indefinitely.")

        oConsole.fPrint(INFO, "  --symbols=path\\to\\symbols\\folder")
        oConsole.fPrint(
            "    Use the given path as a local symbol folder in addition to the symbol paths"
        )
        oConsole.fPrint(
            "    specified in dxConfig. You can provide this option multiple times to add"
        )
        oConsole.fPrint("    as many additional local symbol paths as needed.")

        oConsole.fPrint(INFO, "  --reports=path\\to\\reports\\folder")
        oConsole.fPrint(
            "    Store reports in the given path. Optional cdb output and crash dumps are"
        )
        oConsole.fPrint("    stored in the same location.")

        oConsole.fPrint(INFO, "  -v, --verbose")
        oConsole.fPrint(
            "    Output all commands send to cdb.exe and everything it outputs in return."
        )
        oConsole.fPrint("    Note that -q and -v are not mutually exclusive.")

        oConsole.fPrint(INFO, "  --version")
        oConsole.fPrint("    Show version information and check for updates.")

        oConsole.fPrint()
        oConsole.fPrint(
            "  Options also include any of the settings in dxConfig.py; you can specify them"
        )
        oConsole.fPrint("  using ", INFO, "--[name]=[JSON value]", NORMAL,
                        ". Here are some examples:")
        oConsole.fPrint(INFO, "  --bGenerateReportHTML=false")
        oConsole.fPrint(
            "    Do not save a HTML formatted crash report. This should make BugId run"
        )
        oConsole.fPrint(
            "    faster and use less RAM, as it does not need to gather and process the"
        )
        oConsole.fPrint("    information needed for the HTML report.")
        oConsole.fPrint(
            "    If you only need to confirm a crash can be reproduced, you may want to use"
        )
        oConsole.fPrint(
            "    this: it can make the process of analyzing a crash a lot faster. But if"
        )
        oConsole.fPrint(
            "    no local or cached symbols are available, you'll get less information"
        )
        oConsole.fPrint(INFO, "  \"--sReportFolderPath=\\\"BugId\\\"\"")
        oConsole.fPrint(
            "    Save report to the specified folder, in this case \"BugId\". The quotes"
        )
        oConsole.fPrint(
            "    mess is needed because of the Windows quirck explained below."
        )
        oConsole.fPrint("  The remaining dxConfig settings are:")
        for sSettingName in sorted(dxConfig.keys()):
            if sSettingName not in [
                    "bGenerateReportHTML", "sReportFolderPath", "cBugId"
            ]:
                xSettingValue = dxConfig[sSettingName]
                oConsole.fPrint("  ", INFO, "--", sSettingName,
                                NORMAL, " (default value: ", INFO,
                                str(xSettingValue), NORMAL, ")")
        oConsole.fPrint("  See ", INFO, "dxConfig.py", NORMAL,
                        " for details on each setting.")
        oConsole.fPrint()
        oConsole.fPrint(
            "  You can also adjust cBugId specific settings, such as:")
        oConsole.fPrint(
            INFO,
            "  --cBugId.asSymbolServerURLs=[\"http://msdl.microsoft.com/download/symbols\"]"
        )
        oConsole.fPrint(
            "    Use http://msdl.microsoft.com/download/symbols as a symbol server."
        )
        oConsole.fPrint(INFO,
                        "  --cBugId.asSymbolCachePaths=[\"C:\\Symbols\"]")
        oConsole.fPrint("    Use C:\\Symbols to cache symbol files.")
        oConsole.fPrint("  See ", INFO, "cBugId\\dxConfig.py", NORMAL,
                        " for details on all available settings.")
        oConsole.fPrint(
            "  All values must be valid JSON of the appropriate type. No checks are made to"
        )
        oConsole.fPrint(
            "  ensure this! Providing illegal values may result in exceptions at any time"
        )
        oConsole.fPrint("  during execution. You have been warned!")
        oConsole.fPrint()
        oConsole.fPrint(
            "  Note that you may need to do a bit of \"quote-juggling\" because Windows likes"
        )
        oConsole.fPrint(
            "  to eat quotes for no obvious reason. So, if you want to specify --a=\"b\", you"
        )
        oConsole.fPrint(
            "  will need to use \"--a=\\\"b\\\"\", or BugId will see --a=b and `b` is not valid"
        )
        oConsole.fPrint("  JSON.")
        oConsole.fPrint()
        oConsole.fPrint(HILITE, "Known application keywords:")
        asLine = ["  "]
        uLineLength = 2
        for sApplicationKeyword in asApplicationKeywords:
            if uLineLength > 2:
                if uLineLength + 2 + len(sApplicationKeyword) + 2 > 80:
                    asLine += [NORMAL, ","]
                    oConsole.fPrint(*asLine)
                    asLine = ["  "]
                    uLineLength = 2
                else:
                    asLine += [NORMAL, ", "]
                    uLineLength += 2
            asLine += [INFO, sApplicationKeyword]
            uLineLength += len(sApplicationKeyword)
        asLine += [NORMAL, "."]
        oConsole.fPrint(*asLine)
        oConsole.fPrint()
        oConsole.fPrint(
            "  Run ", INFO, "BugId.py application?", NORMAL,
            " for an overview of the application specific command")
        oConsole.fPrint("  line arguments and settings.")
        oConsole.fPrint()
        oConsole.fPrint(HILITE, "Exit codes:")
        oConsole.fPrint("  ", INFO, "0", NORMAL,
                        " = BugId successfully ran the application ",
                        UNDERLINE, "without detecting a bug", NORMAL, ".")
        oConsole.fPrint("  ", INFO, "1", NORMAL,
                        " = BugId successfully ran the application and ",
                        UNDERLINE, "detected a bug", NORMAL, ".")
        oConsole.fPrint(
            "  ", ERROR_INFO, "2", NORMAL,
            " = BugId was unable to parse the command-line arguments provided."
        )
        oConsole.fPrint(
            "  ", ERROR_INFO, "3", NORMAL,
            " = BugId ran into an internal error: please report the details!")
        oConsole.fPrint(
            "  ", ERROR_INFO, "4", NORMAL,
            " = BugId was unable to start or attach to the application.")
        oConsole.fPrint("  ", ERROR_INFO, "5", NORMAL,
                        " = You do not have a valid license.")
    finally:
        oConsole.fUnlock()