需要用到的包

gopacket: ``go get -u -v github.com/google/gopacket

代码演示

获取 pcap 版本及网卡列表

import (
    "fmt"
    "github.com/google/gopacket/pcap"
)

func main() {
    //  获取 libpcap 的版本
    version := pcap.Version()
    fmt.Println(version)
    //  获取网卡列表
    var devices []pcap.Interface
    devices, _ := pcap.FindAllDevs()
        fmt.Println(devices)
}
pcap.Interface
type Interface struct {
    Name        string
    Description    string
    Flags        uint32
    Addresses    []InterfaceAddress
}
InterfaceAddress
type InterfaceAddress struct {
    IP  net.IP
    Netmask net.IPMask
}

打开网络接口

这是在线捕获分析

handle, _ := pcap.OpenLive(
    "eth0", // device
    int32(65535),   //  snapshot length
    false,  //  promiscuous mode?
    -1 * time.Second,   // timeout 负数表示不缓存,直接输出
)
defer handle.Close()

打开dump文件

对于一些抓到的包进行离线分析,可以用文件。

handle, _ := pcap.OpenOffline("dump.pcap")
defer handle.Close()

建立 packet source

packetSource := gopacket.NewPacketSource(
    handle,
    handle.LinkType()
)

从 packet source 读取抓的包

一个包

packet, _ := packetSource.NextPacket()
fmt.Println(packet)

所有包

for packet := range packetSource.Packets() {
    fmt.Println(packet)
}

过滤

默认是将所有捕获的包返回回来,而很多时候我们需要关注某个特定类型的包,这时候就需要设置过滤器。这里可以用 Berkeley Packet Filter 的语法

handle.SetBPFFilter("tcp and port 80")

例子

  • 过滤IP: 10.1.1.3
  • 过滤CIDR: 128.3/16
  • 过滤端口: port 53
  • 过滤主机和端口: host 8.8.8.8 and udp port 53
  • 过滤网段和端口: net 199.16.156.0/22 and port
  • 过滤非本机 Web 流量: (port 80 and port 443) and not host 192.168.0.1

将捕获到的包保存到文件

dumpFile, _ := os.Create("dump.pcap")
defer dumpFile.Close()
//  准备好写入的 Writer
packetWriter := pcapgo.NewWriter(dumpFile)
packetWriter.WriteFileHeader(
    65535,  //  Snapshot length
    layers.LinkTypeEthernet,
)
//  写入包
for packet := range packetSource.Packets() {
    packetWriter.WritePacket(
        packet.Metadata().CaptureInfo,
        packet.Data(),
    )
}

解析包

列出包的层

for _, layer := range packet.Layers() {
    fmt.Println(layer.LayerType())
}

解析 IP 层

ipLayer := packet.Layer(layers.LayerTypeIPv4)
if ipLayer != nil {
    ip, _ := ipLayer.(*layers.IPv4)
    fmt.Println(ip.SrcIP, ip.DstIP)
    fmt.Println(ip.Protocol)
}

解析 TCP 层

tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer != nil {
    tcp, _ := tcpLayer.(*layers.TCP)
    fmt.Println(tcp.SrcPort)
    fmt.Println(tcp.DstPort)
}

常见的包层

packet.LinkLayer()packet.NetworkLayer()packet.TransportLayer()packet.ApplicationLayer()packet.ErrorLayer()

示例,解析redis tcp/ip数据

package main

import (
    "encoding/json"
    "fmt"
    "strings"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

func main() {
    //  获取 libpcap 的版本
    version := pcap.Version()
    fmt.Println(version)

    var device string
    if device = findNetName("127."); device == "" {
        panic("not net is prefix 127.")
    }

    handle, e := pcap.OpenLive(
        device,         // device
        int32(65535),   //  snapshot length
        false,          //  promiscuous mode?
        -1*time.Second, // timeout 负数表示不缓存,直接输出
    )
    if e != nil {
        panic(e.Error())
    }
    defer handle.Close()

    handle.SetBPFFilter("dst port 6379")
    packetSource := gopacket.NewPacketSource(
        handle,
        handle.LinkType(),
    )

    for packet := range packetSource.Packets() {
        //  解析 IP 层
        ipLayer := packet.Layer(layers.LayerTypeIPv4)
        if ipLayer != nil {
            //  解析 TCP 层
            tcpLayer := packet.Layer(layers.LayerTypeTCP)
            if tcpLayer != nil {
                tcp, _ := tcpLayer.(*layers.TCP)
                if len(tcp.Payload) > 0 {
                    ip, _ := ipLayer.(*layers.IPv4)
                    fmt.Printf("%s:%s->%s:%s\n%s\n",
                        ip.SrcIP, tcp.SrcPort,
                        ip.DstIP, tcp.DstPort,
                        string(tcp.Payload))
                }
            } else if errLayer := packet.ErrorLayer(); errLayer != nil {
                fmt.Printf("tcp.err: %v", errLayer)
            }
        } else if errLayer := packet.ErrorLayer(); errLayer != nil {
            fmt.Printf("ip.err: %v", errLayer)
        }
    }

    return
}

func findNetName(prefix string) string {
    //  获取网卡列表
    var devices []pcap.Interface
    devices, _ = pcap.FindAllDevs()
    for _, d := range devices {
        for _, addr := range d.Addresses {
            if ip4 := addr.IP.To4(); ip4 != nil {
                if strings.HasPrefix(ip4.String(), prefix) {
                    data, _ := json.MarshalIndent(d, "", "  ")
                    fmt.Println(string(data))
                    return d.Name
                }
            }
        }
    }
    return ""
}

本机打开redis-server,并bind在6379端口上。

go run main.go
redis-cli -p 6379set a bget a

得到以下输出结果

libpcap version 1.8.1 -- Apple version 79.20.1
{
  "Name": "lo0",
  "Description": "",
  "Flags": 7,
  "Addresses": [
    {
      "IP": "127.0.0.1",
      "Netmask": "/wAAAA==",
      "Broadaddr": "",
      "P2P": ""
    },
    {
      "IP": "::1",
      "Netmask": "/////////////////////w==",
      "Broadaddr": "",
      "P2P": ""
    },
    {
      "IP": "fe80::1",
      "Netmask": "//////////8AAAAAAAAAAA==",
      "Broadaddr": "",
      "P2P": ""
    }
  ]
}
127.0.0.1:60484->127.0.0.1:6379(redis)
*3
$3
set
$1
a
$1
b

127.0.0.1:60484->127.0.0.1:6379(redis)
*2
$3
get
$1
a