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();
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()
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
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();
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);
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);
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.");
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 );
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.");
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);
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()
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();
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()
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)
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()
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;
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 );
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();
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);
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, ".", );
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.");
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))
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();
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)
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"
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()