From aee269cc144f004e9e739772d7f4c82553aaf416 Mon Sep 17 00:00:00 2001
From: Miroslav Stampar <miroslav.stampar@gmail.com>
Date: Fri, 17 Feb 2012 14:22:48 +0000
Subject: [PATCH] gazillion changes, nothing will work, muhahaha

---
 lib/core/common.py                |  38 +-
 lib/core/option.py                |   1 +
 lib/core/settings.py              |   2 +
 lib/request/inject.py             | 119 ++---
 lib/techniques/blind/inference.py | 812 +++++++++++++++---------------
 lib/techniques/error/use.py       |  44 +-
 lib/techniques/union/use.py       |  18 +-
 lib/utils/resume.py               |   7 -
 8 files changed, 492 insertions(+), 549 deletions(-)

diff --git a/lib/core/common.py b/lib/core/common.py
index 51aff3807..3b63efde8 100644
--- a/lib/core/common.py
+++ b/lib/core/common.py
@@ -61,6 +61,7 @@ from lib.core.convert import unicodeencode
 from lib.core.convert import urldecode
 from lib.core.convert import urlencode
 from lib.core.enums import DBMS
+from lib.core.enums import EXPECTED
 from lib.core.enums import HTTPHEADER
 from lib.core.enums import HTTPMETHOD
 from lib.core.enums import OS
@@ -2206,10 +2207,10 @@ def trimAlphaNum(value):
 
 def isNumPosStrValue(value):
     """
-    Returns True if value is a string with a positive integer representation
+    Returns True if value is a string (or integer) with a positive integer representation
     """
 
-    return value and isinstance(value, basestring) and value.isdigit() and value != "0"
+    return (value and isinstance(value, basestring) and value.isdigit() and value != "0") or (isinstance(value, int) and value != 0)
 
 @cachedmethod
 def aliasToDbmsEnum(dbms):
@@ -3096,3 +3097,36 @@ def getCounter(technique):
     """
 
     return kb.counters.get(technique, 0)
+
+def extractExpectedValue(value, expected):
+    """
+    Extracts and returns expected value by a given type
+    """
+
+    if not expected:
+        return value
+
+    value = unArrayizeValue(value)
+
+    if isNoneValue(value):
+        value = None
+    elif expected == EXPECTED.BOOL:
+        if isinstance(value, int):
+            value = bool(value)
+        elif isinstance(value, basestring):
+            value = value.strip().lower()
+            if value in ("true", "false"):
+                value = value == "true"
+            elif value in ("1", "-1"):
+                value = True
+            elif value == "0":
+                value = False
+            else:
+                value = None
+    elif expected == EXPECTED.INT:
+        if isinstance(value, basestring):
+            if value.isdigit():
+                value = int(value)
+            else:
+                value = None
+    return value
diff --git a/lib/core/option.py b/lib/core/option.py
index 649d5c16c..9e530153a 100644
--- a/lib/core/option.py
+++ b/lib/core/option.py
@@ -1467,6 +1467,7 @@ def __setKnowledgeBaseAttributes(flushAll=True):
     kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS:0, REFLECTIVE_COUNTER.HIT:0}
     kb.responseTimes = []
     kb.resumedQueries = {}
+    kb.resumeValues = True
     kb.safeCharEncode = False
     kb.singleLogFlags = set()
     kb.skipOthersDbms = None
diff --git a/lib/core/settings.py b/lib/core/settings.py
index 8b4144202..e786e0877 100644
--- a/lib/core/settings.py
+++ b/lib/core/settings.py
@@ -57,6 +57,8 @@ DUMP_DEL_MARKER = "__DEL__"
 PARAMETER_AMP_MARKER = "__AMP__"
 PARAMETER_SEMICOLON_MARKER = "__SEMICOLON__"
 
+PARTIAL_VALUE_MARKER = "__PARTIAL__"
+
 URI_QUESTION_MARKER = "__QUESTION_MARK__"
 
 PAYLOAD_DELIMITER = "\x00"
diff --git a/lib/request/inject.py b/lib/request/inject.py
index d09be9353..3643fe0f5 100644
--- a/lib/request/inject.py
+++ b/lib/request/inject.py
@@ -17,8 +17,10 @@ from lib.core.common import calculateDeltaSeconds
 from lib.core.common import cleanQuery
 from lib.core.common import dataToSessionFile
 from lib.core.common import expandAsteriskForColumns
+from lib.core.common import extractExpectedValue
 from lib.core.common import getPublicTypeMembers
 from lib.core.common import initTechnique
+from lib.core.common import isNoneValue
 from lib.core.common import isNumPosStrValue
 from lib.core.common import isTechniqueAvailable
 from lib.core.common import parseUnionPage
@@ -72,7 +74,7 @@ def __goInference(payload, expression, charsetType=None, firstChar=None, lastCha
 
     return value
 
-def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False):
+def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None, charsetType=None, firstChar=None, lastChar=None, dump=False):
     outputs = []
     origExpr = None
 
@@ -91,16 +93,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl
         else:
             expressionReplaced = expression.replace(expressionFields, field, 1)
 
-        if resumeValue:
-            output = resume(expressionReplaced, payload)
-
-        if not output or (expected == EXPECTED.INT and not output.isdigit()):
-            if output:
-                warnMsg = "expected value type %s, resumed '%s', " % (expected, output)
-                warnMsg += "sqlmap is going to retrieve the value again"
-                logger.warn(warnMsg)
-
-            output = __goInference(payload, expressionReplaced, charsetType, firstChar, lastChar, dump)
+        output = __goInference(payload, expressionReplaced, charsetType, firstChar, lastChar, dump)
 
         if isinstance(num, int):
             expression = origExpr
@@ -109,7 +102,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl
 
     return outputs
 
-def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, resumeValue=True, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False):
+def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False):
     """
     Retrieve the output of a SQL query characted by character taking
     advantage of an blind SQL injection vulnerability on the affected
@@ -129,14 +122,6 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r
     untilLimitChar = None
     untilOrderChar = None
 
-    if resumeValue:
-        output = resume(expression, payload)
-    else:
-        output = None
-
-    if output and (expected is None or (expected == EXPECTED.INT and output.isdigit())):
-        return output
-
     if not unpack:
         return __goInference(payload, expression, charsetType, firstChar, lastChar, dump)
 
@@ -229,12 +214,8 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r
                     untilOrderChar = countedExpression.index(" ORDER BY ")
                     countedExpression = countedExpression[:untilOrderChar]
 
