1. 程式人生 > >golang使用gopacket包進行資料包捕獲,注入和分析

golang使用gopacket包進行資料包捕獲,注入和分析

							            <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-e4c7a3727d.css">
					<div class="htmledit_views">

使用golang實現網路抓包是非常容易的,可以使用谷歌的包github.com/google/gopacket。由於gopacket構建在libpcap之上,我強烈建議您瞭解該庫的工作原理。您可以在C中學習如何使用libpcap進行更深入的瞭解。

1.libpcap

gopacket是基於libpcap(資料包捕獲函式庫)的,該庫提供的C函式介面用於捕捉經過指定網路介面的資料包,該介面應該是被設為混雜模式。 著名的軟體TCPDUMP就是在Libpcap的基礎上開發而成的。Libpcap提供的介面函式實現和封裝了與資料包截獲有關的過程。Libpcap可以在絕大多數Linux平臺上執行。 主要有以下功能: 資料包捕獲:捕獲流經網絡卡的原始資料包 自定義資料包傳送:構造任何格式的原始資料包 流量採集與統計:採集網路中的流量資訊 規則過濾:提供自帶規則過濾功能,按需要選擇過濾規則

2.先決條件

# Get the gopacket package from GitHub
go get github.com/google/gopacket # Pcap dev headers might be necessary sudo apt-get install libpcap-dev

3.獲取所有的網路裝置資訊

package main

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 main

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 main

import( “fmt” “os” “time”

var( deviceName  string=“eth0” 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(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 } } }

6.讀取pcap格式檔案來檢視分析網路資料包

我們不用開啟一個裝置進行實時捕獲,也可以開啟pcap檔案進行離線檢查。您可以使用tcpdump建立要使用的測試檔案。
# Capture packets to test.pcap file
sudo 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 files

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 main

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等眾多已知層建立了相應型別。有效載荷是應用層的一部分。
package main

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 main

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 main

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 main

// Create custom layer structure type CustomLayerstruct{ // This layer just has two bytes at the front SomeBytebyte AnotherBytebyte     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 unique varCustomLayerType= 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 type func (l CustomLayer)LayerType() gopacket.LayerType{ returnCustomLayerType }

// LayerContents returns the information that our layer // provides. In this case it is a header layer so // we return the header information func (l CustomLayer)LayerContents()[]byte{ return[]byte{l.SomeByte, l.AnotherByte} }

// LayerPayload returns the subsequent layer built // on top of our layer or raw payload func (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 function func 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 main

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 :=