Skip to content

GoPacket

date
2023-09-15 21:23:28

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

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

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

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

应用场景:

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

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

  • npcap:https://nmap.org/npcap
  • libpcap:https://www.tcpdump.org

库安装:

go get github.com/google/gopacket

抓取数据包

获取网络设备

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
}

实时抓包

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")

创建数据包源:

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

读取一次数据包:

packet, _ := packetSource.NextPacket()

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

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

写入读取

读取 pcap 文件:

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())
}

分析某层的数据:

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}))

发送

handle.WritePacketData(buffer.Bytes())

参考链接