-                if resumeValue:
-                    count = resume(countedExpression, payload)
-
                 if not stopLimit:
-                    if not count or not count.isdigit():
-                        count = __goInference(payload, countedExpression, 2, firstChar, lastChar)
+                    count = __goInference(payload, countedExpression, 2, firstChar, lastChar)
 
                     if isNumPosStrValue(count):
                         count = int(count)
@@ -298,17 +279,12 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r
 
                         return None
 
-                elif (not count or int(count) == 0) and (not stopLimit or stopLimit == 0):
-                    if not count:
-                        warnMsg = "the SQL query provided does not "
-                        warnMsg += "return any output"
-                        logger.warn(warnMsg)
-
+                elif (not stopLimit or stopLimit == 0):
                     return None
 
                 try:
                     for num in xrange(startLimit, stopLimit):
-                        output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, resumeValue=resumeValue, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump)
+                        output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump)
                         outputs.append(output)
 
                 except KeyboardInterrupt:
@@ -321,12 +297,12 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r
     elif Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().startswith("SELECT ") and " FROM " not in expression.upper():
         expression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]
 
-    outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, resumeValue=resumeValue, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump)
+    outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump)
     returnValue = ", ".join(output for output in outputs)
 
     return returnValue
 
-def __goBooleanProxy(expression, resumeValue=True):
+def __goBooleanProxy(expression):
     """
     Retrieve the output of a boolean based SQL query
     """
@@ -340,54 +316,37 @@ def __goBooleanProxy(expression, resumeValue=True):
     payload = agent.payload(newValue=query)
     timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)
 
-    if resumeValue:
-        output = resume(expression, payload)
-    else:
-        output = None
+    output = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries, not kb.resumeValues]) else None
 
     if not output:
         output = Request.queryPage(payload, timeBasedCompare=timeBasedCompare, raise404=False)
 
+    if output is not None:
+        conf.hashDB.write(expression, output)
+
     return output
 
-def __goError(expression, expected=None, resumeValue=True, dump=False):
+def __goError(expression, expected=None, dump=False):
     """
     Retrieve the output of a SQL query taking advantage of an error-based
     SQL injection vulnerability on the affected parameter.
     """
 
-    output = None
-
-    if resumeValue:
-        output = resume(expression, None)
-
-        if output and expected == EXPECTED.INT and not output.isdigit():
-            output = None
-
-    if output is None:
-        output = errorUse(expression, expected, resumeValue, dump)
+    output = errorUse(expression, expected, dump)
 
     return output
 
-def __goInband(expression, expected=None, unique=True, resumeValue=True, unpack=True, dump=False):
+def __goInband(expression, expected=None, unique=True, unpack=True, dump=False):
     """
     Retrieve the output of a SQL query taking advantage of an inband SQL
     injection vulnerability on the affected parameter.
     """
 
-    output = None
-    partial = False
-    data = None
+    output = unionUse(expression, unpack=unpack, dump=dump)
+    if isinstance(output, basestring):
+        output = parseUnionPage(output, unique)
 
-    if output is None:
-        output = unionUse(expression, unpack=unpack, dump=dump)
-
-    if isinstance(output, list):
-        data = output
-    else:
-        data = parseUnionPage(output, unique)
-
-    return data
+    return output
 
 def getValue(expression, blind=True, inband=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, unique=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True):
     """
@@ -398,6 +357,7 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
     """
 
     kb.safeCharEncode = safeCharEncode
+    kb.resumeValues = resumeValue
 
     if suppressOutput is not None:
         pushValue(getCurrentThreadData().disableStdOut)
@@ -433,9 +393,9 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
                 kb.technique = PAYLOAD.TECHNIQUE.UNION
 
                 if expected == EXPECTED.BOOL:
-                    value = __goInband(forgeCaseExpression, expected, unique, resumeValue, unpack, dump)
+                    value = __goInband(forgeCaseExpression, expected, unique, unpack, dump)
                 else:
-                    value = __goInband(query, expected, unique, resumeValue, unpack, dump)
+                    value = __goInband(query, expected, unique, unpack, dump)
 
                 count += 1
                 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
@@ -444,9 +404,9 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
                 kb.technique = PAYLOAD.TECHNIQUE.ERROR
 
                 if expected == EXPECTED.BOOL:
-                    value = __goError(forgeCaseExpression, expected, resumeValue, dump)
+                    value = __goError(forgeCaseExpression, expected, dump)
                 else:
-                    value = __goError(query, expected, resumeValue, dump)
+                    value = __goError(query, expected, dump)
 
                 count += 1
                 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
@@ -455,9 +415,9 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
                 kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN
 
                 if expected == EXPECTED.BOOL:
-                    value = __goBooleanProxy(booleanExpression, resumeValue)
+                    value = __goBooleanProxy(booleanExpression)
                 else:
-                    value = __goInferenceProxy(query, fromUser, expected, batch, resumeValue, unpack, charsetType, firstChar, lastChar, dump)
+                    value = __goInferenceProxy(query, fromUser, expected, batch, unpack, charsetType, firstChar, lastChar, dump)
 
                 count += 1
                 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
@@ -469,9 +429,9 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
                     kb.technique = PAYLOAD.TECHNIQUE.STACKED
 
                 if expected == EXPECTED.BOOL:
-                    value = __goBooleanProxy(booleanExpression, resumeValue)
+                    value = __goBooleanProxy(booleanExpression)
                 else:
-                    value = __goInferenceProxy(query, fromUser, expected, batch, resumeValue, unpack, charsetType, firstChar, lastChar, dump)
+                    value = __goInferenceProxy(query, fromUser, expected, batch, unpack, charsetType, firstChar, lastChar, dump)
 
             if value and isinstance(value, basestring):
                 value = value.strip()
@@ -481,28 +441,13 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
             raise sqlmapNotVulnerableException, errMsg
 
     finally:
+        kb.resumeValues = True
         if suppressOutput is not None:
             getCurrentThreadData().disableStdOut = popValue()
 
-    if value and expected == EXPECTED.BOOL:
-        if isinstance(value, basestring):
-            value = value.strip().lower()
-            if value in ("true", "false"):
-                value = value == "true"
-            elif value in ("1", "-1"):
-                value = True
-            elif value == "0":
-                value = False
-            else:
-                value = None
-        elif isinstance(value, int):
-            value = bool(value)
-        elif value == [None]:
-            value = None
-
     kb.safeCharEncode = False
 
