需要用到的包
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