[已解决] Golang中使用Gopacket/pcap处理Windows设备名称的问题

[已解决] Golang中使用Gopacket/pcap处理Windows设备名称的问题 编辑:帖子已完全重写。

我仍然想感谢GoLangBridge创建并培育了这个社区。

我已经简化了我的代码(技术上主要是从John Leon在GopherCon 2016的演讲中借鉴了代码,并做了一个小改动,使用net包来获取接口)。

现在它只尝试打开接口并捕获一个数据包。

package main

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

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

var (
	snapshotLength int32         = 65535
	promiscuous    bool          = false
	timeout        time.Duration = -1 * time.Second
	err            error
	handle         *pcap.Handle
)

func main() {
	device, err := net.InterfaceByName("Ethernet")
	fmt.Println(device)
	// 打开设备
	handle, err = pcap.OpenLive(device.Name, snapshotLength, promiscuous, timeout)
	if err != nil {
		log.Fatal("OpenLive Call: ", err)
	}
	defer handle.Close()

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

	// 获取下一个数据包
	packet, err := packetSource.NextPacket()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(packet)
}

这是我的输出。 &{5 1500 Ethernet 00:d8:61:33:8e:84 up|broadcast|multicast} 2019/10/20 14:24:19 OpenLive Call: Ethernet: Error opening adapter: The system cannot find the device specified. (20) exit status 1

Go版本:1.13.3 npcap版本:0.9983(Wireshark目前与此安装配合工作正常) Windows 10

以管理员身份运行Powershell和cmd。

看起来我的问题出在npcap或Windows上,但我卡住了。有没有进一步排查此问题的建议?


更多关于[已解决] Golang中使用Gopacket/pcap处理Windows设备名称的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

问题已解决。我保留此内容而非编辑,以防有人遇到同样的问题并搜索到此结果。考虑到我搜索时找到的信息很少,这很有可能。

问题出在我尝试使用的设备名称上。

第一个问题,我将作为bug上报,即net.Interfaces()使用显示名称作为Name属性。这可能是故意的。如果是这样,那么bug就在arpscan.go示例中(https://github.com/google/gopacket/blob/master/examples/arpscan/arpscan.go),该示例在使用pcap.OpenLive()初始化句柄时,将此属性用作设备名称。

第二个问题是,Windows是一个糟糕的操作系统,它没有提供任何方法(至少我没找到)来获取实际的接口名称。PowerShell的get-netadapter命令接近目标。

get-netadapter | where {$_.Name -eq "Ethernet"} | Select-Object -Property *

其中"Ethernet"是我关心的适配器的显示名称,它给出了:

DeviceName  : \Device{13044533-0543-4AF5-9E3C-85EBBC7C04BB}

以及其他一些包含GUID的属性。

然而,这并不是我们需要的名称。

ipconfig本应提供,但毫无用处。

getmac /fo csv /v 给了我:

\Device\Tcpip_{13044533-0543-4AF5-9E3C-85EBBC7C04BB}

仍然不对。

老实说,我仍然不知道从每台Windows机器上获取实际设备名称的一致方法,但我注意到我查看过的其他一些系统的名称格式是:

\Device\NPF_{13044533-0543-4AF5-9E3C-85EBBC7C04BB}

(注意NPF_,它在我查看的这台系统上的任何地方都没有出现)

所以,在绝望中,我尝试了一下。这就是今天在我正在使用的这台电脑上对我有效的方法。

不幸的是,如果没有办法从诸如net.Interfaces()net.InterfaceByName()net.InterfaceByIndex()之类的东西中获取这个名称,就很难构建一个能在非为其量身定制的机器上运行的程序。

我归咎于Windows,但我希望谷歌能给我们提供绕过它的工具。

更多关于[已解决] Golang中使用Gopacket/pcap处理Windows设备名称的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题在于Windows设备名称的格式。在Windows上,pcap.OpenLive()需要的是npcap的设备名称格式,而不是标准的网络接口名称。

以下是修正后的代码:

package main

import (
	"fmt"
	"log"
	"time"

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

var (
	snapshotLength int32         = 65535
	promiscuous    bool          = false
	timeout        time.Duration = -1 * time.Second
)

func main() {
	// 获取所有网络设备
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal("FindAllDevs error:", err)
	}

	// 查找名为"Ethernet"的设备
	var deviceName string
	for _, device := range devices {
		if device.Name == "\\Device\\NPF_{你的设备GUID}" || device.Description == "Ethernet" {
			deviceName = device.Name
			fmt.Printf("Found device: %s (Description: %s)\n", device.Name, device.Description)
			break
		}
	}

	if deviceName == "" {
		// 列出所有可用设备供参考
		fmt.Println("Available devices:")
		for _, device := range devices {
			fmt.Printf("Name: %s, Description: %s\n", device.Name, device.Description)
		}
		log.Fatal("Device 'Ethernet' not found")
	}

	// 打开设备 - 使用pcap返回的设备名称
	handle, err := pcap.OpenLive(deviceName, snapshotLength, promiscuous, timeout)
	if err != nil {
		log.Fatal("OpenLive error:", err)
	}
	defer handle.Close()

	fmt.Printf("Successfully opened device: %s\n", deviceName)

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

	// 设置超时,避免无限等待
	timeoutChan := time.After(5 * time.Second)
	
	select {
	case packet := <-packetSource.Packets():
		fmt.Printf("Captured packet: %v\n", packet)
	case <-timeoutChan:
		fmt.Println("Timeout: No packet captured within 5 seconds")
	}
}

或者,更简单的版本,直接使用pcap查找设备:

package main

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

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

func main() {
	// 获取所有设备
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	// 查找包含"Ethernet"描述的接口
	var targetDevice pcap.Interface
	for _, device := range devices {
		if strings.Contains(strings.ToLower(device.Description), "ethernet") {
			targetDevice = device
			break
		}
	}

	if targetDevice.Name == "" {
		log.Fatal("Ethernet device not found")
	}

	// 使用pcap的设备名称打开
	handle, err := pcap.OpenLive(targetDevice.Name, 65535, false, -1*time.Second)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	fmt.Printf("Listening on %s (%s)\n", targetDevice.Description, targetDevice.Name)

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		fmt.Println(packet)
		break // 只捕获一个包
	}
}

关键点:

  1. Windows上npcap的设备名称格式为\Device\NPF_{GUID},不是简单的Ethernet
  2. 使用pcap.FindAllDevs()获取正确的设备名称
  3. 通过设备描述(Description)来匹配"Ethernet",然后使用返回的Name字段

运行代码前,先查看实际的设备名称:

devices, _ := pcap.FindAllDevs()
for _, d := range devices {
    fmt.Printf("Name: %s, Description: %s\n", d.Name, d.Description)
}
回到顶部