Golang抓包:实现网络数据包捕获与分析_golang pcap,996页阿里Golang面试真题解析火爆全网

66 阅读5分钟
package main

import (
	"fmt"
	"net"
)

func main() {
	interfaces, err := net.Interfaces()
	if err != nil {
		fmt.Println("Failed to get interfaces:", err)
		return
	}

	fmt.Println("Network interfaces:")
	for \_, iface := range interfaces {
		fmt.Println("- Name:", iface.Name)
	}
}

执行上述代码,会输出计算机上所有的网络设备名称。

可以通过以下代码来打开一个网络设备:

package main

import (
	"fmt"
	"log"
	"net"

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

func main() {
	device := "eth0" // 要打开的网络设备名称

	handle, err := pcap.OpenLive(device, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	fmt.Println("Device opened:", device)
}

在上述代码中,我们使用pcap.OpenLive函数来打开一个网络设备。该函数接受设备名称、数据包最大长度、是否要抓取数据包的全部内容以及超时时间作为参数。如果打开成功,将返回一个pcap.Handle对象,可以用于后续的数据包捕获和分析。

捕获数据包

在打开网络设备之后,我们可以开始捕获数据包。可以通过以下代码来捕获指定数量的数据包:

package main

import (
	"fmt"
	"log"
	"net"
	"time"

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

func main() {
	device, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	handle, err := pcap.OpenLive(device[0].Name, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	packetCount := 0
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		packetCount++
		fmt.Println("Packet:", packetCount)

		// TODO: 进行数据包分析
		time.Sleep(1 \* time.Second) // 仅用于示例,避免数据包流量过大
	}
}

上述代码中,我们使用gopacket.NewPacketSource函数将打开的设备与pcap.Handle对象关联起来,然后使用PacketSourcePackets方法来获取捕获到的数据包。每次从Packets方法获取到一个数据包,我们都会对其进行处理,即打印出数据包的序号(用于示例,实际应用中可能需要根据需求进行其他操作)。

数据包分析

在捕获到数据包后,我们可以对其进行分析并提取所需的信息。gopacket库提供了丰富的工具和功能,用于数据包分析。

以下是一些常见的数据包分析方法:

解析以太网帧
ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
if ethernetLayer != nil {
    ethernetPacket, \_ := ethernetLayer.(\*layers.Ethernet)
    fmt.Println("Ethernet source MAC:", ethernetPacket.SrcMAC)
    fmt.Println("Ethernet destination MAC:", ethernetPacket.DstMAC)
    fmt.Println("Ethernet type:", ethernetPacket.EthernetType)
}

以上代码演示了如何解析以太网帧中的源MAC地址、目的MAC地址和以太网类型。

解析IP包
ipLayer := packet.Layer(layers.LayerTypeIPv4)
if ipLayer != nil {
    ipPacket, \_ := ipLayer.(\*layers.IPv4)
    fmt.Println("IP version:", ipPacket.Version)
    fmt.Println("IP source address:", ipPacket.SrcIP)
    fmt.Println("IP destination address:", ipPacket.DstIP)
    fmt.Println("IP protocol:", ipPacket.Protocol)
}

以上代码演示了如何解析IPv4包中的版本、源IP地址、目的IP地址和协议。

解析TCP包
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer != nil {
    tcpPacket, \_ := tcpLayer.(\*layers.TCP)
    fmt.Println("TCP source port:", tcpPacket.SrcPort)
    fmt.Println("TCP destination port:", tcpPacket.DstPort)
    fmt.Println("TCP sequence number:", tcpPacket.Sequence)
    fmt.Println("TCP acknowledgment number:", tcpPacket.Acknowledgment)
    fmt.Println("TCP flags:", tcpPacket.Flags)
}

以上代码演示了如何解析TCP包中的源端口、目的端口、序列号、确认号和标志位。

解析UDP包
udpLayer := packet.Layer(layers.LayerTypeUDP)
if udpLayer != nil {
    udpPacket, \_ := udpLayer.(\*layers.UDP)
    fmt.Println("UDP source port:", udpPacket.SrcPort)
    fmt.Println("UDP destination port:", udpPacket.DstPort)
}

以上代码演示了如何解析UDP包中的源端口和目的端口。

解析应用层协议

在数据包的应用层有各种各样的协议,如HTTP、DNS等。gopacket库提供了根据协议类型解析数据包的方法。以下是解析HTTP协议的示例代码:

httpLayer := packet.Layer(layers.LayerTypeHTTP)
if httpLayer != nil {
    httpPacket, \_ := httpLayer.(\*layers.HTTP)
    fmt.Println("HTTP method:", httpPacket.Method)
    fmt.Println("HTTP host:", httpPacket.Host)
    fmt.Println("HTTP user-agent:", httpPacket.UserAgent)
}

以上代码演示了如何解析HTTP包中的方法、主机和用户代理信息。

示例:捕获HTTP请求

现在,我们将结合以上的知识来实现一个简单的示例:捕获HTTP请求,并提取请求的URL和请求头信息。

package main

import (
	"fmt"
	"log"
	"net"
	"strings"
	"time"

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

func main() {
	device, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	handle, err := pcap.OpenLive(device[0].Name, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
		if ethernetLayer != nil {
			ethernetPacket, \_ := ethernetLayer.(\*layers.Ethernet)

			ipLayer := packet.Layer(layers.LayerTypeIPv4)
			if ipLayer != nil {
				ipPacket, \_ := ipLayer.(\*layers.IPv4)

				tcpLayer := packet.Layer(layers.LayerTypeTCP)
				if tcpLayer != nil {
					tcpPacket, \_ := tcpLayer.(\*layers.TCP)

					httpLayer := packet.Layer(layers.LayerTypeHTTP)
					if httpLayer != nil {
						httpPacket, \_ := httpLayer.(\*layers.HTTP)

						fmt.Println("Source MAC:", ethernetPacket.SrcMAC)
						fmt.Println("Destination MAC:", ethernetPacket.DstMAC)

						fmt.Println("Source IP:", ipPacket.SrcIP)
						fmt.Println("Destination IP:", ipPacket.DstIP)

						fmt.Println("Source Port:", tcpPacket.SrcPort)
						fmt.Println("Destination Port:", tcpPacket.DstPort)

						fmt.Println("HTTP Method:", httpPacket.Method)
						fmt.Println("HTTP Host:", httpPacket.Host)

						headers := strings.Split(string(httpPacket.Headers), "\r\n")
						for \_, header := range headers {
							fmt.Println("HTTP Header:", header)
						}
						
						fmt.Println("--------")
					}
				}
			}
		}

		time.Sleep(1 \* time.Second) // 仅用于示例,避免数据包流量过大
	}
}

以上示例代码中,我们使用了嵌套的条件语句来逐级解析数据包的各个层级,并提取所需的信息。其中,我们关注以太网帧、IPv4包、TCP包和HTTP协议,提取了包括源MAC地址、目的MAC地址、源IP地址、目的IP地址、源端口、目的端口、HTTP方法、主机和请求头信息等。

案例

案例一:统计流量

我们可以使用抓包技术来统计特定端口的流量。以下示例代码演示了如何捕获HTTP流量,并统计总共传输的数据量:

package main

import (
	"fmt"
	"log"
	"net"
	"strings"
	"time"

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

func main() {
	device, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	handle, err := pcap.OpenLive(device[0].Name, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	totalBytes := 0
	startTime := time.Now()
	for packet := range packetSource.Packets() {
		ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
		if ethernetLayer != nil {
			ethernetPacket, \_ := ethernetLayer.(\*layers.Ethernet)

			ipLayer := packet.Layer(layers.LayerTypeIPv4)
			if ipLayer != nil {
				ipPacket, \_ := ipLayer.(\*layers.IPv4)

				tcpLayer := packet.Layer(layers.LayerTypeTCP)
				if tcpLayer != nil {
					tcpPacket, \_ := tcpLayer.(\*layers.TCP)

					httpLayer := packet.Layer(layers.LayerTypeHTTP)
					if httpLayer != nil {
						httpPacket, \_ := httpLayer.(\*layers.HTTP)

						if tcpPacket.SrcPort.String() == "80" || tcpPacket.DstPort.String() == "80" {
							totalBytes += len(packet.Data())
						}
					}
				}
			}
		}

		elapsed := time.Since(startTime)
		if elapsed.Seconds() >= 10 {
			fmt.Printf("Total Bytes: %d\n", totalBytes)
			break
		}
	}
}

上述代码中,我们在数据包捕获的过程中判断源端口或目标端口是否为80(HTTP默认端口),如果是则统计这些HTTP流量的数据量。我们使用一个计时器来控制统计的时间,示例中设置为10秒。随着流量的捕获,我们将统计的总数据量打印出来。

案例二:HTTP请求重放

我们可以抓取HTTP请求,并将其重放到目标服务器。以下示例代码演示了如何捕获HTTP请求,并将其重放到指定的目标服务器:

package main

import (
	"log"
	"net/http"
	"strings"

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

func main() {
	device, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	handle, err := pcap.OpenLive(device[0].Name, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
		if ethernetLayer != nil {


![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f8f377cfffd14575bee834a9afbbd8c2~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770647678&x-signature=cRNTsFT3D%2BzHh2dbvFwyCSsd5TI%3D)
![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/209983bba52540708f8f8be7451023f8~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770647678&x-signature=hBtHYF%2FgMuUH5as7K%2F0KSrq7Qpk%3D)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://gitee.com/vip204888)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**