mysql协议解析之握手协议(handshake)

原创
2017/11/02 17:29
阅读数 2.4K

要解析mysql协议需要了解一下知识和软件

一、wireshark

wireshark 是一款网络抓包工具,可以让我们很方便的查看mysql协议。

二、网络字节序

网络字节序分为大端法和小端法,它们的区别可以参考其它网站,在此不做叙述。

三、golang

golang是一款非常赞的开发语言,学习成本比较低,并发性能好的特点

mysql 协议文档详见:(http://www.cnblogs.com/davygeek/p/5647175.html)

mysql 协议分为报文头和报文体 输入图片说明 输入图片说明

这里为了简单起见,没有处理任何的异常情况

package main

import (
	"bytes"
	"fmt"
	"io"
	"net"
)

type Handshake struct {
	//1Byte 协议版本号
	protocol byte
	//n Byte 服务器版本信息(Null-Termimated-String)
	version string
	//4Byte服务器线程ID
	theadID int
	//8bytes 随机挑战数
	salt1 string
	//1Byte 填充值(0x00)
	fill uint8
	//2Byte 服务器权能标志(低16位)
	capabilitiesLow int
	//1Byte字符编码
	charset uint8
	//2Byte服务器状态
	status int
	//2Byte 服务器权能标志(高16位)
	capabilitiesHigh int
	//随机挑战数(12位)
	salt2 string
	//
	pluginName string
}

const (
	ipAddr = "127.0.0.1"
	port   = "3306"
)

func main() {

	conn, _ := net.Dial("tcp", fmt.Sprintf("%s:%s", ipAddr, port))
	defer conn.Close()
	//读取消息体
	body, _ := ReadPackge(conn)

	r := bytes.NewReader(body)
	//跳过4Byte消息头
	SkipHeaderPackage(r)
	//读取1Byte协议版本号
	protoVersion, _ := r.ReadByte()

	//n Byte服务版本信息
	serverVersion := ReadNullTerminatedString(r)

	//4 byte 服务线程ID
	theadIDBuf := make([]byte, 4)
	r.Read(theadIDBuf)
	theadId := int(uint32(theadIDBuf[0]) | uint32(theadIDBuf[1])<<8 | uint32(theadIDBuf[2])<<16 | uint32(theadIDBuf[3])<<24)

	//8 byte 随机挑战数
	saltBuf := make([]byte, 8)
	salt, _ := r.Read(saltBuf)

	//跳过 1 byte填充值
	r.Seek(1, io.SeekCurrent)

	//读取 2 byte 服务器权能标识(低16位)
	capabilitiesLow1Buf := make([]byte, 2)
	r.Read(capabilitiesLow1Buf)
	capabilitiesLow := int(uint32(capabilitiesLow1Buf[0]) | uint32(capabilitiesLow1Buf[1])<<8)
	//读取1 byte 字符编码
	chartset, _ := r.ReadByte()

	//读取2 byte 服务器状态
	serverStatusBuf := make([]byte, 2)
	r.Read(serverStatusBuf)
	serverStatus := int(uint32(serverStatusBuf[0]) | uint32(serverStatusBuf[1]))

	//读取服务器权能标志(高16位)
	capabilitiesHigh2Buf := make([]byte, 2)
	r.Read(capabilitiesHigh2Buf)
	capabilitiesHigh := int(uint32(capabilitiesHigh2Buf[0]) | uint32(capabilitiesHigh2Buf[1])<<8)
	//跳过 1byte 未使用的挑战长度
	r.Seek(1, io.SeekCurrent)
	//跳过10 byte 填充值
	r.Seek(10, io.SeekCurrent)

	//读取12位挑战随机数
	salt2 := ReadNullTerminatedString(r)

	//读取密码认证插件
	pluginName := ReadNullTerminatedString(r)

	//handshake
	handshake := Handshake{
		protocol:         protoVersion,
		version:          serverVersion,
		theadID:          theadId,
		salt1:            string(salt),
		capabilitiesLow:  capabilitiesLow,
		charset:          chartset,
		status:           serverStatus,
		capabilitiesHigh: capabilitiesHigh,
		salt2:            salt2,
		pluginName:       pluginName,
	}
    //打印输出
	fmt.Println(handshake)

}

//读取已0x00结尾的字符串
func ReadNullTerminatedString(r *bytes.Reader) string {
	var str []byte
	for {
		b, _ := r.ReadByte()
		if b == 0x00 {
			return string(str)
		} else {
			str = append(str, b)
		}
	}

}

//跳过消息头
func SkipHeaderPackage(r *bytes.Reader) error {
	if _, err := r.Seek(4, io.SeekStart); err != nil {
		return err
	}
	return nil

}

//读取数据包
func ReadPackge(conn net.Conn) ([]byte, error) {

	//读取4byte消息头
	header := []byte{0, 0, 0, 0}

	if _, err := io.ReadFull(conn, header); err != nil {
		return nil, err
	}
	//获取3byte消息长度
	bodyLen := int(uint32(header[0]) | uint32(header[1]<<8) | uint32(header[2]<<16))

	body := make([]byte, bodyLen)
	//读取消息体
	n, err := io.ReadFull(conn, body)
	if err != nil {
		return nil, err
	}

	return append(header, body[0:n]...), nil

}


展开阅读全文
go
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部