#!/usr/bin/env python """ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ # 导入所需的Python标准库 import glob # 用于文件路径模式匹配 import os # 提供与操作系统交互的功能 import re # 正则表达式模块 import shutil # 提供高级文件操作功能 import subprocess # 用于创建子进程 import time # 时间相关功能 import zipfile # ZIP文件操作 # 从自定义库中导入所需函数 from lib.core.common import dataToStdout # 输出数据到标准输出 from lib.core.common import extractRegexResult # 提取正则表达式结果 from lib.core.common import getLatestRevision # 获取最新版本号 from lib.core.common import getSafeExString # 安全地获取异常字符串 from lib.core.common import openFile # 打开文件 from lib.core.common import pollProcess # 轮询进程 from lib.core.common import readInput # 读取用户输入 from lib.core.convert import getText # 文本转换 from lib.core.data import conf # 配置数据 from lib.core.data import logger # 日志记录器 from lib.core.data import paths # 路径信息 from lib.core.revision import getRevisionNumber # 获取版本号 from lib.core.settings import GIT_REPOSITORY # Git仓库地址 from lib.core.settings import IS_WIN # 是否为Windows系统 from lib.core.settings import VERSION # 版本信息 from lib.core.settings import TYPE # 安装类型 from lib.core.settings import ZIPBALL_PAGE # ZIP包下载页面 from thirdparty.six.moves import urllib as _urllib # URL处理 def update(): """ 更新sqlmap的主函数 """ # 如果未启用更新全部选项,直接返回 if not conf.updateAll: return success = False # 更新是否成功的标志 # 如果是通过pip安装的 if TYPE == "pip": infoMsg = "updating sqlmap to the latest stable version from the " infoMsg += "PyPI repository" logger.info(infoMsg) # 记录更新信息 debugMsg = "sqlmap will try to update itself using 'pip' command" logger.debug(debugMsg) # 记录调试信息 dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) # 显示更新进度 output = "" try: # 执行pip更新命令 process = subprocess.Popen("pip install -U sqlmap", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH) pollProcess(process, True) # 轮询进程 output, _ = process.communicate() # 获取输出 success = not process.returncode # 检查返回码 except Exception as ex: success = False output = getSafeExString(ex) finally: output = getText(output) # 根据更新结果输出相应信息 if success: logger.info("%s the latest revision '%s'" % ("already at" if "already up-to-date" in output else "updated to", extractRegexResult(r"\binstalled sqlmap-(?P\d+\.\d+\.\d+)", output) or extractRegexResult(r"\((?P\d+\.\d+\.\d+)\)", output))) else: logger.error("update could not be completed ('%s')" % re.sub(r"[^a-z0-9:/\\]+", " ", output).strip()) # 如果不是Git仓库 elif not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")): warnMsg = "not a git repository. It is recommended to clone the 'sqlmapproject/sqlmap' repository " warnMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY logger.warning(warnMsg) # 提示用户使用git克隆 # 检查是否已是最新版本 if VERSION == getLatestRevision(): logger.info("already at the latest revision '%s'" % (getRevisionNumber() or VERSION)) return # 询问用户是否尝试下载ZIP包更新 message = "do you want to try to fetch the latest 'zipball' from repository and extract it (experimental) ? [y/N]" if readInput(message, default='N', boolean=True): directory = os.path.abspath(paths.SQLMAP_ROOT_PATH) try: # 尝试创建/更新主程序文件 open(os.path.join(directory, "sqlmap.py"), "w+b") except Exception as ex: errMsg = "unable to update content of directory '%s' ('%s')" % (directory, getSafeExString(ex)) logger.error(errMsg) else: # 保存原文件属性 attrs = os.stat(os.path.join(directory, "sqlmap.py")).st_mode # 清理目录内容 for wildcard in ('*', ".*"): for _ in glob.glob(os.path.join(directory, wildcard)): try: if os.path.isdir(_): shutil.rmtree(_) else: os.remove(_) except: pass # 检查目录是否清空 if glob.glob(os.path.join(directory, '*')): errMsg = "unable to clear the content of directory '%s'" % directory logger.error(errMsg) else: try: # 下载并解压最新的ZIP包 archive = _urllib.request.urlretrieve(ZIPBALL_PAGE)[0] with zipfile.ZipFile(archive) as f: for info in f.infolist(): info.filename = re.sub(r"\Asqlmap[^/]+", "", info.filename) if info.filename: f.extract(info, directory) # 获取并显示新版本信息 filepath = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py") if os.path.isfile(filepath): with openFile(filepath, "rb") as f: version = re.search(r"(?m)^VERSION\s*=\s*['\"]([^'\"]+)", f.read()).group(1) logger.info("updated to the latest version '%s#dev'" % version) success = True except Exception as ex: logger.error("update could not be completed ('%s')" % getSafeExString(ex)) else: if not success: logger.error("update could not be completed") else: try: # 恢复文件属性 os.chmod(os.path.join(directory, "sqlmap.py"), attrs) except OSError: logger.warning("could not set the file attributes of '%s'" % os.path.join(directory, "sqlmap.py")) # 如果是Git仓库 else: infoMsg = "updating sqlmap to the latest development revision from the " infoMsg += "GitHub repository" logger.info(infoMsg) debugMsg = "sqlmap will try to update itself using 'git' command" logger.debug(debugMsg) dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) output = "" try: # 执行git更新命令 process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH) pollProcess(process, True) output, _ = process.communicate() success = not process.returncode except Exception as ex: success = False output = getSafeExString(ex) finally: output = getText(output) # 根据git更新结果输出信息 if success: logger.info("%s the latest revision '%s'" % ("already at" if "Already" in output else "updated to", getRevisionNumber())) else: if "Not a git repository" in output: errMsg = "not a valid git repository. Please checkout the 'sqlmapproject/sqlmap' repository " errMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY logger.error(errMsg) else: logger.error("update could not be completed ('%s')" % re.sub(r"\W+", " ", output).strip()) # 如果更新失败,根据操作系统给出建议 if not success: if IS_WIN: infoMsg = "for Windows platform it's recommended " infoMsg += "to use a GitHub for Windows client for updating " infoMsg += "purposes (https://desktop.github.com/) or just " infoMsg += "download the latest snapshot from " infoMsg += "https://github.com/sqlmapproject/sqlmap/downloads" else: infoMsg = "for Linux platform it's recommended " infoMsg += "to install a standard 'git' package (e.g.: 'apt install git')" logger.info(infoMsg)