當前位置:
首頁 > 新聞 > 直播閱讀神器SqlMap源碼(第一集)

直播閱讀神器SqlMap源碼(第一集)

SQLmap閱讀筆記 -1


我使用的閱讀工具:PyCharm


我的SQLmap版本:1.1.3.2#dev


前言:





  1. 國內我沒有找到SQLmap的完整源碼分析文章,就打算在我16歲的時候完成我人生的第一個小目標,讀完SQLmap源碼。



  2. SQLmap是用來對付SQL漏洞的最佳工具,用Python編寫。

    點擊閱讀原文查看官方GIT地址



  3. 這將是一個完整的系列且會穩定更新,作者高中生,學業繁忙。如果我斷更了,我也很絕望。



  4. 這篇文章的定位是,會用PyCharm和SQLmap以及Python的入門人群,對小白將很不友好,建議小白先點亮前置技能,再來學習。



  5. 其實我之前寫了一篇很照顧小白且很詳細的分析筆記,然而被FB退稿了,我也很無奈啊。



  6. 我沒看完,我是一邊看一邊寫,算是直播,所以分析很可能出錯和不嚴謹,求大佬不噴,給我點信心。



  7. 我會跳過我認為不重要的函數。



  8. 歡迎大家加我的QQ交流:1478023488




虎頭:


閱讀的準備工具:



PyCharm(一個很好用的IDE,只是用它就對了):https://www.jetbrains.com/pycharm/


SQLmap(SQLmap的源碼都沒有,你還真的只是來讀文章的?):https://github.com/sqlmapproject/sqlmap


Python2.7(Python環境都沒有,你是來搞笑的嗎?):https://www.python.org/downloads/release/python-2712/


一個注入測試環境(一個注入測試環境):一個注入測試環境




用PyCharm打開SQLmap,然後開始設置運行參數。


run->Edit Configurations


第一個是運行文件的路徑,請填sqlmap.py的路徑


第二個是參數,平時怎麼用就怎麼寫,建議加入--tamper




然後在main下個斷點。 Debug模式運行


上面的藍色三邊形是運行的下一處代碼。


下面的綠色三邊形是運行到下一處斷點。


紅色四邊形是停止運行。


好了,上車吧


SQLmap的框架


我們先在仔細看代碼之前,先大略的看一遍代碼,了解一下

SQLmap的整體框架和結構,這會方便我們之後的仔細分析。


首先,我們可以發現,SQLmap的消息和提示,是用

logging

模塊完成的。我們喜聞樂見的SQLmaplogo就是用這個模塊輸出的


配置信息在

SQLmap目錄/lib/core/log.py

,現在我們即使不看具體代碼,也可以通過logging這個模塊猜到-v參數是怎麼控制消息的輸出等級的了吧。


然後我在

sqlmap.py

中的

mian

函數裡面,看到了這兩句代碼:




sys.stdout = StdDbOut(conf.taskid, messagetype="stdout")


sys.stderr = StdDbOut(conf.taskid, messagetype="stderr")


看到這段代碼,我是懵逼的。這?。。。作者你不按照基本法寫代碼啊!你居然賦值替換Python原生的

sys.stdout

sys.stderr

???



好吧,我查了半天資料,發現確實可以....



只要任何類重寫了write()方法,就可以替換sys.stdout和sys.stderr



文檔連接:https://docs.python.org/2/library/sys.html?highlight=sys#sys.stdout


我們暫時不管我們內心的懵逼之情,先跟進

StdDbOut

函數,來到

SQLmap目錄/lib/api.py

看到

StdDbOut

的代碼。開頭沒什麼好說的,我們先來看

write

函數。




def write(self, value, status=CONTENT_STATUS.IN_PROGRESS, content_type=None):


開頭就又冒出了個

CONTENT_STATUS

:


我們跟進,跳到

SQLmap目錄/lib/core/enums.py


發現一堆

class

.....



這熟悉的定義,這麼多這種模式的

class

....


莫非...你是傳說中的

enum

了?(其實文件名就已經暴露一切了)


看起來作者拿

class

enum

,存了一堆配置信息。


引用一下百度對enum的解釋,詳細信息自己去查。



enum是計算機編程語言中的一種數據類型。枚舉類型:在實際問題中,有些變數的取值被限定在一個有限的範圍內。例如,一個星期內只有七天,一年只有十二個月,一個班每周有六門課程等等。如果把這些量說明為整型,字元型或其它類型顯然是不妥當的。


為此,C語言提供了一種稱為「枚舉」的類型。在「枚舉」類型的定義中列舉出所有可能的取值,被說明為該「枚舉」類型的變數取值不能超過定義的範圍。應該說明的是,枚舉類型是一種基本數據類型,而不是一種構造類型,因為它不能再分解為任何基本類型。


