需求
監聽通過網卡的所有mysql流量,進行解析,可在不影響現有業務情況下,進行入侵檢測(ids)或數據集成
協議要點
起初發現 用mysql-front訪問數據庫和mysql 的客戶端訪問時數據包格式不同,糾結很久,不明白,mysql-front源碼看了眼,delphi,不懂,棄
壓縮解析
當鏈接mysql時,若啟用-C參數表示,對于連接數據啟用壓縮,壓縮格式為zlib
mysql的壓縮函數為:
//?mysql-source/mysys/my_compress.c my_bool?my_compress(uchar?*packet,?size_t?*len,?size_t?*complen) { ??DBUG_ENTER("my_compress"); ??if?(*len?=?*len) ??{ ????*complen=?0; ????my_free(compbuf); ????DBUG_PRINT("note",("Packet?got?longer?on?compression;?Not?compressed")); ????return?0; ??} ??/*?Store?length?of?compressed?packet?in?*len?*/ ??swap_variables(size_t,?*len,?*complen); ??return?compbuf; }
?其中第35行調用了zlib中的compress()函數,但是該處僅對compress()進行了封裝,并沒有協議解析部分,我們繼續往下看。
整個項目尋找目標代碼比較費勁,可以先在頭文件中尋找關鍵信息,于是找到了下面的代碼
//?mysql-source/include/sql_state.h {?ER_NET_UNCOMPRESS_ERROR?????????????????,"08S01",?""?}
這是在mysql在解析壓縮的數據時如果出錯的提示信息和錯誤碼,依次可以查找其引用,發現了真正的數據包壓縮代碼
//?mysql-source/sql/net_serv.cc static?uchar?* compress_packet(NET?*net,?const?uchar?*packet,?size_t?*length) { ??uchar?*compr_packet; ??size_t?compr_length; ??const?uint?header_length=?NET_HEADER_SIZE?+?COMP_HEADER_SIZE; ??compr_packet=?(uchar?*)?my_malloc(key_memory_NET_compress_packet, ????????????????????????????????????*length?+?header_length,?MYF(MY_WME)); ??if?(compr_packet?==?NULL) ????return?NULL; ??memcpy(compr_packet?+?header_length,?packet,?*length); ??/*?Compress?the?encapsulated?packet.?*/ ??if?(my_compress(compr_packet?+?header_length,?length,?&compr_length)) ??{ ????/* ??????If?the?length?of?the?compressed?packet?is?larger?than?the ??????original?packet,?the?original?packet?is?sent?uncompressed. ????*/ ????compr_length=?0; ??} ??/*?Length?of?the?compressed?(original)?packet.?*/ ??int3store(&compr_packet[NET_HEADER_SIZE],?static_cast<uint>(compr_length)); ??/*?Length?of?this?packet.?*/ ??int3store(compr_packet,?static_cast<uint>(*length)); ??/*?Packet?number.?*/ ??compr_packet[3]=?(uchar)?(net->compress_pkt_nr++); ??*length+=?header_length; ??return?compr_packet; }</uint></uint>
?從8-19行可以看到,壓縮數據的組包過程,前面分別加了NET_HEADER_SIZE + COMP_HEADER_SIZE 長的控制字段
查找該宏,發現其定義如下
1?//?mysql-source/include/mysql_com.h 2? 3???/*?Constants?when?using?compression?*/ 4?#define?NET_HEADER_SIZE?4????????/*?standard?header?size?*/ 5?#define?COMP_HEADER_SIZE?3????????/*?compression?header?extra?size?*/
NET_HEADER_SIZE 字段中 長度字段存儲 數據部分 未解壓時的長度
COMP_HEADER_SIZE 字段是用來存儲 ?解壓后的 數據的長度,我們可以依次申請內存,然后調用zlib對壓縮內容進行解析即可。
?如果不分析直接進行對wireshark抓到的數據進行zlib解析的話,由于控制字段的存在會解壓縮失敗,在python中的報錯如下
Traceback?(most?recent?call?last): ??File?"<stdin>",?line?1,?in?<module>zlib.error:?Error?-3?while?decompressing?data:?incorrect?data?check</module></stdin>
?起初看到這個錯誤很頭痛也不想看zlib解析細節,才有了從mysql找原因的本文,現在可以記錄zlib 壓縮字符串的開頭常常是x78x9c,出現同樣錯誤的可以看看是否正確