監聽經過網卡的全部mysql流量,進行解析,可在不影響現有業務狀況下,進行入侵檢測(IDS)或數據集成python
起初發現 用mysql-front訪問數據庫和mysql 的客戶端訪問時數據包格式不一樣,糾結好久,不明白,mysql-front源碼看了眼,delphi,不懂,棄mysql
當連接mysql時,若啓用-C參數表示,對於鏈接數據啓用壓縮,壓縮格式爲zlibsql
mysql的壓縮函數爲:數據庫
1 // mysql-source/mysys/my_compress.c 2 3 my_bool my_compress(uchar *packet, size_t *len, size_t *complen) 4 { 5 DBUG_ENTER("my_compress"); 6 if (*len < MIN_COMPRESS_LENGTH) 7 { 8 *complen=0; 9 DBUG_PRINT("note",("Packet too short: Not compressed")); 10 } 11 else 12 { 13 uchar *compbuf=my_compress_alloc(packet,len,complen); 14 if (!compbuf) 15 DBUG_RETURN(*complen ? 0 : 1); 16 memcpy(packet,compbuf,*len); 17 my_free(compbuf); 18 } 19 DBUG_RETURN(0); 20 } 21 22 23 uchar *my_compress_alloc(const uchar *packet, size_t *len, size_t *complen) 24 { 25 uchar *compbuf; 26 uLongf tmp_complen; 27 int res; 28 *complen= *len * 120 / 100 + 12; 29 30 if (!(compbuf= (uchar *) my_malloc(key_memory_my_compress_alloc, 31 *complen, MYF(MY_WME)))) 32 return 0; /* Not enough memory */ 33 34 tmp_complen= (uint) *complen; 35 res= compress((Bytef*) compbuf, &tmp_complen, (Bytef*) packet, (uLong) *len); 36 *complen= tmp_complen; 37 38 if (res != Z_OK) 39 { 40 my_free(compbuf); 41 return 0; 42 } 43 44 if (*complen >= *len) 45 { 46 *complen= 0; 47 my_free(compbuf); 48 DBUG_PRINT("note",("Packet got longer on compression; Not compressed")); 49 return 0; 50 } 51 /* Store length of compressed packet in *len */ 52 swap_variables(size_t, *len, *complen); 53 return compbuf; 54 }
其中第35行調用了zlib中的compress()函數,可是該處僅對compress()進行了封裝,並無協議解析部分,咱們繼續往下看。函數
整個項目尋找目標代碼比較費勁,能夠先在頭文件中尋找關鍵信息,因而找到了下面的代碼ui
// mysql-source/include/sql_state.h
{ ER_NET_UNCOMPRESS_ERROR ,"08S01", "" }
這是在mysql在解析壓縮的數據時若是出錯的提示信息和錯誤碼,依次能夠查找其引用,發現了真正的數據包壓縮代碼this
1 // mysql-source/sql/net_serv.cc 2 3 static uchar * 4 compress_packet(NET *net, const uchar *packet, size_t *length) 5 { 6 uchar *compr_packet; 7 size_t compr_length; 8 const uint header_length= NET_HEADER_SIZE + COMP_HEADER_SIZE; 9 10 compr_packet= (uchar *) my_malloc(key_memory_NET_compress_packet, 11 *length + header_length, MYF(MY_WME)); 12 13 if (compr_packet == NULL) 14 return NULL; 15 16 memcpy(compr_packet + header_length, packet, *length); 17 18 /* Compress the encapsulated packet. */ 19 if (my_compress(compr_packet + header_length, length, &compr_length)) 20 { 21 /* 22 If the length of the compressed packet is larger than the 23 original packet, the original packet is sent uncompressed. 24 */ 25 compr_length= 0; 26 } 27 28 /* Length of the compressed (original) packet. */ 29 int3store(&compr_packet[NET_HEADER_SIZE], static_cast<uint>(compr_length)); 30 /* Length of this packet. */ 31 int3store(compr_packet, static_cast<uint>(*length)); 32 /* Packet number. */ 33 compr_packet[3]= (uchar) (net->compress_pkt_nr++); 34 35 *length+= header_length; 36 37 return compr_packet; 38 }
從8-19行能夠看到,壓縮數據的組包過程,前面分別加了NET_HEADER_SIZE + COMP_HEADER_SIZE 長的控制字段spa
查找該宏,發現其定義以下code
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 字段中 長度字段存儲 數據部分 未解壓時的長度blog
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
起初看到這個錯誤很頭痛也不想看zlib解析細節,纔有了從mysql找緣由的本文,如今能夠記錄zlib 壓縮字符串的開頭經常是\x78\x9c,出現一樣錯誤的能夠看看是否正確