好了,我們回到

SQLmap目錄/lib/api.py

,看完剩下的

write

函數。我們又發現了一個新類名....kb




if kb.partRun is not None:


content_type = PART_RUN_CONTENT_TYPES.get(kb.partRun)


kb又是什麼鬼....算了,我們繼續跟進!


來到

SQLmap目錄/lib/core/data.py



我們發現

AttribDict

賦值給了

kb

,我們只能繼續跟進

AttribDict

函數


來到

SQLmap目錄/lib/core/datatype.py




好吧,我們先看第一行類的定義,發現

AttribDict

繼承了Python原生

dict

,看起來作者自己封裝了一個

dict

。但是,為啥要自己在封裝一個????


我們先繼續往下看



等等,我們抓到了一隻野生的深拷貝函數,去掉頭就可以吃,富含....呸!


看起來和自帶的深拷貝函數沒有區別....才怪啊!


首先,我們都知道Python原生的dist的key是不可變的,dict的key不能為list,dict,set.,value可以為任意。


另外在普及一下淺拷貝和深拷貝區別:





  1. copy.copy 淺拷貝 只拷貝父對象,不會拷貝對象的內部的子對象。



  2. copy.deepcopy 深拷貝 拷貝對象及其子對象


舉個例子:



默認賦值操作是淺拷貝,所以我們只修改了C,但是P的值也同意改變了,你可以理解為C和P都只是一個指針,它們指向同一個數據,另外也可以看到他們的ID是一樣的。



深拷貝就不會出現這種情況。


可能作者需要一個

key

能修改的

dist

且默認都是深拷貝,這個函數把凡是不能直接拷貝的比如id,私有變數,內建函數都特殊處理,其他的都用直接拷貝。看起來這就是作者大佬自己寫一個

dict

的目的了。


好了,我們應該回頭繼續分析了,我也不知道跟進了多少函數,我們回到

sqlmap.py

文件。


開頭的幾個函數沒什麼好講的



checkEnvironment():就是檢查系統環境


setPaths(modulePath()):檢測SQLmap關鍵文件的完整性,可讀可寫等等


banner():輸出我們喜聞樂見的logo



我們先跟進

cmdLineParser().__dict__

,來到

SQLmap目錄/lib/parse/cmdine.py




if not argv:


argv = sys.argv



checkSystemEncoding()



_ = getUnicode(os.path.basename(argv[0]),encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)


開頭首先判斷有沒有傳遞argv進來,沒有的話,就默認拿sys.argv作為默認值。



我們從這裡就可以猜到,這個函數是用來處理我們傳遞的指令的。

checkSystemEncoding()

和剩下的一句,都是處理編碼的:



def checkSystemEncoding():


if sys.getdefaultencoding() == "cp720":


try:


codecs.lookup("cp720")


except LookupError:


errMsg = "there is a known Python issue (#1616979) related "


errMsg += "to support for charset "cp720". Please visit "


errMsg +=""http://blog.oneortheother.info/tip/python-fix-cp720-encoding/index.html""


errMsg += "and follow the instructions to be able to fix it"


logger.critical(errMsg)



warnMsg = "temporary switching to charset "cp1256""


logger.warn(warnMsg)



reload(sys)


sys.setdefaultencoding("cp1256")


看起來不支持cp720編碼,如果是cp720,就臨時切換為cp1256。應該是為了兼容日文系統。


我們來到

parser = OptionParser(usage=usage)

這句,SQLmap的作者用

OptionParser

模塊處理的命令。在然後下面就是用

OptionParser

初始化各種參數,差不多千行,沒什麼分析的價值。


我們回到

sqlmap.py

,跟進

cmdLineOptions.update

。又來到了

SQLmap目錄/lib/core/data.py

。發現

cmdLineOptions

AttrDict

的一個實例,至於

AttrDict

就是之前我們分析的那個作者自己封裝的

dist

。而

update

方法是

AttrDict

的父類

dict

的方法,沒有重寫,接受一個

dict

或者

mapping object

為參數,更新調用者,在這裡就是拿

cmdLineParser

更新

cmdLineOptions

。同時,我們看到

cmdLineParser

有一個

__dict__


那麼關係我們就理清楚了,

cmdLineOptions

通過

update

方法,用

cmdLineParser

__dict__

屬性構建了一個新對象。


在這裡,我先科普一下

__dict__

屬性還有類和父類的繼承關係,本來在分析

AttrDict

的時候就該講的,然而不想講的,但是不講就說不清,果然躲不了啊:



首先,可以看到,我創建了一個新

class

,然後又創建了它的兩個子類,分別是

CNM

