MySQL协议分析(2)
此阶段是在压缩传输无加密条件下进行的协议分析
思路
结合Oracle官网的说明和自己用wireshark加python进行数据包分析
步骤
客户端与服务器端是否压缩的协商阶段
压缩传输的数据包格式
数据包解压缩分析过程
压缩协商阶段
首先,用户发起连接数据库的请求,并添加了压缩的参数-C进行传输,此时在网络中TCP建立后,服务器端会给客户端一个Greeting,其中包括了服务器端的MySQL版本、协议版本、支持的能力(其中就包括是否支持压缩)等。
然后,客户端会发送给服务器端一个登陆请求,其中包括:能力标志(里面包括了是否要进行压缩传输,若是压缩传输,则标志位置为1)、字符集、用户名、密码(md5加密)等。
数据包如下:
MySQL Protocol
Packet Length: 58
Packet Number: 1
Login Request
Client Capabilities: 0xa6a5
.... .... .... ...1 = Long Password: Set
.... .... .... ..0. = Found Rows: Not set
.... .... .... .1.. = Long Column Flags: Set
.... .... .... 0... = Connect With Database: Not set
.... .... ...0 .... = Don't Allow database.table.column: Not set
.... .... ..1. .... = Can use compression protocol: Set
.... .... .0.. .... = ODBC Client: Not set
.... .... 1... .... = Can Use LOAD DATA LOCAL: Set
.... ...0 .... .... = Ignore Spaces before '(': Not set
.... ..1. .... .... = Speaks 4.1 protocol (new flag): Set
.... .1.. .... .... = Interactive Client: Set
.... 0... .... .... = Switch to SSL after handshake: Not set
...0 .... .... .... = Ignore sigpipes: Not set
..1. .... .... .... = Knows about transactions: Set
.0.. .... .... .... = Speaks 4.1 protocol (old flag): Not set
1... .... .... .... = Can do 4.1 authentication: Set
Extended Client Capabilities: 0x0003
MAX Packet: 1073741824
Charset: utf8 COLLATE utf8_general_ci (33)
Username: root
Password: 3dad2597af36922c753c7ef0d0f22c18ee0af0bf
具体的能力标志位如下:
共16位,值为0xa6a5,即1010011010100101,其中,第11位的 Can use compression protocol: Set就是是否压缩,若为1,则压缩传输,若为0,则不压缩。
- 服务器端发送确认,开始压缩传输,规则见下面的压缩传输的数据包格式。
压缩传输的数据包格式说明
压缩数据包主要分为压缩数据包头和压缩后的数据包(或者未压缩的数据包,这种情况在后面说明)
压缩数据包头
压缩数据包头共占7字节,分为3段,格式如下:
第一段: 压缩后的mysql数据包的长度。它的值的计算方法是TCP总数据包的长度-7,也就是总长度减去压缩数据包头剩下的就是mysql数据包压缩后的长度,占3字节;
第二段: sequence id,序列id,占1字节;
第三段: 压缩前/解压缩后的mysql数据包的长度,占3字节。
压缩传输的数据包
分为两种情况:
第一种情况是数据包长度小于50字节,这种情况下即使选择了压缩传输,由于数据包太短,兼顾效率问题,就不会被压缩传输;同时在上述的压缩数据包头的第三个字段会为0.
第二种情况是数据包长度大于50字节,这种情况下就会对数据包进行压缩传输,且压缩数据包头的第三个字段会大于0.
解压缩
在官网中说明了压缩过程选用的是zlib进行的压缩,所以我们需要找到zlib的解压缩方法。本次采用的是Python中的zlib库。
- 请求的压缩
(从客户端发起一个SQL语句的请求,保证此请求的长度大于50bytes,会产生压缩)
执行SQL语句如下:
select ID,USERID,USERNAME,BIRTHDAY,SALARY from smalltable where ID = 999 and USERNAME = 'fdsfsdfdsdsafs'
在wireshark中抓到此次请求的包,右键,复制为转移字符串,然后粘贴到Python中,定义一个字符串s。
import zlib
s = "\x78\x9c\xcb\x64\x60\x60\x60\x2e\x4e\xcd\x49\x4d\x2e\x51\xf0\x74\xd1\x09\x0d\x76\x0d\x82\x52\x7e\x8e\xbe\xae\x3a\x4e\x9e\x41\x21\x1e\x2e\x8e\x91\x3a\xc1\x8e\x3e\x8e\x41\x91\x0a\x69\x45\xf9\xb9\x0a\xc5\xb9\x89\x39\x39\x25\x89\x49\x39\xa9\x0a\xe5\x19\xa9\x45\xa9\x40\x8d\x0a\xb6\x0a\x96\x96\x96\x0a\x89\x79\x29\x0a\x30\xbd\x40\x21\xf5\xb4\x94\xe2\xb4\xe2\x14\x20\x99\x52\x9c\x98\x56\xac\x0e\x00\xcd\x25\x21\x24"
un_c = zlib.decompress(s)
print un_c
观察到输出为iselect ID,USERID,USERNAME,BIRTHDAY,SALARY from smalltable where ID = 999 and USERNAME = 'fdsfsdfdsdsafs'
验证了分析过程的正确性。
问题:在语句前有一个字符'i',猜测可能为校验码之类的值,未验证。
- 响应的压缩
(从客户端发起个SQL语句的请求,保证得到的响应结果大于50bytes,且大于1514,大于1514是为了验证当响应结果特别长,发生了分包时的情况)
执行SQL语句
select * from smalltable where ID < 100
类似于上面的步骤,将所有响应的TCP包中的TCP payload右键,复制为转义字符串,粘贴到Python中,通过这种方式手动把多个包拼在一起。(因为这个包太长了,这里就不放了)程序也与上面相同。验证后正确解压缩,只是结果与我们查看的结果的格式有些许的不同。