Golang编程语言知识介绍


  • 首页

  • todo

  • 思考

  • life

  • food

  • OS

  • lua

  • redis

  • Golang

  • C

  • TCP/IP

  • ebpf

  • p4

  • OpenVPN

  • IPSec

  • L2TP

  • DNS

  • distributed

  • web

  • OpenWRT

  • 运维

  • Git

  • 鸟哥的私房菜

  • IT杂谈

  • 投资

  • About Me

  • 友情链接

  • FTP

  • 搜索
close

golang使用gopacket包进行数据包捕获,注入和分析

时间: 2023-12-13   |   分类: go     |   阅读: 2178 字 ~5分钟

使用 golang 实现网络抓包是非常容易的,可以使用谷歌的包 github.com/google/gopacket。由于 gopacket 构建在 libp­cap 之上,我强烈建议您了解该库的工作原理。您可以在 C 中学习如何使用 libp­cap 进行更深入的了解。 1.libp­cap

gopacket 是基于 libp­cap(数据包捕获函数库)的,该库提供的 C 函数接口用于捕捉经过指定网络接口的数据包,该接口应该是被设为混杂模式。著名的软件 TCP­DUMP 就是在 Libp­cap 的基础上开发而成的。Libp­cap 提供的接口函数实现和封装了与数据包截获有关的过程。Libp­cap 可以在绝大多数 Linux 平台上运行。 主要有以下功能:

  • 数据包捕获:捕获流经网卡的原始数据包
  • 自定义数据包发送:构造任何格式的原始数据包
  • 流量采集与统计:采集网络中的流量信息
  • 规则过滤:提供自带规则过滤功能,按需要选择过滤规则
  1. 先决条件
# Get the gopacket package from GitHub
go get github.com/google/gopacket
# Pcap dev headers might be necessary
sudo apt-get install libpcap-dev
  1. 获取所有的网络设备信息
package main