-    return value
+    return extractExpectedValue(value, expected)
 
 def goStacked(expression, silent=False):
     kb.technique = PAYLOAD.TECHNIQUE.STACKED
diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py
index 38d5e5266..0ed91a94e 100644
--- a/lib/techniques/blind/inference.py
+++ b/lib/techniques/blind/inference.py
@@ -13,7 +13,6 @@ import traceback
 
 from lib.core.agent import agent
 from lib.core.common import Backend
-from lib.core.common import dataToSessionFile
 from lib.core.common import dataToStdout
 from lib.core.common import decodeIntToUnicode
 from lib.core.common import filterControlChars
@@ -46,6 +45,7 @@ from lib.core.settings import INFERENCE_GREATER_CHAR
 from lib.core.settings import INFERENCE_EQUALS_CHAR
 from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR
 from lib.core.settings import MAX_TIME_REVALIDATION_STEPS
+from lib.core.settings import PARTIAL_VALUE_MARKER
 from lib.core.settings import PYVERSION
 from lib.core.threads import getCurrentThreadData
 from lib.core.threads import runThreads
@@ -58,466 +58,459 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
     on an affected host
     """
 
-    retVal = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries]) else None
-
-    if retVal:
-        return 0, retVal
-
-    partialValue = ""
-    finalValue = ""
+    partialValue = u""
+    finalValue = None
+    abortedFlag = False
     asciiTbl = getCharset(charsetType)
     timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED))
+    retVal = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries, not kb.resumeValues]) else None
 
-    # Set kb.partRun in case "common prediction" feature (a.k.a. "good
-    # samaritan") is used
-    kb.partRun = getPartRun() if conf.predictOutput else None
-
-    if "LENGTH(" in expression or "LEN(" in expression:
-        firstChar = 0
-    elif dump and conf.firstChar is not None and ( isinstance(conf.firstChar, int) or ( isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit() ) ):
-        firstChar = int(conf.firstChar) - 1
-    elif firstChar is None:
-        firstChar = 0
-    elif ( isinstance(firstChar, basestring) and firstChar.isdigit() ) or isinstance(firstChar, int):
-        firstChar = int(firstChar) - 1
-
-    if "LENGTH(" in expression or "LEN(" in expression:
-        lastChar = 0
-    elif dump and conf.lastChar is not None and ( isinstance(conf.lastChar, int) or ( isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit() ) ):
-        lastChar = int(conf.lastChar)
-    elif lastChar in ( None, "0" ):
-        lastChar = 0
-    elif ( isinstance(lastChar, basestring) and lastChar.isdigit() ) or isinstance(lastChar, int):
-        lastChar = int(lastChar)
-
-    if Backend.getDbms():
-        _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression)
-        nulledCastedField = agent.nullAndCastField(fieldToCastStr)
-        expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)
-        expressionUnescaped = unescaper.unescape(expressionReplaced)
-    else:
-        expressionUnescaped = unescaper.unescape(expression)
-
-    if length and not isinstance(length, int) and length.isdigit():
-        length = int(length)
-
-    if length == 0:
-        return 0, ""
-
-    if lastChar > 0 and length > ( lastChar - firstChar ):
-        length = ( lastChar - firstChar )
-
-    showEta = conf.eta and isinstance(length, int)
-    numThreads = min(conf.threads, length)
-
-    if showEta:
-        progress = ProgressBar(maxValue=length)
-        progressTime = []
-
-    if timeBasedCompare and conf.threads > 1:
-        warnMsg = "multi-threading is considered unsafe in time-based data retrieval. Going to switch it off automatically"
-        singleTimeWarnMessage(warnMsg)
-
-    if numThreads > 1:
-        if not timeBasedCompare:
-            debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else ""))
-            logger.debug(debugMsg)
+    if retVal:
+        if PARTIAL_VALUE_MARKER in retVal:
+            partialValue = retVal.replace(PARTIAL_VALUE_MARKER, "")
+            dataToStdout("[%s] [INFO] resuming partial value: '%s'\r\n" % (time.strftime("%X"), safecharencode(partialValue)))
         else:
-            numThreads = 1
+            dataToStdout("[%s] [INFO] resumed: %s\r\n" % (time.strftime("%X"), safecharencode(retVal)))
+            return 0, retVal
 
-    if conf.threads == 1 and not timeBasedCompare:
-        warnMsg = "running in a single-thread mode. Please consider "
-        warnMsg += "usage of option '--threads' for faster data retrieval"
-        singleTimeWarnMessage(warnMsg)
+    try:
+        # Set kb.partRun in case "common prediction" feature (a.k.a. "good
+        # samaritan") is used
+        kb.partRun = getPartRun() if conf.predictOutput else None
 
-    if conf.verbose in (1, 2) and not showEta:
-        if isinstance(length, int) and conf.threads > 1:
-            dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth)))
-            dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))
+        if partialValue:
+            firstChar = len(partialValue)
+        elif "LENGTH(" in expression or "LEN(" in expression:
+            firstChar = 0
+        elif dump and conf.firstChar is not None and ( isinstance(conf.firstChar, int) or ( isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit() ) ):
+            firstChar = int(conf.firstChar) - 1
+        elif firstChar is None:
+            firstChar = 0
+        elif ( isinstance(firstChar, basestring) and firstChar.isdigit() ) or isinstance(firstChar, int):
+            firstChar = int(firstChar) - 1
+
+        if "LENGTH(" in expression or "LEN(" in expression:
+            lastChar = 0
+        elif dump and conf.lastChar is not None and ( isinstance(conf.lastChar, int) or ( isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit() ) ):
+            lastChar = int(conf.lastChar)
+        elif lastChar in ( None, "0" ):
+            lastChar = 0
+        elif ( isinstance(lastChar, basestring) and lastChar.isdigit() ) or isinstance(lastChar, int):
+            lastChar = int(lastChar)
+
+        if Backend.getDbms():
+            _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression)
+            nulledCastedField = agent.nullAndCastField(fieldToCastStr)
+            expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)
+            expressionUnescaped = unescaper.unescape(expressionReplaced)
         else:
-            dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X"))
+            expressionUnescaped = unescaper.unescape(expression)
 
-    hintlock = threading.Lock()
+        if length and not isinstance(length, int) and length.isdigit():
+            length = int(length)
 
-    def tryHint(idx):
-        hintlock.acquire()
-        hintValue = kb.hintValue
-        hintlock.release()
+        if length == 0:
+            return 0, ""
 
-        if hintValue is not None and len(hintValue) >= idx:
-            if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2):
-                posValue = hintValue[idx-1]
+        if lastChar > 0 and length > ( lastChar - firstChar ):
+            length = ( lastChar - firstChar )
+
+        showEta = conf.eta and isinstance(length, int)
+        numThreads = min(conf.threads, length)
+
+        if showEta:
+            progress = ProgressBar(maxValue=length)
+            progressTime = []
+
+        if timeBasedCompare and conf.threads > 1:
+            warnMsg = "multi-threading is considered unsafe in time-based data retrieval. Going to switch it off automatically"
+            singleTimeWarnMessage(warnMsg)
+
+        if numThreads > 1:
+            if not timeBasedCompare:
+                debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else ""))
+                logger.debug(debugMsg)
             else:
-                posValue = ord(hintValue[idx-1])
+                numThreads = 1
 
-            forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue))
-            result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
-            incrementCounter(kb.technique)
+        if conf.threads == 1 and not timeBasedCompare:
+            warnMsg = "running in a single-thread mode. Please consider "
+            warnMsg += "usage of option '--threads' for faster data retrieval"
+            singleTimeWarnMessage(warnMsg)
 
-            if result:
-                return hintValue[idx-1]
-
-        hintlock.acquire()
-        kb.hintValue = None
-        hintlock.release()
-
-        return None
-
-    def validateChar(idx, value):
-        """
-        Used in time-based inference (in case that original and retrieved
-        value are not equal there will be a deliberate delay).
-        """
-
-        forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_NOT_EQUALS_CHAR), (expressionUnescaped, idx, value))
-        incrementCounter(kb.technique)
-        result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
-
-        return not result
-
-    def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is None, shiftTable=None):
-        """
-        continuousOrder means that distance between each two neighbour's
-        numerical values is exactly 1
-        """
-
-        result = tryHint(idx)
-
-        if result:
-            return result
-
-        originalTbl = list(charTbl)
-
-        if continuousOrder and shiftTable is None:
-            # Used for gradual expanding into unicode charspace
-            shiftTable = [5, 4]
-
-        if CHAR_INFERENCE_MARK in payload and ord('\n') in charTbl:
-            charTbl.remove(ord('\n'))
-
-        if len(charTbl) == 1:
-            forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0]))
-            result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
-            incrementCounter(kb.technique)
-
-            if result:
-                return decodeIntToUnicode(charTbl[0])
+        if conf.verbose in (1, 2) and not showEta:
+            if isinstance(length, int) and conf.threads > 1:
+                dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth)))
+                dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))
             else:
-                return None
+                dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X"))
 
-        maxChar = maxValue = charTbl[-1]
-        minChar = minValue = charTbl[0]
+        hintlock = threading.Lock()
 
-        while len(charTbl) != 1:
-            position = (len(charTbl) >> 1)
-            posValue = charTbl[position]
+        def tryHint(idx):
+            hintlock.acquire()
+            hintValue = kb.hintValue
+            hintlock.release()
 
-            if CHAR_INFERENCE_MARK not in payload:
-                forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue))
-            else:
-                # e.g.: ... > '%c' -> ... > ORD(..)
-                markingValue = "'%s'" % CHAR_INFERENCE_MARK
-                unescapedCharValue = unescaper.unescape("'%s'" % decodeIntToUnicode(posValue))
-                forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue)
-
-            result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
-            incrementCounter(kb.technique)
-
-            if result:
-                minValue = posValue
-
-                if type(charTbl) != xrange:
-                    charTbl = charTbl[position:]
+            if hintValue is not None and len(hintValue) >= idx:
+                if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2):
+                    posValue = hintValue[idx-1]
                 else:
-                    # xrange() - extended virtual charset used for memory/space optimization
-                    charTbl = xrange(charTbl[position], charTbl[-1] + 1)
-            else:
-                maxValue = posValue
+                    posValue = ord(hintValue[idx-1])
 
-                if type(charTbl) != xrange:
-                    charTbl = charTbl[:position]
-                else:
-                    charTbl = xrange(charTbl[0], charTbl[position])
+                forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue))
+                result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
+                incrementCounter(kb.technique)
+
+                if result:
+                    return hintValue[idx-1]
+
+            hintlock.acquire()
+            kb.hintValue = None
+            hintlock.release()
+
+            return None
+
+        def validateChar(idx, value):
+            """
+            Used in time-based inference (in case that original and retrieved
+            value are not equal there will be a deliberate delay).
+            """
+
+            forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_NOT_EQUALS_CHAR), (expressionUnescaped, idx, value))
+            result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
+            incrementCounter(kb.technique)
+
+            return not result
+
+        def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is None, shiftTable=None):
+            """
+            continuousOrder means that distance between each two neighbour's
+            numerical values is exactly 1
+            """
+
+            result = tryHint(idx)
+
+            if result:
+                return result
+
+            originalTbl = list(charTbl)
+
+            if continuousOrder and shiftTable is None:
+                # Used for gradual expanding into unicode charspace
+                shiftTable = [5, 4]
+
+            if CHAR_INFERENCE_MARK in payload and ord('\n') in charTbl:
+                charTbl.remove(ord('\n'))
 
             if len(charTbl) == 1:
-                if continuousOrder:
-                    if maxValue == 1:
-                        return None
+                forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0]))
+                result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
+                incrementCounter(kb.technique)
 
-                    # Going beyond the original charset
-                    elif minValue == maxChar:
-                        # If the original charTbl was [0,..,127] new one
-                        # will be [128,..,128*16-1] or from 128 to 2047
-                        # and instead of making a HUGE list with all the
-                        # elements we use a xrange, which is a virtual
-                        # list
-                        if expand and shiftTable:
-                            charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop())
-                            originalTbl = list(charTbl)
-                            maxChar = maxValue = charTbl[-1]
-                            minChar = minValue = charTbl[0]
-                        else:
-                            return None
-                    else:
-                        retVal = minValue + 1
-
-                        if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload):
-                            if timeBasedCompare and not validateChar(idx, retVal):
-                                if not kb.originalTimeDelay:
-                                    kb.originalTimeDelay = conf.timeSec
-
-                                if (conf.timeSec - kb.originalTimeDelay) < MAX_TIME_REVALIDATION_STEPS:
-                                    errMsg = "invalid character detected. retrying.."
-                                    logger.error(errMsg)
-
-                                    warnMsg = "increasing time delay to %d second%s " % (conf.timeSec, 's' if conf.timeSec > 1 else '')
-                                    logger.warn(warnMsg)
-
-                                    conf.timeSec += 1
-
-                                    if kb.adjustTimeDelay:
-                                        dbgMsg = "turning off auto-adjustment mechanism"
-                                        logger.debug(dbgMsg)
-                                        kb.adjustTimeDelay = False
-                                    return getChar(idx, originalTbl, continuousOrder, expand, shiftTable)
-                                else:
-                                    errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal)
-                                    logger.error(errMsg)
-                                    conf.timeSec = kb.originalTimeDelay
-                                    return decodeIntToUnicode(retVal)
-                            else:
-                                return decodeIntToUnicode(retVal)
-                        else:
-                            return None
+                if result:
+                    return decodeIntToUnicode(charTbl[0])
                 else:
-                    if minValue == maxChar or maxValue == minChar:
-                        return None
-
-                    # If we are working with non-continuous elements, set
-                    # both minValue and character afterwards are possible
-                    # candidates
-                    for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]):
-                        forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, retVal))
-                        result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
-                        incrementCounter(kb.technique)
-
-                        if result:
-                            return decodeIntToUnicode(retVal)
-
                     return None
 
-    def etaProgressUpdate(charTime, index):
-        if len(progressTime) <= ( (length * 3) / 100 ):
-            eta = 0
-        else:
-            midTime = sum(progressTime) / len(progressTime)
-            midTimeWithLatest = (midTime + charTime) / 2
-            eta = midTimeWithLatest * (length - index) / conf.threads
+            maxChar = maxValue = charTbl[-1]
+            minChar = minValue = charTbl[0]
 
-        progressTime.append(charTime)
-        progress.update(index)
-        progress.draw(eta)
+            while len(charTbl) != 1:
+                position = (len(charTbl) >> 1)
+                posValue = charTbl[position]
 
-    # Go multi-threading (--threads > 1)
-    if conf.threads > 1 and isinstance(length, int) and length > 1:
-        value = []
-        threadData = getCurrentThreadData()
+                if CHAR_INFERENCE_MARK not in payload:
+                    forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue))
+                else:
+                    # e.g.: ... > '%c' -> ... > ORD(..)
+                    markingValue = "'%s'" % CHAR_INFERENCE_MARK
+                    unescapedCharValue = unescaper.unescape("'%s'" % decodeIntToUnicode(posValue))
+                    forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue)
 
-        threadData.shared.value = [ None ] * length
-        threadData.shared.index = [ firstChar ]    # As list for python nested function scoping
+                result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
+                incrementCounter(kb.technique)
 
-        try:
-            def blindThread():
-                threadData = getCurrentThreadData()
+                if result:
+                    minValue = posValue
 
-                while kb.threadContinue:
-                    kb.locks.index.acquire()
+                    if type(charTbl) != xrange:
+                        charTbl = charTbl[position:]
+                    else:
+                        # xrange() - extended virtual charset used for memory/space optimization
+                        charTbl = xrange(charTbl[position], charTbl[-1] + 1)
+                else:
+                    maxValue = posValue
 
-                    if threadData.shared.index[0] >= length:
+                    if type(charTbl) != xrange:
+                        charTbl = charTbl[:position]
+                    else:
+                        charTbl = xrange(charTbl[0], charTbl[position])
+
+                if len(charTbl) == 1:
+                    if continuousOrder:
+                        if maxValue == 1:
+                            return None
+
+                        # Going beyond the original charset
+                        elif minValue == maxChar:
+                            # If the original charTbl was [0,..,127] new one
+                            # will be [128,..,128*16-1] or from 128 to 2047
+                            # and instead of making a HUGE list with all the
+                            # elements we use a xrange, which is a virtual
+                            # list
+                            if expand and shiftTable:
+                                charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop())
+                                originalTbl = list(charTbl)
+                                maxChar = maxValue = charTbl[-1]
+                                minChar = minValue = charTbl[0]
+                            else:
+                                return None
+                        else:
+                            retVal = minValue + 1
+
+                            if retVal in originalTbl or (retVal == ord('\n') and CHAR_INFERENCE_MARK in payload):
+                                if timeBasedCompare and not validateChar(idx, retVal):
+                                    if not kb.originalTimeDelay:
+                                        kb.originalTimeDelay = conf.timeSec
+
+                                    if (conf.timeSec - kb.originalTimeDelay) < MAX_TIME_REVALIDATION_STEPS:
+                                        errMsg = "invalid character detected. retrying.."
+                                        logger.error(errMsg)
+
+                                        warnMsg = "increasing time delay to %d second%s " % (conf.timeSec, 's' if conf.timeSec > 1 else '')
+                                        logger.warn(warnMsg)
+
+                                        conf.timeSec += 1
+
+                                        if kb.adjustTimeDelay:
+                                            dbgMsg = "turning off auto-adjustment mechanism"
+                                            logger.debug(dbgMsg)
+                                            kb.adjustTimeDelay = False
+                                        return getChar(idx, originalTbl, continuousOrder, expand, shiftTable)
+                                    else:
+                                        errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal)
+                                        logger.error(errMsg)
+                                        conf.timeSec = kb.originalTimeDelay
+                                        return decodeIntToUnicode(retVal)
+                                else:
+                                    return decodeIntToUnicode(retVal)
+                            else:
+                                return None
+                    else:
+                        if minValue == maxChar or maxValue == minChar:
+                            return None
+
+                        # If we are working with non-continuous elements, set
+                        # both minValue and character afterwards are possible
+                        # candidates
+                        for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]):
+                            forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, retVal))
+                            result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False)
+                            incrementCounter(kb.technique)
+
+                            if result:
+                                return decodeIntToUnicode(retVal)
+
+                        return None
+
+        def etaProgressUpdate(charTime, index):
+            if len(progressTime) <= ( (length * 3) / 100 ):
+                eta = 0
+            else:
+                midTime = sum(progressTime) / len(progressTime)
+                midTimeWithLatest = (midTime + charTime) / 2
+                eta = midTimeWithLatest * (length - index) / conf.threads
+
+            progressTime.append(charTime)
+            progress.update(index)
+            progress.draw(eta)
+
+        # Go multi-threading (--threads > 1)
+        if conf.threads > 1 and isinstance(length, int) and length > 1:
+            value = []
+            threadData = getCurrentThreadData()
+
+            threadData.shared.value = [ None ] * length
+            threadData.shared.index = [ firstChar ]    # As list for python nested function scoping
+            threadData.shared.start = firstChar
+
+            try:
+                def blindThread():
+                    threadData = getCurrentThreadData()
+
+                    while kb.threadContinue:
+                        kb.locks.index.acquire()
+
+                        if threadData.shared.index[0] >= length:
+                            kb.locks.index.release()
+
+                            return
+
+                        threadData.shared.index[0] += 1
+                        curidx = threadData.shared.index[0]
                         kb.locks.index.release()
 
-                        return
+                        if kb.threadContinue:
+                            charStart = time.time()
+                            val = getChar(curidx)
+                            if val is None:
+                                val = INFERENCE_UNKNOWN_CHAR
+                        else:
+                            break
 
-                    threadData.shared.index[0] += 1
-                    curidx = threadData.shared.index[0]
-                    kb.locks.index.release()
+                        kb.locks.value.acquire()
+                        threadData.shared.value[curidx - 1] = val
+                        currentValue = list(threadData.shared.value)
+                        kb.locks.value.release()
 
-                    if kb.threadContinue:
-                        charStart = time.time()
-                        val = getChar(curidx)
-                        if val is None:
-                            val = INFERENCE_UNKNOWN_CHAR
-                    else:
-                        break
+                        if kb.threadContinue:
+                            if showEta:
+                                etaProgressUpdate(time.time() - charStart, threadData.shared.index[0])
+                            elif conf.verbose >= 1:
+                                startCharIndex = 0
+                                endCharIndex = 0
 
-                    kb.locks.value.acquire()
-                    threadData.shared.value[curidx-1] = val
-                    currentValue = list(threadData.shared.value)
-                    kb.locks.value.release()
+                                for i in xrange(length):
+                                    if currentValue[i] is not None:
+                                        endCharIndex = max(endCharIndex, i)
 
-                    if kb.threadContinue:
-                        if showEta:
-                            etaProgressUpdate(time.time() - charStart, threadData.shared.index[0])
-                        elif conf.verbose >= 1:
-                            startCharIndex = 0
-                            endCharIndex = 0
+                                output = ''
 
-                            for i in xrange(length):
-                                if currentValue[i] is not None:
-                                    endCharIndex = max(endCharIndex, i)
+                                if endCharIndex > conf.progressWidth:
+                                    startCharIndex = endCharIndex - conf.progressWidth
 
-                            output = ''
+                                count = threadData.shared.start
 
-                            if endCharIndex > conf.progressWidth:
-                                startCharIndex = endCharIndex - conf.progressWidth
+                                for i in xrange(startCharIndex, endCharIndex + 1):
+                                    output += '_' if currentValue[i] is None else currentValue[i]
 
-                            count = 0
+                                for i in xrange(length):
+                                    count += 1 if currentValue[i] is not None else 0
 
-                            for i in xrange(startCharIndex, endCharIndex + 1):
-                                output += '_' if currentValue[i] is None else currentValue[i]
+                                if startCharIndex > 0:
+                                    output = '..' + output[2:]
 
-                            for i in xrange(length):
-                                count += 1 if currentValue[i] is not None else 0
+                                if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length-1):
+                                    output = output[:-2] + '..'
 
-                            if startCharIndex > 0:
-                                output = '..' + output[2:]
+                                if conf.verbose in (1, 2) and not showEta:
+                                    output += '_' * (min(length, conf.progressWidth) - len(output))
+                                    status = ' %d/%d (%d%s)' % (count, length, round(100.0*count/length), '%')
+                                    output += status if count != length else " "*len(status)
 
-                            if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length-1):
-                                output = output[:-2] + '..'
+                                    dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output)))
 
-                            if conf.verbose in (1, 2) and not showEta:
-                                output += '_' * (min(length, conf.progressWidth) - len(output))
-                                status = ' %d/%d (%d%s)' % (count, length, round(100.0*count/length), '%')
-                                output += status if count != length else " "*len(status)
+                runThreads(numThreads, blindThread, startThreadMsg=False)
 
-                                dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output)))
+            except KeyboardInterrupt:
+                abortedFlag = True
 
-                if not kb.threadContinue:
-                    if int(threading.currentThread().getName()) == numThreads - 1:
-                        partialValue = unicode()
-                        for v in threadData.shared.value:
-                            if v is None:
-                                break
-                            elif isinstance(v, basestring):
-                                partialValue += v
+            finally:
+                value = map(lambda _: partialValue[_] if _ < len(partialValue) else threadData.shared.value[_], xrange(length))
 
-                        if len(partialValue) > 0:
-                            dataToSessionFile(replaceNewlineTabs(partialValue))
+            infoMsg = None
 
-            runThreads(numThreads, blindThread, startThreadMsg=False)
+            # If we have got one single character not correctly fetched it
+            # can mean that the connection to the target url was lost
+            if None in value:
+                partialValue = "".join(_ for _ in value[:value.index(None)])
 
-        except KeyboardInterrupt:
-            raise
-
-        finally:
-            value = threadData.shared.value
-
-        infoMsg = None
-
-        # If we have got one single character not correctly fetched it
-        # can mean that the connection to the target url was lost
-        if None in value:
-            for v in value:
-                if isinstance(v, basestring) and v is not None:
-                    partialValue += v
-
-            if partialValue:
-                finalValue = partialValue
-                infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue))
-        else:
-            finalValue = "".join(value)
-            infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue))
-
-        if isinstance(finalValue, basestring) and len(finalValue) > 0:
-            dataToSessionFile(replaceNewlineTabs(finalValue))
-
-        if conf.verbose in (1, 2) and not showEta and infoMsg:
-            dataToStdout(infoMsg)
-
-    # No multi-threading (--threads = 1)
-    else:
-        index = firstChar
-
-        while True:
-            index += 1
-            charStart = time.time()
-
-            # Common prediction feature (a.k.a. "good samaritan")
-            # NOTE: to be used only when multi-threading is not set for
-            # the moment
-            if conf.predictOutput and len(finalValue) > 0 and kb.partRun is not None:
-                val = None
-                commonValue, commonPattern, commonCharset, otherCharset = goGoodSamaritan(finalValue, asciiTbl)
-
-                # If there is one single output in common-outputs, check
-                # it via equal against the query output
-                if commonValue is not None:
-                    # One-shot query containing equals commonValue
-                    testValue = unescaper.unescape("'%s'" % commonValue) if "'" not in commonValue else unescaper.unescape("%s" % commonValue, quote=False)
-                    query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (expressionUnescaped, testValue)))
-                    query = agent.suffixQuery(query)
-                    result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)
-                    incrementCounter(kb.technique)
-
-                    # Did we have luck?
-                    if result:
-                        dataToSessionFile(replaceNewlineTabs(commonValue[index-1:]))
-
-                        if showEta:
-                            etaProgressUpdate(time.time() - charStart, len(commonValue))
-                        elif conf.verbose in (1, 2):
-                            dataToStdout(commonValue[index-1:])
-
-                        finalValue = commonValue
-
-                        break
-
-                # If there is a common pattern starting with finalValue,
-                # check it via equal against the substring-query output
-                if commonPattern is not None:
-                    # Substring-query containing equals commonPattern
-                    subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern))
-                    testValue = unescaper.unescape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.unescape("%s" % commonPattern, quote=False)
-                    query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (subquery, testValue)))
-                    query = agent.suffixQuery(query)
-                    result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)
-                    incrementCounter(kb.technique)
-
-                    # Did we have luck?
-                    if result:
-                        val = commonPattern[index-1:]
-                        index += len(val)-1
-
-                # Otherwise if there is no commonValue (single match from
-                # txt/common-outputs.txt) and no commonPattern
-                # (common pattern) use the returned common charset only
-                # to retrieve the query output
-                if not val and commonCharset:
-                    val = getChar(index, commonCharset, False)
-
-                # If we had no luck with commonValue and common charset,
-                # use the returned other charset
-                if not val:
-                    val = getChar(index, otherCharset, otherCharset == asciiTbl)
+                if partialValue:
+                    infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), filterControlChars(partialValue))
             else:
-                val = getChar(index, asciiTbl)
+                finalValue = "".join(value)
+                infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue))
 
-            if val is None or ( lastChar > 0 and index > lastChar ):
-                break
+            if conf.verbose in (1, 2) and not showEta and infoMsg:
+                dataToStdout(infoMsg)
 
-            if kb.data.processChar:
-                val = kb.data.processChar(val)
+        # No multi-threading (--threads = 1)
+        else:
+            index = firstChar
 
-            finalValue += val
-            dataToSessionFile(replaceNewlineTabs(val))
+            while True:
+                index += 1
+                charStart = time.time()
 
-            if showEta:
-                etaProgressUpdate(time.time() - charStart, index)
-            elif conf.verbose in (1, 2):
-                dataToStdout(val)
+                # Common prediction feature (a.k.a. "good samaritan")
+                # NOTE: to be used only when multi-threading is not set for
+                # the moment
+                if conf.predictOutput and len(partialValue) > 0 and kb.partRun is not None:
+                    val = None
+                    commonValue, commonPattern, commonCharset, otherCharset = goGoodSamaritan(partialValue, asciiTbl)
 
-            if len(finalValue) > INFERENCE_BLANK_BREAK and finalValue[-INFERENCE_BLANK_BREAK:].isspace():
-                break
+                    # If there is one single output in common-outputs, check
+                    # it via equal against the query output
+                    if commonValue is not None:
+                        # One-shot query containing equals commonValue
+                        testValue = unescaper.unescape("'%s'" % commonValue) if "'" not in commonValue else unescaper.unescape("%s" % commonValue, quote=False)
+                        query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (expressionUnescaped, testValue)))
+                        query = agent.suffixQuery(query)
+                        result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)
+                        incrementCounter(kb.technique)
+
+                        # Did we have luck?
+                        if result:
+                            if showEta:
+                                etaProgressUpdate(time.time() - charStart, len(commonValue))
+                            elif conf.verbose in (1, 2):
+                                dataToStdout(commonValue[index-1:])
+
+                            finalValue = commonValue
+
+                            break
+
+                    # If there is a common pattern starting with partialValue,
+                    # check it via equal against the substring-query output
+                    if commonPattern is not None:
+                        # Substring-query containing equals commonPattern
+                        subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern))
+                        testValue = unescaper.unescape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.unescape("%s" % commonPattern, quote=False)
+                        query = agent.prefixQuery(safeStringFormat("AND (%s) = %s", (subquery, testValue)))
+                        query = agent.suffixQuery(query)
+                        result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False)
+                        incrementCounter(kb.technique)
+
+                        # Did we have luck?
+                        if result:
+                            val = commonPattern[index-1:]
+                            index += len(val)-1
+
+                    # Otherwise if there is no commonValue (single match from
+                    # txt/common-outputs.txt) and no commonPattern
+                    # (common pattern) use the returned common charset only
+                    # to retrieve the query output
+                    if not val and commonCharset:
+                        val = getChar(index, commonCharset, False)
+
+                    # If we had no luck with commonValue and common charset,
+                    # use the returned other charset
+                    if not val:
+                        val = getChar(index, otherCharset, otherCharset == asciiTbl)
+                else:
+                    val = getChar(index, asciiTbl)
+
+                if val is None or ( lastChar > 0 and index > lastChar ):
+                    finalValue = partialValue
+                    break
+
+                if kb.data.processChar:
+                    val = kb.data.processChar(val)
+
+                partialValue += val
+
+                if showEta:
+                    etaProgressUpdate(time.time() - charStart, index)
+                elif conf.verbose in (1, 2):
+                    dataToStdout(val)
+
+                if len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace():
+                    finalValue = partialValue
+                    break
+
+    except KeyboardInterrupt:
+        abortedFlag = True
 
     if conf.verbose in (1, 2) or showEta:
         dataToStdout("\n")
@@ -526,11 +519,16 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
         infoMsg = "retrieved: %s" % filterControlChars(finalValue)
         logger.info(infoMsg)
 
-    if not partialValue:
+    if finalValue is not None:
         conf.hashDB.write(expression, finalValue)
-        dataToSessionFile("]\n")
+    else:
+        conf.hashDB.write(expression, "%s%s" % (PARTIAL_VALUE_MARKER, partialValue))
 
     if kb.threadException:
         raise sqlmapThreadException, "something unexpected happened inside the threads"
 
-    return getCounter(kb.technique), safecharencode(finalValue) if kb.safeCharEncode else finalValue
+    if abortedFlag:
+        raise KeyboardInterrupt
+
+    _ = finalValue or partialValue
+    return getCounter(kb.technique), safecharencode(_) if kb.safeCharEncode else _
diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py
index 3013f6fef..ee32aa435 100644
--- a/lib/techniques/error/use.py
+++ b/lib/techniques/error/use.py
@@ -45,10 +45,9 @@ from lib.core.threads import getCurrentThreadData
 from lib.core.threads import runThreads
 from lib.core.unescaper import unescaper
 from lib.request.connect import Connect as Request
-from lib.utils.resume import resume
 
 def __oneShotErrorUse(expression, field):
-    retVal = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries]) else None
+    retVal = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries, not kb.resumeValues]) else None
 
     threadData = getCurrentThreadData()
     threadData.resumed = retVal is not None
@@ -137,7 +136,7 @@ def __oneShotErrorUse(expression, field):
 
     return safecharencode(retVal) if kb.safeCharEncode else retVal
 
-def __errorFields(expression, expressionFields, expressionFieldsList, expected=None, num=None, resumeValue=True):
+def __errorFields(expression, expressionFields, expressionFieldsList, expected=None, num=None):
     outputs = []
     origExpr = None
 
@@ -158,22 +157,13 @@ def __errorFields(expression, expressionFields, expressionFieldsList, expected=N
         else:
             expressionReplaced = expression.replace(expressionFields, field, 1)
 
-        if resumeValue:
-            output = resume(expressionReplaced, None)
+        output = __oneShotErrorUse(expressionReplaced, field)
 
-        if not output or (expected == EXPECTED.INT and not output.isdigit()):
-            if output:
-                warnMsg = "expected value type %s, resumed '%s', " % (expected, output)
-                warnMsg += "sqlmap is going to retrieve the value again"
-                logger.warn(warnMsg)
+        if not kb.threadContinue:
+            return None
 
-            output = __oneShotErrorUse(expressionReplaced, field)
-
-            if not kb.threadContinue:
-                return None
-
-            if output is not None:
-                dataToStdout("[%s] [INFO] %s: %s\r\n" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(output)))
+        if output is not None:
+            dataToStdout("[%s] [INFO] %s: %s\r\n" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(output)))
 
         if isinstance(num, int):
             expression = origExpr
@@ -194,7 +184,7 @@ def __errorReplaceChars(value):
 
     return retVal
 
-def errorUse(expression, expected=None, resumeValue=True, dump=False):
+def errorUse(expression, expected=None, dump=False):
     """
     Retrieve the output of a SQL query taking advantage of the error-based
     SQL injection vulnerability on the affected parameter.
