#!/usr/bin/env python """ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ # 导入所需的标准库和自定义模块 import hashlib # 用于计算哈希值 import os # 用于文件和路径操作 import sqlite3 # SQLite数据库操作 import threading # 多线程支持 import time # 时间相关操作 # 导入自定义工具函数 from lib.core.common import getSafeExString # 安全地获取异常字符串 from lib.core.common import serializeObject # 对象序列化 from lib.core.common import singleTimeWarnMessage # 单次警告消息 from lib.core.common import unserializeObject # 对象反序列化 from lib.core.compat import xrange # 兼容Python2/3的range函数 from lib.core.convert import getBytes # 转换为字节 from lib.core.convert import getUnicode # 转换为Unicode from lib.core.data import logger # 日志记录器 from lib.core.exception import SqlmapConnectionException # 自定义连接异常 from lib.core.settings import HASHDB_END_TRANSACTION_RETRIES # 事务结束重试次数 from lib.core.settings import HASHDB_FLUSH_RETRIES # 刷新重试次数 from lib.core.settings import HASHDB_FLUSH_THRESHOLD # 刷新阈值 from lib.core.settings import HASHDB_RETRIEVE_RETRIES # 检索重试次数 from lib.core.threads import getCurrentThreadData # 获取当前线程数据 from lib.core.threads import getCurrentThreadName # 获取当前线程名称 from thirdparty import six # Python 2/3 兼容库 class HashDB(object): """ 哈希数据库类,用于管理SQLite数据库中的键值存储 """ def __init__(self, filepath): """ 初始化哈希数据库 @param filepath: 数据库文件路径 """ self.filepath = filepath # 数据库文件路径 self._write_cache = {} # 写入缓存字典 self._cache_lock = threading.Lock() # 缓存锁,用于线程同步 self._connections = [] # 数据库连接列表 def _get_cursor(self): """ 获取数据库游标 @return: SQLite游标对象 """ threadData = getCurrentThreadData() # 获取当前线程数据 # 如果当前线程没有游标,则创建新的连接和游标 if threadData.hashDBCursor is None: try: # 创建SQLite连接,禁用事务自动提交 connection = sqlite3.connect(self.filepath, timeout=3, isolation_level=None) self._connections.append(connection) threadData.hashDBCursor = connection.cursor() # 创建存储表(如果不存在) threadData.hashDBCursor.execute("CREATE TABLE IF NOT EXISTS storage (id INTEGER PRIMARY KEY, value TEXT)") connection.commit() except Exception as ex: errMsg = "error occurred while opening a session " errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) return threadData.hashDBCursor def _set_cursor(self, cursor): """ 设置数据库游标 @param cursor: SQLite游标对象 """ threadData = getCurrentThreadData() threadData.hashDBCursor = cursor # 游标属性 cursor = property(_get_cursor, _set_cursor) def close(self): """ 关闭当前线程的数据库连接 """ threadData = getCurrentThreadData() try: if threadData.hashDBCursor: threadData.hashDBCursor.connection.commit() threadData.hashDBCursor.close() threadData.hashDBCursor.connection.close() threadData.hashDBCursor = None except: pass def closeAll(self): """ 关闭所有数据库连接 """ for connection in self._connections: try: connection.commit() connection.close() except: pass @staticmethod def hashKey(key): """ 计算键的哈希值 @param key: 要哈希的键 @return: 64位整数哈希值 """ key = getBytes(key if isinstance(key, six.text_type) else repr(key), errors="xmlcharrefreplace") retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff # 确保返回64位正整数 return retVal def retrieve(self, key, unserialize=False): """ 从数据库检索值 @param key: 要检索的键 @param unserialize: 是否需要反序列化 @return: 检索到的值 """ retVal = None if key and (self._write_cache or os.path.isfile(self.filepath)): hash_ = HashDB.hashKey(key) # 首先检查缓存 retVal = self._write_cache.get(hash_) if not retVal: # 多次尝试从数据库检索 for _ in xrange(HASHDB_RETRIEVE_RETRIES): try: for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)): retVal = row[0] except (sqlite3.OperationalError, sqlite3.DatabaseError) as ex: if any(_ in getSafeExString(ex) for _ in ("locked", "no such table")): warnMsg = "problem occurred while accessing session file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) singleTimeWarnMessage(warnMsg) elif "Could not decode" in getSafeExString(ex): break else: errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex)) errMsg += "If the problem persists please rerun with '--flush-session'" raise SqlmapConnectionException(errMsg) else: break time.sleep(1) # 如果需要反序列化 if retVal and unserialize: try: retVal = unserializeObject(retVal) except: retVal = None warnMsg = "error occurred while unserializing value for session key '%s'. " % key warnMsg += "If the problem persists please rerun with '--flush-session'" logger.warning(warnMsg) return retVal def write(self, key, value, serialize=False): """ 写入值到数据库 @param key: 键 @param value: 值 @param serialize: 是否需要序列化 """ if key: hash_ = HashDB.hashKey(key) self._cache_lock.acquire() self._write_cache[hash_] = getUnicode(value) if not serialize else serializeObject(value) self._cache_lock.release() # 主线程自动刷新缓存 if getCurrentThreadName() in ('0', "MainThread"): self.flush() def flush(self, forced=False): """ 将缓存刷新到数据库 @param forced: 是否强制刷新 """ if not self._write_cache: return # 如果未强制刷新且缓存未达到阈值,则不刷新 if not forced and len(self._write_cache) < HASHDB_FLUSH_THRESHOLD: return # 获取并清空缓存 self._cache_lock.acquire() _ = self._write_cache self._write_cache = {} self._cache_lock.release() try: self.beginTransaction() # 遍历缓存项写入数据库 for hash_, value in _.items(): retries = 0 while True: try: try: self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,)) except sqlite3.IntegrityError: self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,)) except (UnicodeError, OverflowError): # 处理编码错误 break except sqlite3.DatabaseError as ex: if not os.path.exists(self.filepath): debugMsg = "session file '%s' does not exist" % self.filepath logger.debug(debugMsg) break if retries == 0: warnMsg = "there has been a problem while writing to " warnMsg += "the session file ('%s')" % getSafeExString(ex) logger.warning(warnMsg) if retries >= HASHDB_FLUSH_RETRIES: return else: retries += 1 time.sleep(1) else: break finally: self.endTransaction() def beginTransaction(self): """ 开始数据库事务 """ threadData = getCurrentThreadData() if not threadData.inTransaction: try: self.cursor.execute("BEGIN TRANSACTION") except: try: self.cursor.close() except sqlite3.ProgrammingError: pass threadData.hashDBCursor = None self.cursor.execute("BEGIN TRANSACTION") finally: threadData.inTransaction = True def endTransaction(self): """ 结束数据库事务 """ threadData = getCurrentThreadData() if threadData.inTransaction: retries = 0 while retries < HASHDB_END_TRANSACTION_RETRIES: try: self.cursor.execute("END TRANSACTION") threadData.inTransaction = False except sqlite3.OperationalError: pass except sqlite3.ProgrammingError: self.cursor = None threadData.inTransaction = False return else: return retries += 1 time.sleep(1) try: self.cursor.execute("ROLLBACK TRANSACTION") except sqlite3.OperationalError: self.cursor.close() self.cursor = None finally: threadData.inTransaction = False