sqlmap源码分析(一)
阅读原文时间:2023年07月08日阅读:6

Sqlmap源码分析(一)

此次分析的sqlmap目标版本为1.6.1.2#dev

只对sqlmap的核心检测代码进行详细分析其他的一带而过

为了不让篇幅过长下面只写出了sqlmap.py(入口点)引用的前三个函数,

from __future__ import print_function  # 即使在python2.X,使用print就得像python3.X那样加括号
try:
    '''
        代码作用:
        python版本检测 导入必要的包 禁止生成__pycache__
        导入官方模块
        消除一些可忽略的警告
        导入自定义模块
    '''
except KeyboardInterrupt:
    '''
    代码作用:
    处理ctrl c v手动中断
    '''  

def modulePath():
    pass
def checkEnvironment():
    pass # 下方有详细代码 这里为了方便展示整体结构不再列出
def main():
    try:
        dirtyPatches() # 补丁 针对python3进行额外配置,对其他第三方的一些参数调整(下方有详细代码)
        resolveCrossReferences() # 解决交叉引用(下方有详细代码)
        checkEnvironment()  # 检测环境 sqlmap版本和路径是否正常(下方有详细代码)
        setPaths(modulePath())  # 路径配置
        banner()  # 见名知意打印banner信息
        '''
        代码作用:
        对命令行进行处理
        根据信息进行初始化
        下面的if和elif中代码 测试sqlmap能否能正常运行如果没问题就进入start()执行sql注入
        '''
        if:
            pass
        elif:
            pass
        else:
            pass
            start()
            pass
    except:# 长达300行的自定义错误处理
        pass
    finally:# 收尾工作
        pass

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        pass
    except SystemExit:
        raise
    except:
        traceback.print_exc()
    finally:   # 根据线程数设定不同的退出方式
        if threading.active_count() > 1:
            os._exit(getattr(os, "_exitcode", 0))
        else:
            sys.exit(getattr(os, "_exitcode", 0))
else:
    __import__("lib.controller.controller")

dirtyPatches函数

函数作用

  • 对http.client进行参数的修改,以及针对python3相应方法的重新设置

  • 对universaldetector检测编码方式的调整

  • 针对Windows操作系统上添加对inet_pton()

  • PLACE.CUSTOM_POST调整

    def dirtyPatches():
    """
    在thirdparty\six\init.py中有这样一行 MovedModule("http_client", "httplib", "http.client"),
    six是Python2和3的兼容性库。这个项目旨在支持可同时运行在Python2和3上的代码库
    如果是python3 _http_client实际就是http.client
    """
    # 可以接收很长的响应头结果行 在Python\Lib\http\client.py中_MAXLINE=65536
    _http_client._MAXLINE = 1 * 1024 * 1024

    if six.PY3:  # 如果是python3就执行这部分代码
        '''
            hasattr 返回对象是否具有具有给定名称的属性
            如果_http_client.HTTPConnection没有__send_output(python3中没有)
            就新建一个并且函数引用,现在执行__send_output函数等同于_send_output
        '''
        if not hasattr(_http_client.HTTPConnection, "__send_output"):
            _http_client.HTTPConnection.__send_output = _http_client.HTTPConnection._send_output
    def _send_output(self, *args, **kwargs):
        '''
        conf是AttribDict类(在lib/core/datatype.py中)的对象
        此类定义了字典,并添加了将成员作为属性访问的功能。因为AttribDict继承了python的dict类重写了一部分功能
        产生的对象就可以理解为字典
        '''
        if conf.get("chunked") and "encode_chunked" in kwargs:
            kwargs["encode_chunked"] = False
        self.__send_output(*args, **kwargs)
    
    # 理解为将_send_output函数内容更新
    _http_client.HTTPConnection._send_output = _send_output
    
    # 现在xx.__send_output的内容为原xx._send_output
    # 现在xx._send_output=自定义函数的内容
    if IS_WIN: # 针对Windows操作系统上添加对inet_pton()的支持处理ipv4,将ip地址192.168.1.1转换成二进制的ip地址 from thirdparty.wininetpton import win_inet_pton # 关于编码的配置 codecs.register(lambda name: codecs.lookup("utf-8") if name == "cp65001" else None) # 与上面_send_output操作流程类似 if hasattr(_http_client, "LineAndFileWrapper"): def _(self, *args): return self._readline()
    _http_client.LineAndFileWrapper._readline = _http_client.LineAndFileWrapper.readline
    _http_client.LineAndFileWrapper.readline = _
    # 原值为0.2 universaldetector是Mozilla公司提供的检测编码方式的工具 thirdparty.chardet.universaldetector.MINIMUM_THRESHOLD = 0.90 # 从命令行中匹配对应值 如果匹配到并且值不等于"POST"进行替换操作 match = re.search(r" --method[= ](\w+)", " ".join(sys.argv)) if match and match.group(1).upper() != PLACE.POST: PLACE.CUSTOM_POST = PLACE.CUSTOM_POST.replace("POST", "%s (body)" % match.group(1)) try: os.urandom(1) # 返回包含适合加密使用的随机字节的字节对象(b'\x03') except NotImplementedError: if six.PY3: os.urandom = lambda size: bytes(random.randint(0, 255) for _ in range(size)) else: os.urandom = lambda size: "".join(chr(random.randint(0, 255)) for _ in xrange(size))