@@ -207,18 +197,11 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False):
     start = time.time()
     startLimit = 0
     stopLimit = None
+    output = None
     outputs = []
     untilLimitChar = None
     untilOrderChar = None
 
-    if resumeValue:
-        output = resume(expression, None)
-    else:
-        output = None
-
-    if output and (expected is None or (expected == EXPECTED.INT and output.isdigit())):
-        return output
-
     _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression)
 
     # We have to check if the SQL query might return multiple entries
@@ -295,11 +278,8 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False):
             if " ORDER BY " in expression:
                 countedExpression = countedExpression[:countedExpression.index(" ORDER BY ")]
 
-            count = resume(countedExpression, None)
-
-            if not count or not count.isdigit():
-                _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields(countedExpression)
-                count = __oneShotErrorUse(countedExpression, countedExpressionFields)
+            _, _, _, _, _, _, countedExpressionFields, _ = agent.getFields(countedExpression)
+            count = __oneShotErrorUse(countedExpression, countedExpressionFields)
 
             if isNumPosStrValue(count):
                 if isinstance(stopLimit, int) and stopLimit > 0:
@@ -360,7 +340,7 @@ def errorUse(expression, expected=None, resumeValue=True, dump=False):
                         finally:
                             kb.locks.limits.release()
 
