虽然wireshark自带了很多知名协议的解析插件,譬如HTTP、DHCP等等,然而在实际应用环境中,有不少软件之间的通信协议都是私有的,如游戏客户端和服务器之间的交互协议通常都是私有的,wireshark无法具体解析出各种字段之间的含义,只能显示接收到的二进制数据,给协议的分析和问题的排查带来了一定的困难,尤其是协议内容比较复杂时。
本文一个自定义的简单协议入手,分析如何基于wireshark开发自定义协议分析插件。
本书使用Go语言来描述协议的交互过程。Go由Google出品,号称是互联网上的C语言,有点类似于C语言,以其简洁和高并发著称。
即使不了解Go语言也没有关系,本书给出的代码,基本可以自释其义,了解过程即可,重点不在于此。
1 package packet
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7 "os"
8 )
9
10 type NPacket struct {
11 Version int16 //两个字节
12 ID uint32 //四个字节
13 Buffer [1024]byte //简单起见,固定为1024个字节
14 }
15
16 func (packet *NPacket) String() string {
17 return fmt.Sprintf("%v, %v, %v\n", packet.Version,
18 packet.ID, string(packet.Buffer[:]))
19 }
20
21 func CheckError(err error) {
22 if err != nil {
23 fmt.Println("Error: %s", err.Error())
24 os.Exit(1)
25 }
26 }
27
28 func ConvertToPacket(b []byte) (packet *NPacket, err error) {
29 buffer := bytes.NewBuffer(b[:])
30
31 packet = &NPacket{}
32 #将缓冲区buffer的内容转换到NPacket结构体中
33 err = binary.Read(buffer, binary.BigEndian, packet)
34
35 return packet, err
36 }
37
38 func ConvertToBuffer(packet *NPacket) (b []byte, err error) {
39 buf := new(bytes.Buffer)
40
41 #将结构体内容转换到缓冲区中
42 err = binary.Write(buf, binary.BigEndian, packet)
43
44 return buf.Bytes(), err
45 }
1 package main
2
3 import (
4 "fmt"
5 "net"
6 )
7
8 import "packet"
9
10 func main() {
11 #连接服务端,UDP协议
12 conn, err := net.Dial("udp", "192.168.5.4:11110")
13 defer conn.Close()
14 packet.CheckError(err)
15
16 #定义报文内容
17 pkt_send := packet.NPacket{Version: 1, ID: 1}
18 copy(pkt_send.Buffer[:], "Ping")
19
20 send_buff, err := packet.ConvertToBuffer(&pkt_send)
21 packet.CheckError(err)
22 #发送报文
23 conn.Write(send_buff)
24
25 var recv_msg [4096]byte
26 #读取报文
27 n, err := conn.Read(recv_msg[0:])
28 packet.CheckError(err)
29
30 #报文转换到结构体中
31 pkt_recv, err := packet.ConvertToPacket(recv_msg[0:n])
32 packet.CheckError(err)
33
34 #会调用NPacket结构体的String方法
35 fmt.Println(pkt_recv)
36 }
1 package main
2
3 import (
4 "fmt"
5 "net"
6 )
7
8 import "packet"
9
10 func recvUDPMsg(conn *net.UDPConn) {
11 var buf [4096]byte
12
13 #读取UDP报文
14 n, raddr, err := conn.ReadFromUDP(buf[0:])
15 packet.CheckError(err)
16
17 #网络数据转换到结构体中
18 pkt_recv, err := packet.ConvertToPacket(buf[0:n])
19 packet.CheckError(err)
20
21 fmt.Println(pkt_recv)
22
23 #构造响应报文
24 pkt_send := packet.NPacket{Version: pkt_recv.Version, ID: pkt_recv.ID}
25 copy(pkt_send.Buffer[:], "Pong")
26
27 send_buff, err := packet.ConvertToBuffer(&pkt_send)
28 packet.CheckError(err)
29 #发送报文
30 _, err = conn.WriteToUDP(send_buff, raddr)
31 packet.CheckError(err)
32 }
33
34 func main() {
35 udp_addr, err := net.ResolveUDPAddr("udp", ":11110")
36 packet.CheckError(err)
37
38 conn, err := net.ListenUDP("udp", udp_addr)
39 defer conn.Close()
40 packet.CheckError(err)
41
42 recvUDPMsg(conn)
43 }
1、启动服务端
打开CMD窗口,进入源码所在目录
$env:GOPATH="源码所在目录"
$env:GOPATH
go run src\main\server.go
2、启动客户端
打开CMD窗口,进入源码所在目录
$env:GOPATH="源码所在目录"
$env:GOPATH
go run src\main\client.go