resolveCrossReferences函数

下面全部都是函数的替换,等号右侧isDigit、readInput等都是函数。

def resolveCrossReferences():
    lib.core.threads.isDigit = isDigit
    lib.core.threads.readInput = readInput
    lib.core.common.getPageTemplate = getPageTemplate
    lib.core.convert.filterNone = filterNone
    lib.core.convert.isListLike = isListLike
    lib.core.convert.shellExec = shellExec
    lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage
    lib.core.option._pympTempLeakPatch = pympTempLeakPatch
    lib.request.connect.setHTTPHandlers = _setHTTPHandlers
    lib.utils.search.setHTTPHandlers = _setHTTPHandlers
    lib.controller.checks.setVerbosity = setVerbosity
    lib.utils.sqlalchemy.getSafeExString = getSafeExString
    thirdparty.ansistrm.ansistrm.stdoutEncode = stdoutEncode

checkEnvironment函数

函数作用:

  • 检测目录是否正常

  • 检测sqlmap版本是否在1.0以上

  • 将对应的特殊的字典类型变量放到全局中

    def checkEnvironment():
    try: # 检测是否是一个正常的目录 如果有编码问题报错处理
    os.path.isdir(modulePath())
    except UnicodeEncodeError:
    errMsg = "your system does not properly handle non-ASCII paths. "
    errMsg += "Please move the sqlmap's directory to the other location"
    logger.critical(errMsg)
    raise SystemExit

    # 检测sqlmap版本 如果过低提示更新版本 并退出
    if LooseVersion(VERSION) < LooseVersion("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 "
        errMsg += "versions"
        logger.critical(errMsg)
        raise SystemExit
    
    if "sqlmap.sqlmap" in sys.modules:
        for _ in ("cmdLineOptions", "conf", "kb"):
            # 将系统路径lib.core.data中的"cmdLineOptions", "conf", "kb"变为同名的全局变量,这三个是AttribDict(dict)的对象。
            globals()[_] = getattr(sys.modules["lib.core.data"], _)
    for _ in (
            "SqlmapBaseException", "SqlmapShellQuitException", "SqlmapSilentQuitException",
            "SqlmapUserQuitException"):
        globals()[_] = getattr(sys.modules["lib.core.exception"], _)</code></pre></li>

通过读代码发现sqlmap花费了大量的代码在python2的基础上进行修补

@stackedmethod
def start():
    '''
        这些if并不涉及核心的检测代码所以忽略掉
    '''
    if xxx:
        pass
    if xxx:
        pass
    if xxx:
        pass
    for targetUrl, targetMethod, targetData, targetCookie, targetHeaders in kb.targets:
        # 核心代码 核心代码 核心代码

    if xxx:
        pass
    if xxx:
        pass
    return True

核心代码很复杂需要花一些时间 在后续的随笔中再进行分析

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器