Sqlmap源碼分析(一)
此次分析的sqlmap目標版本為1.6.1.2#dev
只對sqlmap的核心檢測代碼進行詳細分析其他的一帶而過
sqlmap.py文件結構
為了不讓篇幅過長下面只寫出了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"], _)
通過讀代碼發現sqlmap花費了大量的代碼在python2的基礎上進行修補😶😶😶
start函數
@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
核心代碼很復雜需要花一些時間 在后續的隨筆中再進行分析