Golang GoPacket 库简单使用

介绍

GoPacket 是对 libpcap 和 npcap 的 go 封装,其功能就是抓包。

在分析 ksubdomain 时遇到这个库,简单看一下用法。

像 wireshark 这种抓包软件其底层使用的就是 npcap,所以说 GoPacket 库的功能也就类似这种。

项目地址:https://github.com/google/gopacket

应用场景:

  1. 网络流量分析
  2. 伪造数据包
  3. 离线 pcap 文件的读取

在使用 gopacket 包时,首先要确保在 windows 平台下安装了 npcapwinpcap,或者是在 linux 平台下安装了 libpcap 库。

库安装:

1
go get github.com/google/gopacket

抓取数据包

获取网络设备

1
2
3
4
5
6
7
8
9
10
11
func main() {
devs, err := pcap.FindAllDevs()
if err != nil {
return
}
for _, dev := range devs {
for _, addr := range dev.Addresses {
fmt.Println(dev.Name, "=>", addr.IP.String())
}
}
}

设备信息:

1
2
3
4
5
6
7
// Interface describes a single network interface on a machine.
type Interface struct {
Name string
Description string
Flags uint32
Addresses []InterfaceAddress
}

Addresses

1
2
3
4
5
6
type InterfaceAddress struct {
IP net.IP
Netmask net.IPMask // Netmask may be nil if we were unable to retrieve it.
Broadaddr net.IP // Broadcast address for this IP may be nil
P2P net.IP // P2P destination address for this IP may be nil
}

实时抓包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var (
device = "\\Device\\NPF_{657E0D44-808F-42EA-82B5-AFAFF6AE862B}" // 设备名称
snapshot_len int32 = 1024 // 读取数据包的最大长度
promiscuous = false // 混杂模式 ( 接受目的地不是本机的包 )
timeout = -1 * time.Second // 抓包超时, -1 表示立即刷新数据包
err error
)

func main() {
// 打开网络设备
handle, err := pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
if err != nil {
return
}
defer handle.Close()
handle.SetBPFFilter("dns")
// 创建数据包源, handle.LinkType() => 以太网链路
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
// 读取数据包
for packet := range packetSource.Packets() {
fmt.Println(packet.String())
}
}

设置过滤器:handle.SetBPFFilter("dns")

创建数据包源:

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

读取一次数据包:

1
packet, _ := packetSource.NextPacket()

获得一个读取所有数据包的通道:

1
2
3
for packet := range packetSource.Packets() {
fmt.Println(packet.String())
}

写入读取

读取 pcap 文件:

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

写入 pcap 文件:

1
2
3
4
5
6
7
dumpFile, _ := os.Create("dump.pcap")
defer dumpFile.Close()
packetWriter := pcapgo.NewWriter(dumpFile)
packet := packetSource.Packets()
for packet := range packet{
packetWriter.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
}

数据包解码

Layers 包是 gopacket 的 Go 库中的新功能,在底层 libpcap 库中不存在。它是 gopacket 库的非常有用的一部分。它允许我们轻松地识别数据包是否包含特定类型的层。

1
2
3
4
5
for _, layer := range packet.Layers() {
fmt.Println(layer.LayerType().String()) // 当前层的类型 (TCP/DNS/UDP...)
fmt.Println(layer.LayerContents())
fmt.Println(layer.LayerPayload())
}

分析某层的数据:

1
2
3
4
5
6
7
8
9
10
11
12
for packet := range packetSource.Packets() {
// 判断数据包是否为 dns 数据包
dnsLayer := packet.Layer(layers.LayerTypeDNS)
if dnsLayer != nil {
// 断言为 DNS 类型
dns := dnsLayer.(*layers.DNS)
for _, q := range dns.Questions {
// 遍历 Questions 输出 Name
fmt.Println(string(q.Name))
}
}
}

创建与发送

创建

创建一个新的序列化缓冲区;然后把所有层序列化到缓冲区中。

1
2
3
buffer := gopacket.NewSerializeBuffer()
options := gopacket.SerializeOptions{}
gopacket.SerializeLayers(buffer, options, &layers.Ethernet{}, &layers.IPv4{}, &layers.TCP{}, gopacket.Payload([]byte{65, 66, 67}))

发送

1
handle.WritePacketData(buffer.Bytes())

参考链接


Golang GoPacket 库简单使用
https://liancccc.github.io/2023/09/15/技术/开发/GoPacket/
作者
守心
发布于
2023年9月15日
许可协议