#!/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")