-                        output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num, resumeValue)
+                        output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num)
 
                         if not kb.threadContinue:
                             break
diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py
index d7f6e6beb..17364bfc3 100644
--- a/lib/techniques/union/use.py
+++ b/lib/techniques/union/use.py
@@ -43,10 +43,9 @@ from lib.core.threads import getCurrentThreadData
 from lib.core.threads import runThreads
 from lib.core.unescaper import unescaper
 from lib.request.connect import Connect as Request
-from lib.utils.resume import resume
 
 def __oneShotUnionUse(expression, unpack=True, limited=False):
-    retVal = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries]) else None
+    retVal = conf.hashDB.retrieve(expression) if not any([conf.flushSession, conf.freshQueries, not kb.resumeValues]) else None
 
     threadData = getCurrentThreadData()
     threadData.resumed = retVal is not None
@@ -233,14 +232,8 @@ def unionUse(expression, unpack=True, dump=False):
                 _ = countedExpression.upper().rindex(" ORDER BY ")
                 countedExpression = countedExpression[:_]
 
-            count = resume(countedExpression, None)
-            count = parseUnionPage(count)
-
-            if not count or not count.isdigit():
-                output = __oneShotUnionUse(countedExpression, unpack)
-
-                if output:
-                    count = parseUnionPage(output)
+            output = __oneShotUnionUse(countedExpression, unpack)
+            count = parseUnionPage(output)
 
             if isNumPosStrValue(count):
                 if isinstance(stopLimit, int) and stopLimit > 0:
@@ -301,10 +294,7 @@ def unionUse(expression, unpack=True, dump=False):
                             field = None
 
                         limitedExpr = agent.limitQuery(num, expression, field)
-                        output = resume(limitedExpr, None)
-
-                        if not output:
-                            output = __oneShotUnionUse(limitedExpr, unpack, True)
+                        output = __oneShotUnionUse(limitedExpr, unpack, True)
 
                         if not kb.threadContinue:
                             break
diff --git a/lib/utils/resume.py b/lib/utils/resume.py
index 787577a82..c8c6b934a 100644
--- a/lib/utils/resume.py
+++ b/lib/utils/resume.py
@@ -69,13 +69,6 @@ def queryOutputLength(expression, payload):
     infoMsg = "retrieving the length of query output"
     logger.info(infoMsg)
 
-    output = resume(lengthExpr, payload)
-
-    if output:
-        return 0, output, regExpr
-
-    dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], lengthExpr))
-
     start = time.time()
     lengthExprUnescaped = unescaper.unescape(lengthExpr)
     count, length = bisection(payload, lengthExprUnescaped, charsetType=2)