Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/core/threads.py
3556 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
from __future__ import print_function
9
10
import difflib
11
import sqlite3
12
import threading
13
import time
14
import traceback
15
16
from lib.core.compat import WichmannHill
17
from lib.core.compat import xrange
18
from lib.core.data import conf
19
from lib.core.data import kb
20
from lib.core.data import logger
21
from lib.core.datatype import AttribDict
22
from lib.core.enums import PAYLOAD
23
from lib.core.exception import SqlmapBaseException
24
from lib.core.exception import SqlmapConnectionException
25
from lib.core.exception import SqlmapSkipTargetException
26
from lib.core.exception import SqlmapThreadException
27
from lib.core.exception import SqlmapUserQuitException
28
from lib.core.exception import SqlmapValueException
29
from lib.core.settings import MAX_NUMBER_OF_THREADS
30
from lib.core.settings import PYVERSION
31
32
shared = AttribDict()
33
34
class _ThreadData(threading.local):
35
"""
36
Represents thread independent data
37
"""
38
39
def __init__(self):
40
self.reset()
41
42
def reset(self):
43
"""
44
Resets thread data model
45
"""
46
47
self.disableStdOut = False
48
self.hashDBCursor = None
49
self.inTransaction = False
50
self.lastCode = None
51
self.lastComparisonPage = None
52
self.lastComparisonHeaders = None
53
self.lastComparisonCode = None
54
self.lastComparisonRatio = None
55
self.lastPageTemplateCleaned = None
56
self.lastPageTemplate = None
57
self.lastErrorPage = tuple()
58
self.lastHTTPError = None
59
self.lastRedirectMsg = None
60
self.lastQueryDuration = 0
61
self.lastPage = None
62
self.lastRequestMsg = None
63
self.lastRequestUID = 0
64
self.lastRedirectURL = tuple()
65
self.random = WichmannHill()
66
self.resumed = False
67
self.retriesCount = 0
68
self.seqMatcher = difflib.SequenceMatcher(None)
69
self.shared = shared
70
self.technique = None
71
self.validationRun = 0
72
self.valueStack = []
73
74
ThreadData = _ThreadData()
75
76
def readInput(message, default=None, checkBatch=True, boolean=False):
77
# It will be overwritten by original from lib.core.common
78
pass
79
80
def isDigit(value):
81
# It will be overwritten by original from lib.core.common
82
pass
83
84
def getCurrentThreadData():
85
"""
86
Returns current thread's local data
87
"""
88
89
return ThreadData
90
91
def getCurrentThreadName():
92
"""
93
Returns current's thread name
94
"""
95
96
return threading.current_thread().getName()
97
98
def exceptionHandledFunction(threadFunction, silent=False):
99
try:
100
threadFunction()
101
except KeyboardInterrupt:
102
kb.threadContinue = False
103
kb.threadException = True
104
raise
105
except Exception as ex:
106
from lib.core.common import getSafeExString
107
108
if not silent and kb.get("threadContinue") and not kb.get("multipleCtrlC") and not isinstance(ex, (SqlmapUserQuitException, SqlmapSkipTargetException)):
109
errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex))
110
logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg))
111
112
if conf.get("verbose") > 1 and not isinstance(ex, SqlmapConnectionException):
113
traceback.print_exc()
114
115
def setDaemon(thread):
116
# Reference: http://stackoverflow.com/questions/190010/daemon-threads-explanation
117
if PYVERSION >= "2.6":
118
thread.daemon = True
119
else:
120
thread.setDaemon(True)
121
122
def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False, startThreadMsg=True):
123
threads = []
124
125
def _threadFunction():
126
try:
127
threadFunction()
128
finally:
129
if conf.hashDB:
130
conf.hashDB.close()
131
132
kb.multipleCtrlC = False
133
kb.threadContinue = True
134
kb.threadException = False
135
kb.technique = ThreadData.technique
136
kb.multiThreadMode = False
137
138
try:
139
if threadChoice and conf.threads == numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)):
140
while True:
141
message = "please enter number of threads? [Enter for %d (current)] " % numThreads
142
choice = readInput(message, default=str(numThreads))
143
if choice:
144
skipThreadCheck = False
145
146
if choice.endswith('!'):
147
choice = choice[:-1]
148
skipThreadCheck = True
149
150
if isDigit(choice):
151
if int(choice) > MAX_NUMBER_OF_THREADS and not skipThreadCheck:
152
errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS
153
logger.critical(errMsg)
154
else:
155
conf.threads = numThreads = int(choice)
156
break
157
158
if numThreads == 1:
159
warnMsg = "running in a single-thread mode. This could take a while"
160
logger.warning(warnMsg)
161
162
if numThreads > 1:
163
if startThreadMsg:
164
infoMsg = "starting %d threads" % numThreads
165
logger.info(infoMsg)
166
else:
167
try:
168
_threadFunction()
169
except (SqlmapUserQuitException, SqlmapSkipTargetException):
170
pass
171
return
172
173
kb.multiThreadMode = True
174
175
# Start the threads
176
for numThread in xrange(numThreads):
177
thread = threading.Thread(target=exceptionHandledFunction, name=str(numThread), args=[_threadFunction])
178
179
setDaemon(thread)
180
181
try:
182
thread.start()
183
except Exception as ex:
184
errMsg = "error occurred while starting new thread ('%s')" % ex
185
logger.critical(errMsg)
186
break
187
188
threads.append(thread)
189
190
# And wait for them to all finish
191
while True:
192
alive = False
193
for thread in threads:
194
if thread.is_alive():
195
alive = True
196
break
197
if not alive:
198
break
199
time.sleep(0.1)
200
201
except (KeyboardInterrupt, SqlmapUserQuitException) as ex:
202
print()
203
kb.prependFlag = False
204
kb.threadContinue = False
205
kb.threadException = True
206
207
if kb.lastCtrlCTime and (time.time() - kb.lastCtrlCTime < 1):
208
kb.multipleCtrlC = True
209
raise SqlmapUserQuitException("user aborted (Ctrl+C was pressed multiple times)")
210
211
kb.lastCtrlCTime = time.time()
212
213
if numThreads > 1:
214
logger.info("waiting for threads to finish%s" % (" (Ctrl+C was pressed)" if isinstance(ex, KeyboardInterrupt) else ""))
215
try:
216
while threading.active_count() > 1:
217
time.sleep(0.1)
218
219
except KeyboardInterrupt:
220
kb.multipleCtrlC = True
221
raise SqlmapThreadException("user aborted (Ctrl+C was pressed multiple times)")
222
223
if forwardException:
224
raise
225
226
except (SqlmapConnectionException, SqlmapValueException) as ex:
227
print()
228
kb.threadException = True
229
logger.error("thread %s: '%s'" % (threading.currentThread().getName(), ex))
230
231
if conf.get("verbose") > 1 and isinstance(ex, SqlmapValueException):
232
traceback.print_exc()
233
234
except Exception as ex:
235
print()
236
237
if not kb.multipleCtrlC:
238
if isinstance(ex, sqlite3.Error):
239
raise
240
else:
241
from lib.core.common import unhandledExceptionMessage
242
243
kb.threadException = True
244
errMsg = unhandledExceptionMessage()
245
logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg))
246
traceback.print_exc()
247
248
finally:
249
kb.multiThreadMode = False
250
kb.threadContinue = True
251
kb.threadException = False
252
kb.technique = None
253
254
for lock in kb.locks.values():
255
if lock.locked():
256
try:
257
lock.release()
258
except:
259
pass
260
261
if conf.get("hashDB"):
262
conf.hashDB.flush()
263
264
if cleanupFunction:
265
cleanupFunction()
266
267