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