FUCK

。然後我通過子類

CNM

W

賦值為20,然後我們全部輸出。發現只有子類的

W

改變了。


在然後我們通過他們的父類

hello

W

賦值為30,然後輸出,發現除了

CNM

還是20.父類

hello

和它的子類

FUCK

都發生了改變,為什麼會這樣呢?



因為一個對象的屬性查找順序遵循首先查找實例對象自己,然後是類,接著是類的父類。具體我們要來了解

__dict__

屬性。


__dict__

是一個

dist

key

是屬性名,

value

為屬性值。Python的實例有自己的

__dict__

,它對應的類也有自己的

__dict__



這裡我輸出了它們的

__dist__

屬性,我們可以看到,

CNM

這個子類的

__dist__

裡面有一個key為W,value為20的參數,所以他輸出的值是20,父類的W是30,所以父類輸出的是30。


那麼,

FUCK

__dist__

裡面沒有W,為什麼也會輸出30呢?



因為一個對象的屬性查找順序遵循首先查找實例對象自己,然後是類,接著是類的父類。同樣,父類的方法也會被子類繼承,這就是之前作者重寫

AttrDict

的基礎


OK,現在我們知道了

__dist__

這個方法的功能和知道了父類和子類的關係,我們在學一個相當Python的Python的技巧,我們就明白這一行代碼的意思了。



cmdLineOptions.update(cmdLineParser().__dict__)


我們回來繼續看這行代碼,但是我們注意到,

cmdLineParser

是一個函數啊......


函數怎麼會有

__dict__

方法呢???????



我試著去掉了

__dict__

方法,如圖:



發現報了一個類型錯誤。


我們之前跟進過

cmdLineParser

函數,知道那是用

OptionParser

模塊寫的一個處理命令的函數,我們先去看它的返回值類型。




我們可以看到返回類型的不同,

.update

方法只支持

dist

類型。


這個時候我們就可以理解為什麼作者要自己封裝一個

AttrDict

並在裡面寫了一個

__dist__

,作者用

__dist__

把返回類型轉換為

dist

類型,然後傳遞給

cmdLineOptions

(

cmdLineOptions

父類是

AttrDict

)用

update

更新為一個

class

類型。


一個設計和思想極其偉大的創意!


蛇尾


以上只是對SQLmap源碼的簡單的一個框架分析,我們可以看出來,作者用

OptionParser

模塊處理用戶傳遞進來的參數。同時自己寫了一個

dist

的子類

AttrDict

來替代原生

dist

的功能。SQLmap的全部

dist

都是用

AttrDict

來做的,同時作者也覆蓋重寫

sys.stdout

sys.stderr

。為什麼覆蓋重寫目前還沒找到原因。


框架因為分析的代碼很少,暫時還給不出來。我們仔細看看我們分析了main的幾行代碼.....好像,10行都沒有???????


但是根據我們已經分析的代碼,我們心中應該也有個模型了-。-


沒有的話,等我下篇文章寫吧-。-


補一句,SQLmap的源碼和作者簡直是大佬,能開無雙那種-。-


豬屁股


好了,下篇文章會在星期6寫出來。


假如我沒懶癌發作的話!


還有我沒被拒稿和被你們噴的話


有錯誤直接說,但是求不噴,我還是很玻璃心的-。-


*本文作者:溫酒,轉載請註明來自Freebuf.COM


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 FreeBuf 的精彩文章:

朝鮮使用先進工具監視公民的數字生活
聊聊身份欺詐和竊取那些事

TAG:FreeBuf |

您可能感興趣

掌閱第三代閱讀器iReader Light評測
Python 源碼閱讀:類型
Python 源碼閱讀:tuple
Python 源碼閱讀:list
Python 源碼閱讀:dict
Python 源碼閱讀: String
Python 源碼閱讀:int
閱讀「新貴」 —Amazon 亞馬遜 Kindle Oasis 電子書閱讀器&Kindle微信公眾號輕體驗
Python 源碼閱讀:內存管理機制(1)
Python 源碼閱讀:對象
Nonfiction閱讀 D第6課:Dollars and Cents
Yuval Noah Harari 的《人類簡史》閱讀感悟(下)
iReader Light 上手:支持 Wi-Fi 傳書、微信推送的輕薄電子閱讀器
Bookeen與家樂福合推電子閱讀器Nolim
新Kindle Oasis:亞馬遜首款防水電子閱讀器
業餘草教你解讀Spark源碼閱讀之HistoryServer
另類手機殼:給iPhone7穿上一件Kindle閱讀器
法語A1閱讀第3期:17天手把手帶你精讀一本法語書「La mort dessinée」
閱讀器一哥之爭 掌閱iReader Light對比Kindle