golang使用gopacket包进行数据包捕获,注入和分析
来源:互联网 发布:来自新世界知乎 编辑:程序博客网 时间:2024/05/22 06:59
使用golang实现网络抓包是非常容易的,可以使用谷歌的包github.com/google/gopacket。由于gopacket构建在libpcap之上,我强烈建议您了解该库的工作原理。您可以在C中学习如何使用libpcap进行更深入的了解。
1.libpcap
gopacket是基于libpcap(数据包捕获函数库)的,该库提供的C函数接口用于捕捉经过指定网络接口的数据包,该接口应该是被设为混杂模式。
著名的软件TCPDUMP就是在Libpcap的基础上开发而成的。Libpcap提供的接口函数实现和封装了与数据包截获有关的过程。Libpcap可以在绝大多数Linux平台上运行。
主要有以下功能:
数据包捕获:捕获流经网卡的原始数据包
自定义数据包发送:构造任何格式的原始数据包
流量采集与统计:采集网络中的流量信息
规则过滤:提供自带规则过滤功能,按需要选择过滤规则
著名的软件TCPDUMP就是在Libpcap的基础上开发而成的。Libpcap提供的接口函数实现和封装了与数据包截获有关的过程。Libpcap可以在绝大多数Linux平台上运行。
主要有以下功能:
数据包捕获:捕获流经网卡的原始数据包
自定义数据包发送:构造任何格式的原始数据包
流量采集与统计:采集网络中的流量信息
规则过滤:提供自带规则过滤功能,按需要选择过滤规则
2.先决条件
# Get the gopacket package from GitHubgo get github.com/google/gopacket# Pcap dev headers might be necessarysudo apt-get install libpcap-dev
3.获取所有的网络设备信息
package mainimport ( "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 _, device := range devices { fmt.Println("\nName: ", device.Name) fmt.Println("Description: ", device.Description) fmt.Println("Devices addresses: ", device.Description) for _, address := range device.Addresses { fmt.Println("- IP address: ", address.IP) fmt.Println("- Subnet mask: ", address.Netmask) } }}
4.打开设备实时捕捉
package mainimport ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/pcap" "log" "time")var ( device string = "eth0" 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) }}
5.抓取结果保存为pcap格式文件
要写一个pcap格式的文件,我们必须使用gapacket / pcapgo包。这是一个Writer接口和两个有用的函数:WriteFileHeader()和WritePacket()
package mainimport ("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"snapshotLen int32 = 1024promiscuous bool = falseerr errortimeout time.Duration = -1 * time.Secondhandle *pcap.HandlepacketCount int = 0)func main() {// Open output pcap file and write header f, _ := os.Create("test.pcap")w := pcapgo.NewWriter(f)w.WriteFileHeader(snapshotLen, layers.LinkTypeEthernet)defer f.Close()// Open the device for capturinghandle, 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 packetspacketSource := gopacket.NewPacketSource(handle, handle.LinkType())for packet := range packetSource.Packets() {// Process packet herefmt.Println(packet)w.WritePacket(packet.Metadata().CaptureInfo, packet.Data())packetCount++// Only capture 100 and then stopif packetCount > 100 {break}}}
6.读取pcap格式文件来查看分析网络数据包
我们不用打开一个设备进行实时捕获,也可以打开pcap文件进行离线检查。您可以使用tcpdump创建要使用的测试文件。
# Capture packets to test.pcap filesudo tcpdump -w test.pcap
package main// Use tcpdump to create a test file// tcpdump -w test.pcap// or use the example above for writing pcap filesimport ( "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) }}
7.设置过滤器
只抓取tcp协议80端口的数据
package mainimport ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/pcap" "log" "time")var ( device string = "eth0" 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) }}
8.解码抓取的数据
我们可以使用原始数据包,并且可将其转换为已知格式。它与不同的层兼容,所以我们可以轻松访问以太网,IP和TCP层。layers包是Go库中新增的,在底层pcap库中不可用。这是一个令人难以置信的有用的包,它是gopacket库的一部分。它允许我们容易地识别包是否包含特定类型的层。该代码示例将显示如何使用layers包来查看数据包是以太网,IP和TCP,并轻松访问这些头文件中的元素。
查找有效载荷取决于所涉及的所有层。每个协议是不同的,必须相应地计算。这就是layer包的魅力所在。 gopacket的作者花了时间为诸如以太网,IP,UDP和TCP等众多已知层创建了相应类型。有效载荷是应用层的一部分。
查找有效载荷取决于所涉及的所有层。每个协议是不同的,必须相应地计算。这就是layer包的魅力所在。 gopacket的作者花了时间为诸如以太网,IP,UDP和TCP等众多已知层创建了相应类型。有效载荷是应用层的一部分。
package mainimport ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "log" "strings" "time")var ( device string = "eth0" 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) }}
8.构造发送数据包
这个例子做了几件事情。首先将显示如何使用网络设备发送原始字节。这样就可以像串行连接一样使用它来发送数据。这对于真正的低层数据传输非常有用,但如果您想与应用程序进行交互,您应该构建可以识别该数据包的其他硬件和软件。接下来,它将显示如何使用以太网,IP和TCP层创建一个数据包。一切都是默认空的。要完成它,我们创建另一个数据包,但实际上填写了以太网层的一些MAC地址,IPv4的一些IP地址和TCP层的端口号。你应该看到如何伪装数据包和仿冒网络设备。TCP层结构体具有可读取和可设置的SYN,FIN,ACK标志。这有助于操纵和模糊TCP三次握手,会话和端口扫描。pcap库提供了一种发送字节的简单方法,但gopacket中的图层可帮助我们为多层创建字节结构。
package mainimport ( "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "log" "net" "time")var ( device string = "eth0" 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()}
9.更多的解码/构造数据包的例子
package mainimport ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers")func main() { // If we don't have a handle to a device or a file, but we have a bunch // of raw bytes, we can try to decode them in to packet information // NewPacket() takes the raw bytes that make up the packet as the first parameter // The second parameter is the lowest level layer you want to decode. It will // decode that layer and all layers on top of it. The third layer // is the type of decoding: default(all at once), lazy(on demand), and NoCopy // which will not create a copy of the buffer // Create an packet with ethernet, IP, TCP, and payload layers // We are creating one we know will be decoded properly but // your byte source could be anything. If any of the packets // come back as nil, that means it could not decode it in to // the proper layer (malformed or incorrect packet type) payload := []byte{2, 4, 6} options := gopacket.SerializeOptions{} buffer := gopacket.NewSerializeBuffer() gopacket.SerializeLayers(buffer, options, &layers.Ethernet{}, &layers.IPv4{}, &layers.TCP{}, gopacket.Payload(payload), ) rawBytes := buffer.Bytes() // Decode an ethernet packet ethPacket := gopacket.NewPacket( rawBytes, layers.LayerTypeEthernet, gopacket.Default, ) // with Lazy decoding it will only decode what it needs when it needs it // This is not concurrency safe. If using concurrency, use default ipPacket := gopacket.NewPacket( rawBytes, layers.LayerTypeIPv4, gopacket.Lazy, ) // With the NoCopy option, the underlying slices are referenced // directly and not copied. If the underlying bytes change so will // the packet tcpPacket := gopacket.NewPacket( rawBytes, layers.LayerTypeTCP, gopacket.NoCopy, ) fmt.Println(ethPacket) fmt.Println(ipPacket) fmt.Println(tcpPacket)}
10.自定义layer
下一个程序将显示如何创建自己的layer。构建gopacket layer包不包含的协议。如果您要创建自己的l33t协议,甚至不使用TCP / IP或以太网,这是很有用的。
package mainimport ( "fmt" "github.com/google/gopacket")// Create custom layer structuretype CustomLayer struct { // This layer just has two bytes at the front SomeByte byte AnotherByte byte restOfData []byte}// Register the layer type so we can use it// The first argument is an ID. Use negative// or 2000+ for custom layers. It must be uniquevar CustomLayerType = gopacket.RegisterLayerType( 2001, gopacket.LayerTypeMetadata{ "CustomLayerType", gopacket.DecodeFunc(decodeCustomLayer), },)// When we inquire about the type, what type of layer should// we say it is? We want it to return our custom layer typefunc (l CustomLayer) LayerType() gopacket.LayerType { return CustomLayerType}// LayerContents returns the information that our layer// provides. In this case it is a header layer so// we return the header informationfunc (l CustomLayer) LayerContents() []byte { return []byte{l.SomeByte, l.AnotherByte}}// LayerPayload returns the subsequent layer built// on top of our layer or raw payloadfunc (l CustomLayer) LayerPayload() []byte { return l.restOfData}// Custom decode function. We can name it whatever we want// but it should have the same arguments and return value// When the layer is registered we tell it to use this decode functionfunc decodeCustomLayer(data []byte, p gopacket.PacketBuilder) error { // AddLayer appends to the list of layers that the packet has p.AddLayer(&CustomLayer{data[0], data[1], data[2:]}) // The return value tells the packet what layer to expect // with the rest of the data. It could be another header layer, // nothing, or a payload layer. // nil means this is the last layer. No more decoding // return nil // Returning another layer type tells it to decode // the next layer with that layer's decoder function // return p.NextDecoder(layers.LayerTypeEthernet) // Returning payload type means the rest of the data // is raw payload. It will set the application layer // contents with the payload return p.NextDecoder(gopacket.LayerTypePayload)}func main() { // If you create your own encoding and decoding you can essentially // create your own protocol or implement a protocol that is not // already defined in the layers package. In our example we are just // wrapping a normal ethernet packet with our own layer. // Creating your own protocol is good if you want to create // some obfuscated binary data type that was difficult for others // to decode // Finally, decode your packets: rawBytes := []byte{0xF0, 0x0F, 65, 65, 66, 67, 68} packet := gopacket.NewPacket( rawBytes, CustomLayerType, gopacket.Default, ) fmt.Println("Created packet out of raw bytes.") fmt.Println(packet) // Decode the packet as our custom layer customLayer := packet.Layer(CustomLayerType) if customLayer != nil { fmt.Println("Packet was successfully decoded with custom layer decoder.") customLayerContent, _ := customLayer.(*CustomLayer) // Now we can access the elements of the custom struct fmt.Println("Payload: ", customLayerContent.LayerPayload()) fmt.Println("SomeByte element:", customLayerContent.SomeByte) fmt.Println("AnotherByte element:", customLayerContent.AnotherByte) }}
11.更快地解码数据包
如果我们知道我们要预期的得到的层,我们可以使用现有的结构来存储分组信息,而不是为每个需要时间和内存的分组创建新的结构。使用DecodingLayerParser更快。就像编组和解组数据一样。
package mainimport ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "log" "time")var ( device string = "eth0" snapshot_len int32 = 1024 promiscuous bool = false err error timeout time.Duration = 30 * time.Second handle *pcap.Handle // Will reuse these for each packet ethLayer layers.Ethernet ipLayer layers.IPv4 tcpLayer layers.TCP)func main() { // Open device handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout) if err != nil { log.Fatal(err) } defer handle.Close() packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) for packet := range packetSource.Packets() { parser := gopacket.NewDecodingLayerParser( layers.LayerTypeEthernet, ðLayer, &ipLayer, &tcpLayer, ) foundLayerTypes := []gopacket.LayerType{} err := parser.DecodeLayers(packet.Data(), &foundLayerTypes) if err != nil { fmt.Println("Trouble decoding layers: ", err) } for _, layerType := range foundLayerTypes { if layerType == layers.LayerTypeIPv4 { fmt.Println("IPv4: ", ipLayer.SrcIP, "->", ipLayer.DstIP) } if layerType == layers.LayerTypeTCP { fmt.Println("TCP Port: ", tcpLayer.SrcPort, "->", tcpLayer.DstPort) fmt.Println("TCP SYN:", tcpLayer.SYN, " | ACK:", tcpLayer.ACK) } } }}
12.tcp流重组
gopacket包提供了一些名为Flow和Endpoint的类型。我没有机会深入探索这些文档,但文档中有一个使用它来将特定TCP流发送到数据包通道的示例。https://godoc.org/github.com/google/gopacket
阅读全文
0 0
- golang使用gopacket包进行数据包捕获,注入和分析
- Go语言用GoPacket抓包分析
- 使用jnetpcap捕获数据包进行流量检测
- 使用golang的pprof包对程序进行性能分析
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- Wireshark数据抓包教程之认识捕获分析数据包
- 数据包捕获与分析
- c语言指定网口捕获数据包--使用libpcap包
- 使用Python进行TCP数据包注入(伪造)
- 使用Wireshark捕获数据帧和IP数据包
- 预定义异常
- ReentrantLock 理解
- EventBus用法详解
- oracle数据库-归档模式/非归档模式修改
- 手把手教你新建一个基于 Maven 的 idea 项目(Web 项目)
- golang使用gopacket包进行数据包捕获,注入和分析
- SQL语法之自定义函数UDF
- Android的string中添加array 并使用
- Linux网络驱动程序开发实例分析
- Implement strStr()
- Java--泛型的原理以及使用场景
- 第一天 node.js基础部分
- JS构造函数与原型
- 数组的扩展