import (
    "fmt"
    "log"

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

func main() {
    // Find all devices
    devices, err := pcap.FindAllDevs()
    if err != nil {
        log.Fatal(err)
    }

    // Print device information
    fmt.Println("Devices found:")
    for _, d := range devices {
        fmt.Println("\nName: ", d.Name)
        fmt.Println("Description: ", d.Description)
        fmt.Println("Devices addresses: ", d.Addresses)

        for _, address := range d.Addresses {
            fmt.Println("- IP address: ", address.IP)
            fmt.Println("- Subnet mask: ", address.Netmask)
        }
    }
}
  1. 打开设备实时捕捉
package main

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

var (
    device       string = "你的网卡名称,可以通过上面3获取所有设备获取到(Name)"
    snapshot_len int32  = 1024
    promiscuous  bool   = false
    err          error
    timeout      time.Duration = 30 * time.Second
    handle       pcap.Handle
)

func main() {
    // Open device
    handle, err := pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
    if err != nil {
        log.Fatal(err)
    }
    defer handle.Close()

    // Use the handle as a packet source to process all packets
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    for packet := range packetSource.Packets() {
        // Process packet here
        fmt.Println(packet)
    }
}
  1. 抓取结果保存为 pcap 格式文件
package main

import (
    "fmt"
    "os"
    "time"

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

var (
    //deviceName  string = "eth0"
    deviceName string = "\\Device\\NPF_{9A3E6D9C-B24F-4CD0-9C4A-E7ACF6F09ABF}"
    snapshotLen int32  = 1024
    promiscuous bool   = false
    err         error
    timeout     time.Duration = 1 * time.Second
    handle      pcap.Handle
    packetCount int = 0
)

func main() {
    // Open output pcap file and write header
    f, _ := os.Create("test.pcap")
    w := pcapgo.NewWriter(f)
    w.WriteFileHeader(uint32(snapshotLen), layers.LinkTypeEthernet)
    defer f.Close()

    // Open the device for capturing
    handle, err := pcap.OpenLive(deviceName, snapshotLen, promiscuous, timeout)
    if err != nil {
        fmt.Printf("Error opening device %s: %v", deviceName, err)
        os.Exit(1)
    }
    defer handle.Close()

    // Start processing packets
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    for packet := range packetSource.Packets() {
        // Process packet here
        fmt.Println(packet)
        w.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
        packetCount++

        // Only capture 100 and then stop
        if packetCount > 100 {
            break
        }
    }
}
  1. 读取 pcap 格式文件来查看分析网络数据包
package main

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

var (
    pcapFile string = "test.pcap"
    handle   *pcap.Handle
    err      error
)

func main() {
    // Open file instead of device
    handle, err = pcap.OpenOffline(pcapFile)
    if err != nil {
        log.Fatal(err)
    }
    defer handle.Close()

    // Loop through packets in file
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    for packet := range packetSource.Packets() {
        fmt.Println(packet)
    }
}
  1. 设置过滤器

只抓取 tcp 协议 80 端口的数据 fil­ter 没看懂是怎么写的

package main

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

var (
    //device       string = "eth0"
    device string = "\\Device\\NPF_{9A3E6D9C-B24F-4CD0-9C4A-E7ACF6F09ABF}"
    snapshot_len int32  = 1024
    promiscuous  bool   = false
    err          error
    timeout      time.Duration = 30 * time.Second
    handle       pcap.Handle
)

func main() {
    // Open device
    handle, err := pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
    if err != nil {
        log.Fatal(err)
    }
    defer handle.Close()

    // Set filter
    var filter string = "tcp and port 80"
    err = handle.SetBPFFilter(filter)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Only capturing TCP port 80 packets.")

    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    for packet := range packetSource.Packets() {
        // Do something with a packet here.
        fmt.Println(packet)
    }

}
  1. 解码抓取的数据

我们可以使用原始数据包,并且可将其转换为已知格式。它与不同的层兼容,所以我们可以轻松访问以太网,IP 和 TCP 层。lay­ers 包是 Go 库中新增的,在底层 pcap 库中不可用。这是一个令人难以置信的有用的包,它是 gopacket 库的一部分。它允许我们容易地识别包是否包含特定类型的层。该代码示例将显示如何使用 lay­ers 包来查看数据包是以太网,IP 和 TCP,并轻松访问这些头文件中的元素。 查找有效载荷取决于所涉及的所有层。每个协议是不同的,必须相应地计算。这就是 layer 包的魅力所在。 gopacket 的作者花了时间为诸如以太网,IP,UDP 和 TCP 等众多已知层创建了相应类型。有效载荷是应用层的一部分。

package main

import (
    "fmt"
    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
    "log"
    "strings"
    "time"
)

var (
    //device      string = "eth0"
    device string = "\\Device\\NPF_{9A3E6D9C-B24F-4CD0-9C4A-E7ACF6F09ABF}"
    snapshotLen int32  = 1024
    promiscuous bool   = false
    err         error
    timeout     time.Duration = 30 * time.Second
    handle      pcap.Handle
)

func main() {
    // Open device
    handle, err := pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
    if err != nil {
        log.Fatal(err)
    }
    defer handle.Close()

    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    for packet := range packetSource.Packets() {
        printPacketInfo(packet)
    }
}

func printPacketInfo(packet gopacket.Packet) {
    // Let’s see if the packet is an ethernet packet
    ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
    if ethernetLayer != nil {
        fmt.Println("Ethernet layer detected.")
        ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
        fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
        fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
        // Ethernet type is typically IPv4 but could be ARP or other
        fmt.Println("Ethernet type: ", ethernetPacket.EthernetType)
        fmt.Println()
    }

    // Let’s see if the packet is IP (even though the ether type told us)
    ipLayer := packet.Layer(layers.LayerTypeIPv4)
    if ipLayer != nil {
        fmt.Println("IPv4 layer detected.")
        ip, _ := ipLayer.(*layers.IPv4)

        // IP layer variables:
        // Version (Either 4 or 6)
        // IHL (IP Header Length in 32-bit words)
        // TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?),
        // Checksum, SrcIP, DstIP
        fmt.Printf("From %s to %s\n", ip.SrcIP, ip.DstIP)
        fmt.Println("Protocol: ", ip.Protocol)
        fmt.Println()
    }

    // Let’s see if the packet is TCP
    tcpLayer := packet.Layer(layers.LayerTypeTCP)
    if tcpLayer != nil {
        fmt.Println("TCP layer detected.")
        tcp, _ := tcpLayer.(*layers.TCP)

        // TCP layer variables:
        // SrcPort, DstPort, Seq, Ack, DataOffset, Window, Checksum, Urgent
        // Bool flags: FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS
        fmt.Printf("From port %d to %d\n", tcp.SrcPort, tcp.DstPort)
        fmt.Println("Sequence number: ", tcp.Seq)
        fmt.Println()
    }

    // Iterate over all layers, printing out each layer type
    fmt.Println("All packet layers:")
    for _, layer := range packet.Layers() {
        fmt.Println("- ", layer.LayerType())
    }

    // When iterating through packet.Layers() above,
    // if it lists Payload layer then that is the same as
    // this applicationLayer. applicationLayer contains the payload
    applicationLayer := packet.ApplicationLayer()
    if applicationLayer != nil {
        fmt.Println("Application layer/Payload found.")
        fmt.Printf("%s\n", applicationLayer.Payload())

        // Search for a string inside the payload
        if strings.Contains(string(applicationLayer.Payload()), "HTTP") {
            fmt.Println("HTTP found!")
        }
    }

    // Check for errors
    if err := packet.ErrorLayer(); err != nil {
        fmt.Println("Error decoding some part of the packet:", err)
    }
}
  1. 构造发送数据包

这个例子做了几件事情。首先将显示如何使用网络设备发送原始字节。这样就可以像串行连接一样使用它来发送数据。这对于真正的低层数据传输非常有用,但如果您想与应用程序进行交互,您应该构建可以识别该数据包的其他硬件和软件。接下来,它将显示如何使用以太网,IP 和 TCP 层创建一个数据包。一切都是默认空的。要完成它,我们创建另一个数据包,但实际上填写了以太网层的一些 MAC 地址,IPv4 的一些 IP 地址和 TCP 层的端口号。你应该看到如何伪装数据包和仿冒网络设备。TCP 层结构体具有可读取和可设置的 SYN,FIN,ACK 标志。这有助于操纵和模糊 TCP 三次握手,会话和端口扫描。pcap 库提供了一种发送字节的简单方法,但 gopacket 中的图层可帮助我们为多层创建字节结构。

package main

import (
    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
    "log"
    "net"
    "time"
)

var (
    //device       string = "eth0"
    device string = "\\Device\\NPF_{9A3E6D9C-B24F-4CD0-9C4A-E7ACF6F09ABF}"

    snapshot_len int32 = 1024
    promiscuous  bool  = false
    err          error
    timeout      time.Duration = 30 * time.Second
    handle       pcap.Handle
    buffer       gopacket.SerializeBuffer
    options      gopacket.SerializeOptions
)

func main() {
    // Open device
    handle, err := pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
    if err != nil {
        log.Fatal(err)
    }
    defer handle.Close()

    // Send raw bytes over wire
    rawBytes := []byte{10, 20, 30}
    err = handle.WritePacketData(rawBytes)
    if err != nil {
        log.Fatal(err)
    }

    // Create a properly formed packet, just with
    // empty details. Should fill out MAC addresses,
    // IP addresses, etc.
    buffer = gopacket.NewSerializeBuffer()
    gopacket.SerializeLayers(buffer, options,
        &layers.Ethernet{},
        &layers.IPv4{},
        &layers.TCP{},
        gopacket.Payload(rawBytes),
    )
    outgoingPacket := buffer.Bytes()
    // Send our packet
    err = handle.WritePacketData(outgoingPacket)
    if err != nil {
        log.Fatal(err)
    }

    // This time lets fill out some information
    ipLayer := &layers.IPv4{
        SrcIP: net.IP{127, 0, 0, 1},
        DstIP: net.IP{8, 8, 8, 8},
    }
    ethernetLayer := &layers.Ethernet{
        SrcMAC: net.HardwareAddr{0xFF, 0xAA, 0xFA, 0xAA, 0xFF, 0xAA},
        DstMAC: net.HardwareAddr{0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD},
    }
    tcpLayer := &layers.TCP{
        SrcPort: layers.TCPPort(4321),
        DstPort: layers.TCPPort(80),
    }
    // And create the packet with the layers
    buffer = gopacket.NewSerializeBuffer()
    gopacket.SerializeLayers(buffer, options,
        ethernetLayer,
        ipLayer,
        tcpLayer,
        gopacket.Payload(rawBytes),
    )
    outgoingPacket = buffer.Bytes()
}

以上内容转载自本链接,若需删除请联系站长

相关的连接:

  • Golanggopacket 初体验
#go#
xl2tpd和pppd 服务器的搭建和配置
thrift 之 go 入门 Getting Started with Thrift Go
shankusu2017@gmail.com

shankusu2017@gmail.com

日志
分类
标签
GitHub
© 2009 - 2025
粤ICP备2021068940号-1 粤公网安备44011302003059
0%