You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sqlmap/src/sqlmap-master/sqlmap.py

678 lines
47 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python
"""
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from __future__ import print_function # 导入print_function特性确保在Python 2和3中print函数的行为一致
try:
import sys # 导入sys模块用于处理与Python解释器相关的操作
# 防止Python自动生成.pyc文件
sys.dont_write_bytecode = True # 设置不生成.pyc字节码文件以减少磁盘空间占用和加载时间
try:
# 检查sqlmap的安装是否正确
__import__("lib.utils.versioncheck") # 动态导入lib.utils.versioncheck模块通常用于检查版本兼容性
except ImportError: # 如果导入失败(例如模块不存在),则执行以下代码
sys.exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details") # 退出程序并提示安装错误,建议用户访问指定链接查看安装说明
# 导入标准库模块
import bdb # 导入bdb模块用于调试器支持
import glob # 导入glob模块用于文件路径匹配
import inspect # 导入inspect模块用于获取对象信息
import json # 导入json模块用于处理JSON数据
import logging # 导入logging模块用于记录日志信息
import os # 导入os模块用于与操作系统进行交互
import re # 导入re模块用于正则表达式操作
import shutil # 导入shutil模块用于文件操作如复制、删除等
import sys # 再次导入sys模块确保其可用性
import tempfile # 导入tempfile模块用于创建临时文件和目录
import threading # 导入threading模块用于多线程支持
import time # 导入time模块用于时间相关操作
import traceback # 导入traceback模块用于获取异常信息
import warnings # 导入warnings模块用于处理警告信息
# 忽略DeprecationWarning除非命令行参数中包含"--deprecations"
if "--deprecations" not in sys.argv: # 如果命令行参数中不包含"--deprecations"
warnings.filterwarnings(action="ignore", category=DeprecationWarning) # 忽略所有DeprecationWarning类别的警告
else: # 如果命令行参数中包含"--deprecations"
warnings.resetwarnings() # 重置警告过滤器
warnings.filterwarnings(action="ignore", message="'crypt'", category=DeprecationWarning) # 忽略特定消息的DeprecationWarning
warnings.simplefilter("ignore", category=ImportWarning) # 忽略所有ImportWarning类别的警告
if sys.version_info >= (3, 0): # 如果Python版本大于等于3.0
warnings.simplefilter("ignore", category=ResourceWarning) # 忽略所有ResourceWarning类别的警告
# 忽略特定警告
warnings.filterwarnings(action="ignore", message="Python 2 is no longer supported") # 忽略关于Python 2不再支持的警告
warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) # 忽略关于模块已导入的UserWarning
warnings.filterwarnings(action="ignore", message=".*using a very old release", category=UserWarning) # 忽略关于使用旧版本的UserWarning
warnings.filterwarnings(action="ignore", message=".*default buffer size will be used", category=RuntimeWarning) # 忽略关于默认缓冲区大小的RuntimeWarning
warnings.filterwarnings(action="ignore", category=UserWarning, module="psycopg2") # 忽略psycopg2模块的UserWarning
# 导入sqlmap的核心日志模块
from lib.core.data import logger # 从lib.core.data模块导入logger对象用于记录日志信息
# 导入sqlmap的核心功能模块
from lib.core.common import banner # 从lib.core.common模块导入banner函数用于显示程序横幅
from lib.core.common import checkPipedInput # 从lib.core.common模块导入checkPipedInput函数用于检查管道输入
from lib.core.common import checkSums # 从lib.core.common模块导入checkSums函数用于校验文件哈希值
from lib.core.common import createGithubIssue # 从lib.core.common模块导入createGithubIssue函数用于创建GitHub问题
from lib.core.common import dataToStdout # 从lib.core.common模块导入dataToStdout函数用于将数据输出到标准输出
from lib.core.common import extractRegexResult # 从lib.core.common模块导入extractRegexResult函数用于提取正则表达式匹配结果
from lib.core.common import filterNone # 从lib.core.common模块导入filterNone函数用于过滤None值
from lib.core.common import getDaysFromLastUpdate # 从lib.core.common模块导入getDaysFromLastUpdate函数用于计算自上次更新以来的天数
from lib.core.common import getFileItems # 从lib.core.common模块导入getFileItems函数用于从文件中读取内容
from lib.core.common import getSafeExString # 从lib.core.common模块导入getSafeExString函数用于安全地获取异常信息
from lib.core.common import maskSensitiveData # 从lib.core.common模块导入maskSensitiveData函数用于屏蔽敏感数据
from lib.core.common import openFile # 从lib.core.common模块导入openFile函数用于打开文件
from lib.core.common import setPaths # 从lib.core.common模块导入setPaths函数用于设置文件路径
from lib.core.common import weAreFrozen # 从lib.core.common模块导入weAreFrozen函数用于检查程序是否被冻结打包
from lib.core.convert import getUnicode # 从lib.core.convert模块导入getUnicode函数用于将字符串转换为Unicode格式
from lib.core.common import setColor # 从lib.core.common模块导入setColor函数用于设置输出颜色
from lib.core.common import unhandledExceptionMessage # 从lib.core.common模块导入unhandledExceptionMessage函数用于处理未捕获的异常信息
from lib.core.compat import LooseVersion # 从lib.core.compat模块导入LooseVersion类用于版本号比较
from lib.core.compat import xrange # 从lib.core.compat模块导入xrange函数用于兼容Python 2和3的range函数
from lib.core.data import cmdLineOptions # 从lib.core.data模块导入cmdLineOptions对象用于存储命令行选项
from lib.core.data import conf # 从lib.core.data模块导入conf对象用于存储配置信息
from lib.core.data import kb # 从lib.core.data模块导入kb对象用于存储知识库信息
from lib.core.datatype import OrderedSet # 从lib.core.datatype模块导入OrderedSet类用于存储有序集合
from lib.core.enums import MKSTEMP_PREFIX # 从lib.core.enums模块导入MKSTEMP_PREFIX常量表示临时文件前缀
from lib.core.exception import SqlmapBaseException # 从lib.core.exception模块导入SqlmapBaseException类表示sqlmap的基础异常
from lib.core.exception import SqlmapShellQuitException # 从lib.core.exception模块导入SqlmapShellQuitException类表示shell退出异常
from lib.core.exception import SqlmapSilentQuitException # 从lib.core.exception模块导入SqlmapSilentQuitException类表示静默退出异常
from lib.core.exception import SqlmapUserQuitException # 从lib.core.exception模块导入SqlmapUserQuitException类表示用户退出异常
from lib.core.option import init # 从lib.core.option模块导入init函数用于初始化选项
from lib.core.option import initOptions # 从lib.core.option模块导入initOptions函数用于初始化命令行选项
from lib.core.patch import dirtyPatches # 从lib.core.patch模块导入dirtyPatches函数用于应用临时补丁
from lib.core.patch import resolveCrossReferences # 从lib.core.patch模块导入resolveCrossReferences函数用于解决交叉引用问题
from lib.core.settings import GIT_PAGE # 从lib.core.settings模块导入GIT_PAGE常量表示GitHub页面地址
from lib.core.settings import IS_WIN # 从lib.core.settings模块导入IS_WIN常量表示当前操作系统是否为Windows
from lib.core.settings import LAST_UPDATE_NAGGING_DAYS # 从lib.core.settings模块导入LAST_UPDATE_NAGGING_DAYS常量表示提醒更新的天数
from lib.core.settings import LEGAL_DISCLAIMER # 从lib.core.settings模块导入LEGAL_DISCLAIMER常量表示法律声明
from lib.core.settings import THREAD_FINALIZATION_TIMEOUT # 从lib.core.settings模块导入THREAD_FINALIZATION_TIMEOUT常量表示线程结束的超时时间
from lib.core.settings import UNICODE_ENCODING # 从lib.core.settings模块导入UNICODE_ENCODING常量表示默认的Unicode编码
from lib.core.settings import VERSION # 从lib.core.settings模块导入VERSION常量表示程序版本号
from lib.parse.cmdline import cmdLineParser # 从lib.parse.cmdline模块导入cmdLineParser函数用于解析命令行参数
from lib.utils.crawler import crawl # 从lib.utils.crawler模块导入crawl函数用于爬取数据
except Exception as ex: # 捕获所有异常
print("An error occurred: " + str(ex)) # 打印异常信息
except KeyboardInterrupt: # 捕获用户中断如按下Ctrl+C
errMsg = "user aborted" # 定义错误信息
if "logger" in globals(): # 如果logger对象已定义
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
else: # 如果logger对象未定义
import time # 导入time模块
sys.exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg)) # 退出程序并打印错误信息,包含当前时间
def modulePath(): # 定义modulePath函数用于获取程序的目录路径
"""
获取程序的目录路径,即使使用了 py2exe 进行冻结打包也能正确获取。
返回值:
返回程序所在目录的Unicode编码路径字符串。
"""
try:
# 如果程序被py2exe冻结则使用sys.executable获取路径否则使用__file__获取
_ = sys.executable if weAreFrozen() else __file__ # 如果程序被冻结使用sys.executable获取路径否则使用__file__获取当前文件路径
except NameError: # 如果__file__未定义在某些环境下可能发生则执行以下代码
# 如果__file__未定义在某些环境下可能发生则使用inspect模块获取当前函数的文件路径
_ = inspect.getsourcefile(modulePath) # 使用inspect模块获取当前函数的源文件路径
# 获取_的目录路径并转换为Unicode编码
return getUnicode(os.path.dirname(os.path.realpath(_)), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) # 返回_的目录路径并将其转换为Unicode编码
def checkEnvironment(): # 定义checkEnvironment函数用于检查运行环境是否适合运行sqlmap
"""
检查运行环境是否适合运行 sqlmap。
如果在检查过程中发现问题,则会记录错误信息并退出程序。
"""
try:
# 检查程序目录是否存在
os.path.isdir(modulePath()) # 检查modulePath函数返回的路径是否为目录
except UnicodeEncodeError: # 如果系统无法正确处理非ASCII路径则执行以下代码
# 如果系统无法正确处理非ASCII路径则记录错误信息并退出
errMsg = "your system does not properly handle non-ASCII paths. " # 定义错误信息提示系统无法处理非ASCII路径
errMsg += "Please move the sqlmap's directory to the other location" # 建议用户将sqlmap目录移动到其他位置
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查sqlmap的版本是否低于1.0,如果是,则说明运行环境有问题
if LooseVersion(VERSION) < LooseVersion("1.0"): # 如果当前版本低于1.0
errMsg = "your runtime environment (e.g. PYTHONPATH) is " # 定义错误信息,提示运行环境有问题
errMsg += "broken. Please make sure that you are not running " # 建议用户检查是否使用了旧版本的运行脚本
errMsg += "newer versions of sqlmap with runtime scripts for older " # 提示用户不要使用旧版本的运行脚本运行新版本的sqlmap
errMsg += "versions" # 提示用户检查运行环境
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果是通过pip安装的sqlmap则需要对sys.modules进行一些修补操作
if "sqlmap.sqlmap" in sys.modules: # 如果sqlmap.sqlmap模块已加载
for _ in ("cmdLineOptions", "conf", "kb"): # 遍历需要修补的变量名
# 将lib.core.data模块中的cmdLineOptions、conf、kb变量添加到全局变量中
globals()[_] = getattr(sys.modules["lib.core.data"], _) # 将lib.core.data模块中的变量添加到全局变量中
for _ in ("SqlmapBaseException", "SqlmapShellQuitException", "SqlmapSilentQuitException", "SqlmapUserQuitException"): # 遍历需要修补的异常类名
# 将lib.core.exception模块中的异常类添加到全局变量中
globals()[_] = getattr(sys.modules["lib.core.exception"], _) # 将lib.core.exception模块中的异常类添加到全局变量中
def main():
"""
当从命令行运行时,这是 sqlmap 的主函数。
"""
try:
# 应用脏补丁和解析交叉引用
dirtyPatches() # 应用一些临时的修复或补丁
resolveCrossReferences() # 解析并处理代码中的交叉引用
# 检查运行环境并设置程序路径
checkEnvironment() # 检查当前运行环境是否满足要求
setPaths(modulePath()) # 设置程序的路径
banner() # 显示程序的横幅信息
# 解析命令行参数并更新全局配置
args = cmdLineParser() # 解析命令行参数
cmdLineOptions.update(args.__dict__ if hasattr(args, "__dict__") else args) # 更新全局命令行选项
initOptions(cmdLineOptions) # 初始化全局配置选项
# 如果有管道输入,则设置批量模式
if checkPipedInput(): # 检查是否有管道输入
conf.batch = True # 如果有管道输入则设置批量模式为True
# 如果配置了API设置API日志和重定向标准输出和错误
if conf.get("api"): # 检查是否配置了API
# 延迟导入(重量级导入)
from lib.utils.api import StdDbOut # 导入StdDbOut类用于重定向标准输出和错误
from lib.utils.api import setRestAPILog # 导入setRestAPILog函数用于设置API日志
# 重定向标准输出和错误到IPC数据库
sys.stdout = StdDbOut(conf.taskid, messagetype="stdout") # 重定向标准输出到IPC数据库
sys.stderr = StdDbOut(conf.taskid, messagetype="stderr") # 重定向标准错误到IPC数据库
setRestAPILog() # 设置API日志
# 设置显示时间并显示法律声明和启动信息
conf.showTime = True # 设置显示时间
dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True) # 显示法律声明
dataToStdout("[*] starting @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True) # 显示启动信息
# 初始化程序
init() # 调用初始化函数,完成程序的初始化工作
# 如果没有设置更新所有选项,则执行后续操作
if not conf.updateAll: # 检查是否设置了更新所有选项
# 延迟导入(更快的启动)
if conf.smokeTest: # 检查是否设置了烟雾测试
# 运行烟雾测试
from lib.core.testing import smokeTest # 导入烟雾测试函数
os._exitcode = 1 - (smokeTest() or 0) # 运行烟雾测试并设置退出码
elif conf.vulnTest: # 检查是否设置了漏洞测试
# 运行漏洞测试
from lib.core.testing import vulnTest # 导入漏洞测试函数
os._exitcode = 1 - (vulnTest() or 0) # 运行漏洞测试并设置退出码
else:
# 启动sqlmap控制器
from lib.controller.controller import start # 导入启动控制器函数
if conf.profile: # 检查是否设置了性能分析
# 如果设置了性能分析,则进行性能分析
from lib.core.profiling import profile # 导入性能分析函数
globals()["start"] = start # 将start函数添加到全局命名空间
profile() # 运行性能分析
else:
try:
# 如果设置了爬取深度和批量文件,则开始爬取
if conf.crawlDepth and conf.bulkFile: # 检查是否设置了爬取深度和批量文件
targets = getFileItems(conf.bulkFile) # 从批量文件中获取目标列表
for i in xrange(len(targets)): # 遍历目标列表
target = None # 初始化目标变量
try:
kb.targets = OrderedSet() # 初始化目标集合
target = targets[i] # 获取当前目标
if not re.search(r"(?i)\Ahttp[s]*://", target): # 检查目标是否包含协议
target = "http://%s" % target # 如果没有协议则添加默认的HTTP协议
infoMsg = "starting crawler for target URL '%s' (%d/%d)" % (target, i + 1, len(targets)) # 生成日志信息
logger.info(infoMsg) # 记录日志信息
crawl(target) # 开始爬取目标
except Exception as ex: # 捕获异常
if target and not isinstance(ex, SqlmapUserQuitException): # 检查异常类型
errMsg = "problem occurred while crawling '%s' ('%s')" % (target, getSafeExString(ex)) # 生成错误信息
logger.error(errMsg) # 记录错误信息
else:
raise # 重新抛出异常
else:
if kb.targets: # 检查目标集合是否非空
start() # 启动控制器
else:
start() # 如果没有设置爬取深度和批量文件,则直接启动控制器
except Exception as ex: # 捕获异常
os._exitcode = 1 # 设置退出码为1
# 如果无法启动新线程,则记录错误信息并退出
if "can't start new thread" in getSafeExString(ex): # 检查异常信息是否包含无法启动新线程
errMsg = "unable to start new threads. Please check OS (u)limits" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
else:
raise # 重新抛出异常
# 捕获并处理各种异常,记录错误信息并退出
except SqlmapUserQuitException: # 捕获用户主动退出的异常
if not conf.batch: # 检查是否处于批量模式
errMsg = "user quit" # 生成错误信息
logger.error(errMsg) # 记录错误信息
except (SqlmapSilentQuitException, bdb.BdbQuit): # 捕获静默退出或调试器退出的异常
pass # 不处理,直接跳过
except SqlmapShellQuitException: # 捕获SQLMap Shell退出的异常
cmdLineOptions.sqlmapShell = False # 设置SQLMap Shell选项为False
except SqlmapBaseException as ex: # 捕获SQLMap基础异常
errMsg = getSafeExString(ex) # 获取安全的异常信息
logger.critical(errMsg) # 记录严重错误信息
os._exitcode = 1 # 设置退出码为1
raise SystemExit # 退出程序
except KeyboardInterrupt: # 捕获键盘中断异常如Ctrl+C
try:
print() # 打印空行
except IOError: # 捕获IO错误
pass # 不处理,直接跳过
except EOFError: # 捕获文件结束异常如Ctrl+D
print() # 打印空行
errMsg = "exit" # 生成错误信息
logger.error(errMsg) # 记录错误信息
except SystemExit as ex: # 捕获系统退出异常
os._exitcode = ex.code or 0 # 设置退出码为异常代码或0
except: # 捕获所有其他异常
print() # 打印空行
errMsg = unhandledExceptionMessage() # 获取未处理异常的信息
excMsg = traceback.format_exc() # 获取异常的详细堆栈信息
valid = checkSums() # 检查校验和
os._exitcode = 255 # 设置退出码为255
# 如果异常信息中包含内存耗尽相关的消息,则记录内存耗尽错误并退出
if any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): # 检查异常信息是否包含内存耗尽
errMsg = "memory exhaustion detected" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含磁盘空间不足相关的消息,则记录磁盘空间错误并退出
elif any(_ in excMsg for _ in ("No space left", "Disk quota exceeded", "Disk full while accessing")): # 检查异常信息是否包含磁盘空间不足
errMsg = "no space left on output device" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含分页文件空间不足的消息,则记录分页文件空间错误并退出
elif any(_ in excMsg for _ in ("The paging file is too small",)): # 检查异常信息是否包含分页文件空间不足
errMsg = "no space left for paging file" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含权限拒绝和Metasploit相关的消息则记录Metasploit权限错误并退出
elif all(_ in excMsg for _ in ("Access is denied", "subprocess", "metasploit")): # 检查异常信息是否包含Metasploit权限错误
errMsg = "permission error occurred while running Metasploit" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含权限拒绝和metasploit相关的消息则记录Metasploit权限错误并退出
elif all(_ in excMsg for _ in ("Permission denied", "metasploit")): # 检查异常信息是否包含Metasploit权限错误
errMsg = "permission error occurred while using Metasploit" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含只读文件系统的消息,则记录只读文件系统错误并退出
elif "Read-only file system" in excMsg: # 检查异常信息是否包含只读文件系统
errMsg = "output device is mounted as read-only" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含系统资源不足的消息,则记录资源耗尽错误并退出
elif "Insufficient system resources" in excMsg: # 检查异常信息是否包含系统资源不足
errMsg = "resource exhaustion detected" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含磁盘I/O错误的消息则记录I/O错误并退出
elif "OperationalError: disk I/O error" in excMsg: # 检查异常信息是否包含磁盘I/O错误
errMsg = "I/O error on output device" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含URL违反BIDI规则的消息则记录无效URL错误并退出
elif "Violation of BIDI" in excMsg: # 检查异常信息是否包含URL违反BIDI规则
errMsg = "invalid URL (violation of Bidi IDNA rule - RFC 5893)" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含无效IPv6 URL的消息则记录无效URL错误并退出
elif "Invalid IPv6 URL" in excMsg: # 检查异常信息是否包含无效IPv6 URL
errMsg = "invalid URL ('%s')" % excMsg.strip().split('\n')[-1] # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含临时文件访问问题的消息,则记录临时文件访问错误并退出
elif "_mkstemp_inner" in excMsg: # 检查异常信息是否包含临时文件访问问题
errMsg = "there has been a problem while accessing temporary files" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含无法写入临时目录的消息,则记录临时目录写入错误并退出
elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp", "tempfile.py")): # 检查异常信息是否包含临时目录写入问题
errMsg = "unable to write to the temporary directory '%s'. " % tempfile.gettempdir() # 生成错误信息
errMsg += "Please make sure that your disk is not full and " # 添加提示信息
errMsg += "that you have sufficient write permissions to " # 添加提示信息
errMsg += "create temporary files and/or directories" # 添加提示信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含权限拒绝的消息,则记录文件访问权限错误并退出
elif "Permission denied: '" in excMsg: # 检查异常信息是否包含权限拒绝
match = re.search(r"Permission denied: '([^']*)", excMsg) # 提取被拒绝访问的文件路径
errMsg = "permission error occurred while accessing file '%s'" % match.group(1) # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含sqlalchemy包版本问题的消息则记录sqlalchemy版本错误并退出
elif all(_ in excMsg for _ in ("twophase", "sqlalchemy")): # 检查异常信息是否包含sqlalchemy版本问题
errMsg = "please update the 'sqlalchemy' package (>= 1.1.11) " # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含Python版本问题的消息则记录Python版本错误并退出
elif "invalid maximum character passed to PyUnicode_New" in excMsg and re.search(r"\A3\.[34]", sys.version) is not None: # 检查异常信息是否包含Python版本问题
errMsg = "please upgrade the Python version (>= 3.5) " # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含PyMySQL包版本问题的消息则记录PyMySQL版本错误并退出
elif all(_ in excMsg for _ in ("scramble_caching_sha2", "TypeError")): # 检查异常信息是否包含PyMySQL版本问题
errMsg = "please downgrade the 'PyMySQL' package (=< 0.8.1) " # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果异常信息中包含Python解
# 假设excMsg是一个包含异常信息的字符串变量
# 检查excMsg是否包含"pymysql"和"configparser"这两个字符串
elif all(_ in excMsg for _ in ("pymysql", "configparser")): # 检查异常信息是否包含pymysql和configparser
errMsg = "wrong initialization of 'pymsql' detected (using Python3 dependencies)" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查excMsg是否包含"ntlm"、"socket.error, err"和"SyntaxError"这三个字符串
elif all(_ in excMsg for _ in ("ntlm", "socket.error, err", "SyntaxError")): # 检查异常信息是否包含ntlm、socket.error和SyntaxError
errMsg = "wrong initialization of 'python-ntlm' detected (using Python2 syntax)" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查excMsg是否包含"drda"和"to_bytes"这两个字符串
elif all(_ in excMsg for _ in ("drda", "to_bytes")): # 检查异常信息是否包含drda和to_bytes
errMsg = "wrong initialization of 'drda' detected (using Python3 syntax)" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查excMsg是否包含特定的错误信息即'WebSocket'对象没有'status'属性
elif "'WebSocket' object has no attribute 'status'" in excMsg: # 检查异常信息是否包含WebSocket对象缺少status属性
errMsg = "wrong websocket library detected" # 生成错误信息
errMsg += " (Reference: 'https://github.com/sqlmapproject/sqlmap/issues/4572#issuecomment-775041086')" # 添加参考链接
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查excMsg是否包含特定的错误信息即初始化GUI界面时出现问题
elif all(_ in excMsg for _ in ("window = tkinter.Tk()",)): # 检查异常信息是否包含GUI初始化问题
errMsg = "there has been a problem in initialization of GUI interface " # 生成错误信息
errMsg += "('%s')" % excMsg.strip().split('\n')[-1] # 添加具体错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查excMsg是否包含特定的错误信息即使用了不同版本的sqlmap文件
elif any(_ in excMsg for _ in ("unable to access item 'liveTest'",)): # 检查异常信息是否包含不同版本sqlmap文件问题
errMsg = "detected usage of files from different versions of sqlmap" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查errMsg是否包含特定的错误信息即版本号相关的错误
elif any(_ in errMsg for _ in (": 9.9.9#",)): # 检查错误信息是否包含特定版本号
errMsg = "LOL xD" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 检查是否设置了键盘中断的标记
elif kb.get("dumpKeyboardInterrupt"): # 检查是否设置了键盘中断标记
raise SystemExit # 退出程序
# 检查excMsg是否包含特定的错误信息即"Broken pipe"
elif any(_ in excMsg for _ in ("Broken pipe",)): # 检查异常信息是否包含Broken pipe
raise SystemExit # 退出程序
# 检查valid变量是否为False
elif valid is False: # 检查校验和是否无效
errMsg = "code checksum failed (turning off automatic issue creation). " # 生成错误信息
errMsg += "You should retrieve the latest development version from official GitHub " # 添加提示信息
errMsg += "repository at '%s'" % GIT_PAGE # 添加GitHub仓库链接
logger.critical(errMsg) # 记录严重错误信息
print() # 打印空行
dataToStdout(excMsg) # 输出异常信息到标准输出
raise SystemExit # 退出程序
# 检查errMsg和excMsg组合后是否包含特定的错误信息即文件路径或特定参数
elif any(_ in "%s\n%s" % (errMsg, excMsg) for _ in ("tamper/", "waf/", "--engagement-dojo")): # 检查错误信息是否包含特定路径或参数
logger.critical(errMsg) # 记录严重错误信息
print() # 打印空行
dataToStdout(excMsg) # 输出异常信息到标准输出
raise SystemExit # 退出程序
# 检查错误信息中是否包含特定的错误关键词,如果包含,则认为是运行时环境错误,并提取错误信息的最后部分。
elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "<frozen", "Can't find file for module", "SAXReaderNotAvailable", "<built-in function compile> returned NULL without setting an exception", "source code string cannot contain null bytes", "No module named", "tp_name field", "module 'sqlite3' has no attribute 'OperationalError'")): # 检查异常信息是否包含运行时环境错误
errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含非ASCII字符编码错误则认为是运行时环境错误并提取错误信息的最后部分。
elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")): # 检查异常信息是否包含非ASCII字符编码错误
errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含文件不存在的错误,则认为是运行时环境错误,并提取错误信息的最后部分。
elif all(_ in excMsg for _ in ("FileNotFoundError: [Errno 2] No such file or directory", "cwd = os.getcwd()")): # 检查异常信息是否包含文件不存在错误
errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含权限错误,则提示用户可能存在权限问题,并建议重新运行程序时禁用多进程。
elif all(_ in excMsg for _ in ("PermissionError: [WinError 5]", "multiprocessing")): # 检查异常信息是否包含权限错误
errMsg = "there is a permission problem in running multiprocessing on this system. " # 生成错误信息
errMsg += "Please rerun with '--disable-multi'" # 添加建议信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含文件不存在的错误则提示用户安装可能已损坏并建议从官方GitHub仓库获取最新开发版本。
elif all(_ in excMsg for _ in ("No such file", "_'")): # 检查异常信息是否包含文件不存在错误
errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] # 生成错误信息
errMsg += "You should retrieve the latest development version from official GitHub " # 添加提示信息
errMsg += "repository at '%s'" % GIT_PAGE # 添加GitHub仓库链接
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含尝试在生产环境中运行开发测试的错误,则输出相应的错误信息。
elif all(_ in excMsg for _ in ("No such file", "sqlmap.conf", "Test")): # 检查异常信息是否包含在生产环境中运行开发测试的错误
errMsg = "you are trying to run (hidden) development tests inside the production environment" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含HTTP NTLM认证相关的兼容性问题则输出相应的错误信息并提供参考链接。
elif all(_ in excMsg for _ in ("HTTPNtlmAuthHandler", "'str' object has no attribute 'decode'")): # 检查异常信息是否包含HTTP NTLM认证兼容性问题
errMsg = "package 'python-ntlm' has a known compatibility issue with the " # 生成错误信息
errMsg += "Python 3 (Reference: 'https://github.com/mullender/python-ntlm/pull/61&#39;)&#34; " # 添加参考链接
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含字典对象属性错误,并且与枚举相关,则提示用户可能存在枚举问题,并建议重新运行程序时刷新会话。
elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")): # 检查异常信息是否包含字典对象属性错误
errMsg = "there has been a problem in enumeration. " # 生成错误信息
errMsg += "Because of a considerable chance of false-positive case " # 添加提示信息
errMsg += "you are advised to rerun with switch '--flush-session'" # 添加建议信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含数据库磁盘映像损坏的错误,则提示用户会话文件可能损坏,并建议刷新会话。
elif "database disk image is malformed" in excMsg: # 检查异常信息是否包含数据库磁盘映像损坏
errMsg = "local session file seems to be malformed. Please rerun with '--flush-session'" # 生成错误信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含模块属性错误,则认为是运行时环境错误,并提取错误信息的最后部分。
elif "AttributeError: 'module' object has no attribute 'F_GETFD'" in excMsg: # 检查异常信息是否包含模块属性错误
errMsg = "invalid runtime (\"%s\") " % excMsg.split("Error: ")[-1].strip() # 生成错误信息
errMsg += "(Reference: 'https://stackoverflow.com/a/38841364&#39; & 'https://bugs.python.org/issue24944#msg249231&#39;)&#34; " # 添加参考链接
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 如果错误信息包含marshal数据错误则提示用户.pyc文件可能损坏并建议删除.pyc文件来解决问题。
elif "bad marshal data (unknown type code)" in excMsg: # 检查异常信息是否包含marshal数据错误
match = re.search(r"\s*(.+)\s+ValueError", excMsg) # 提取损坏的文件路径
errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") # 生成错误信息
errMsg += ". Please delete .pyc files on your system to fix the problem" # 添加建议信息
logger.critical(errMsg) # 记录严重错误信息
raise SystemExit # 退出程序
# 遍历异常信息中的文件路径,并对其进行规范化处理
for match in re.finditer(r'File "(.+?)", line', excMsg): # 查找异常信息中的文件路径
file_ = match.group(1) # 提取文件路径
try:
file_ = os.path.relpath(file_, os.path.dirname(__file__)) # 将文件路径转换为相对路径
except ValueError: # 捕获路径转换错误
pass # 忽略错误
file_ = file_.replace("\\", '/') # 将路径中的反斜杠替换为正斜杠
if "../" in file_: # 检查路径中是否包含上级目录
file_ = re.sub(r"(\.\./)+", '/', file_) # 替换上级目录为单斜杠
else:
file_ = file_.lstrip('/') # 去除路径开头的斜杠
file_ = re.sub(r"/{2,}", '/', file_) # 将多个斜杠替换为单斜杠
excMsg = excMsg.replace(match.group(1), file_) # 更新异常信息中的文件路径
# 对错误信息和异常信息进行敏感数据掩码处理
errMsg = maskSensitiveData(errMsg) # 掩码处理错误信息中的敏感数据
excMsg = maskSensitiveData(excMsg) # 掩码处理异常信息中的敏感数据
# 如果配置了API或校验和无效则记录错误信息和异常信息
if conf.get("api") or not valid: # 检查是否配置了API或校验和无效
logger.critical("%s\n%s" % (errMsg, excMsg)) # 记录严重错误信息和异常信息
else:
logger.critical(errMsg) # 记录严重错误信息
dataToStdout("%s\n" % setColor(excMsg.strip(), level=logging.CRITICAL)) # 输出带颜色的异常信息到标准输出
createGithubIssue(errMsg, excMsg) # 创建GitHub问题
finally: # 无论是否发生异常,最终都会执行的代码块
kb.threadContinue = False # 设置线程继续标志为False
# 如果距离上次更新的天数超过指定值,则提示用户版本过时
if (getDaysFromLastUpdate() or 0) > LAST_UPDATE_NAGGING_DAYS: # 检查距离上次更新的天数
warnMsg = "your sqlmap version is outdated" # 生成警告信息
logger.warning(warnMsg) # 记录警告信息
# 如果配置了显示时间,则输出结束时间
if conf.get("showTime"): # 检查是否配置了显示时间
dataToStdout("\n[*] ending @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True) # 输出结束时间
kb.threadException = True # 设置线程异常标志为True
# 如果存在临时目录,则清理临时文件
if kb.get("tempDir"): # 检查是否存在临时目录
for prefix in (MKSTEMP_PREFIX.IPC, MKSTEMP_PREFIX.TESTING, MKSTEMP_PREFIX.COOKIE_JAR, MKSTEMP_PREFIX.BIG_ARRAY): # 遍历临时文件前缀
for filepath in glob.glob(os.path.join(kb.tempDir, "%s*" % prefix)): # 查找匹配的临时文件
try:
os.remove(filepath) # 删除临时文件
except OSError: # 捕获文件删除错误
pass # 忽略错误
# 如果临时目录中没有其他文件,则删除临时目录
if not filterNone(filepath for filepath in glob.glob(os.path.join(kb.tempDir, '*')) if not any(filepath.endswith(_) for _ in (".lock", ".exe", ".so", '_'))): # 检查临时目录是否为空
# 使用glob模块获取临时目录中的所有文件路径并过滤掉以.lock、.exe、.so或_结尾的文件
try:
shutil.rmtree(kb.tempDir, ignore_errors=True) # 删除临时目录ignore_errors=True表示忽略删除过程中可能出现的错误
except OSError: # 捕获目录删除错误
pass # 忽略错误,继续执行后续代码
if conf.get("hashDB"): # 检查配置中是否存在hashDB
conf.hashDB.flush(True) # 将hashDB中的数据刷新到磁盘
conf.hashDB.close() # 关闭hashDB连接NOTE: 由于PyPy的原因需要显式关闭
if conf.get("harFile"): # 检查配置中是否存在harFile
try:
with openFile(conf.harFile, "w+b") as f: # 以二进制写模式打开harFile
json.dump(conf.httpCollector.obtain(), fp=f, indent=4, separators=(',', ': ')) # 将HTTP收集器中的数据以JSON格式写入文件
except SqlmapBaseException as ex: # 捕获sqlmap基础异常
errMsg = getSafeExString(ex) # 获取异常的安全字符串表示
logger.critical(errMsg) # 记录严重错误日志
if conf.get("api"): # 检查配置中是否存在api
conf.databaseCursor.disconnect() # 断开数据库游标连接
if conf.get("dumper"): # 检查配置中是否存在dumper
conf.dumper.flush() # 刷新dumper中的数据
# short delay for thread finalization
_ = time.time() # 获取当前时间
while threading.active_count() > 1 and (time.time() - _) > THREAD_FINALIZATION_TIMEOUT: # 检查当前活跃线程数是否大于1并且等待时间是否超过线程最终化超时时间
time.sleep(0.01) # 等待0.01秒避免CPU占用过高
if cmdLineOptions.get("sqlmapShell"): # 检查命令行选项中是否存在sqlmapShell
cmdLineOptions.clear() # 清空命令行选项
conf.clear() # 清空配置
kb.clear() # 清空知识库
conf.disableBanner = True # 禁用启动横幅
main() # 调用主函数重新启动sqlmap
# 检查是否是作为主模块运行,如果是,则执行以下代码
if __name__ == "__main__":
try:
# 尝试调用main函数
main()
except KeyboardInterrupt:
# 如果用户按下Ctrl+C键盘中断则捕获KeyboardInterrupt异常但不执行任何操作pass表示空操作
pass
except SystemExit:
# 如果程序调用了sys.exit()则重新抛出SystemExit异常允许正常退出流程
raise
except:
# 捕获其他所有异常,并打印异常信息
traceback.print_exc()
finally:
# 无论try块中的代码是否成功执行都会执行finally块中的代码
# 参考http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program
# 检查当前线程数量是否大于1主线程和至少一个其他线程
if threading.active_count() > 1:
# 如果大于1则调用os._exit强制退出程序不进行清理操作
# getattr(os, "_exitcode", 0)用于获取os模块的_exitcode属性如果不存在则默认为0
os._exit(getattr(os, "_exitcode", 0))
else:
# 如果只有主线程,则正常退出程序
sys.exit(getattr(os, "_exitcode", 0))
else:
# 如果不是作为主模块运行则取消延迟导入因为CI/CD检查
__import__("lib.controller.controller")