<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    網絡流量抓包庫 gopacket介紹

    VSole2022-08-04 11:09:21

    gopacket簡介

    1. gopacket是什么?

    gopacket是google出品的golang三方庫,質量還是靠的住,項目地址為:github.com/google/gopacket

    gopacket到底是什么呢?是個抓取網絡數據包的庫,這么說可能還有點抽象,但是抓包工具大家可能都使用過。

    Windows平臺下有Wireshark抓包工具,其底層抓包庫是npcap(以前是winpcap);

    Linux平臺下有Tcpdump,其抓包庫是libpcap;

    而gopacket庫可以說是libpcap和npcap的go封裝,提供了更方便的go語言操作接口。

    對于抓包庫來說,常規功能就是抓包,而網絡抓包有以下幾個步驟:

    1、枚舉主機上網絡設備的接口

    2、針對某一網口進行抓包

    3、解析數據包的mac層、ip層、tcp/udp層字段等

    4、ip分片重組,或tcp分段重組成上層協議如http協議的數據

    5、對上層協議進行頭部解析和負載部分解析

    2. 應用場景有哪些?

    場景1:網絡流量分析

    對網絡設備流量進行實時采集以及數據包分析。

    場景2:偽造數據包

    不少網絡安全工具,需要偽造網絡數據包,填充上必要的協議字段后發送給對端設備,從而達到一些目的。

    場景3:離線pcap文件的讀取和寫入

    安裝部署

    2.1 安裝libpcap或npcap三方庫

    在使用gopacket包時,首先要確保在windows平臺下安裝了npcap或winpcap,或者是在linux平臺下安裝了libpcap庫。

    npcap下載地址:https://nmap.org/npcap/

    libpcap下載地址:https://www.tcpdump.org/

    下載自己電腦對應的操作系統版本的庫

    如果不想從官網下載libpcap庫的話,也可以采用centos的yum命令或ubuntu的apt get命令來進行安裝。

    2.2 安裝gopacket庫

    go get github.com/google/gopacket

    使用方法

    3.1 枚舉網絡設備

    package main
    import (
        "fmt"
        "log"
        "github.com/google/gopacket/pcap"
    )
    func main() {
        // 得到所有的(網絡)設備
        devices, err := pcap.FindAllDevs()
        if err != nil {
            log.Fatal(err)
        }
        // 打印設備信息
        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)
            }
        }
    }
    

    先調用pcap.FindAllDevs()獲取當前主機所有的網絡設備,網絡設備有哪些屬性呢?

    // Interface describes a single network interface on a machine.
    type Interface struct {
     Name        string //設備名稱
     Description string //設備描述信息
     Flags       uint32 
     Addresses   []InterfaceAddress //網口的地址信息列表
    }
    // InterfaceAddress describes an address associated with an Interface.
    // Currently, it's IPv4/6 specific.
    type InterfaceAddress struct {
     IP        net.IP
     Netmask   net.IPMask // Netmask may be nil if we were unable to retrieve it.
     Broadaddr net.IP     // Broadcast address for this IP may be nil
     P2P       net.IP     // P2P destination address for this IP may be nil
    }
    

    3.2 打開一個設備進行抓包

    package main
    import (
        "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() {
        // 打開某一網絡設備
        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) 實時捕獲

    2.1 節中我們枚舉了當前主機的所有網絡設備,現在需要打開網絡設備并進行實時捕獲數據包,需調用pcap.OpenLive來打開網絡設備,其函數原型如下:

    func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration) (handle *Handle, _ error)
    

    device:網絡設備的名稱,如eth0,也可以填充pcap.FindAllDevs()返回的設備的Name

    snaplen: 每個數據包讀取的最大長度 the maximum size to read for each packet

    promisc:是否將網口設置為混雜模式,即是否接收目的地址不為本機的包

    timeout:設置抓到包返回的超時。如果設置成30s,那么每30s才會刷新一次數據包;設置成負數,會立刻刷新數據包,即不做等待

    函數返回值:是一個*Handle類型的返回值,可能作為gopacket其他函數調用時作為函數參數來傳遞。

    注意事項:

    一定要記得釋放掉handle,如文中的defer handle.Close()。

    2) 創建數據包源

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

    第一個參數為OpenLive的返回值,指向Handle類型的指針變量handle。

    第二個參數為handle.LinkType()此參數默認是以太網鏈路,一般我們抓包,也是從2層以太網鏈路上抓取。

    3)讀取數據包

    //packetSource.Packets()是個channel類型,此處是從channel類型的數據通道中持續的讀取網絡數據包
    for packet := range packetSource.Packets() {
      // Process packet here
      fmt.Println(packet)
     }
    

    3.3 解碼數據包的各層

    我們可以獲取原始數據包,并嘗試將其強制轉換為已知格式。如ethernet、IP和TCP層。

    Layers包是gopacket的Go庫中的新功能,在底層libpcap庫中不存在。它是gopacket庫的非常有用的一部分。它允許我們輕松地識別數據包是否包含特定類型的層。這個代碼示例將演示如何使用layers包來查看包是否是ethernet、IP和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"
        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
        // 判斷數據包是否為以太網數據包,可解析出源mac地址、目的mac地址、以太網類型(如ip類型)等
        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)
        // 判斷數據包是否為IP數據包,可解析出源ip、目的ip、協議號等
        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
        // 判斷數據包是否為TCP數據包,可解析源端口、目的端口、seq序列號、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())
        }
        ///.......................................................
        // Check for errors
        // 判斷layer是否存在錯誤
        if err := packet.ErrorLayer(); err != nil {
            fmt.Println("Error decoding some part of the packet:", err)
        }
    }
    

    僅僅以此處tcp部分的代碼詳細解析下

    // 判斷數據包是否為TCP數據包,可解析源端口、目的端口、seq序列號、tcp標志位等
        tcpLayer := packet.Layer(layers.LayerTypeTCP)
        if tcpLayer != nil {
            fmt.Println("TCP layer detected.")
            tcp, _ := tcpLayer.(*layers.TCP)
            fmt.Printf("From port %d to %d\n", tcp.SrcPort, tcp.DstPort)
        }
    

    此處需要研究下源代碼中數據結構,以防理解錯誤

    type Packet interface {
     // Layer returns the first layer in this packet of the given type, or nil
     Layer(LayerType) Layer   //根據給定的類型,在數據包中尋找其第一個層
    }
    //看看Layer的結構
    type Layer interface {
     // LayerType is the gopacket type for this layer.
     LayerType() LayerType
     // LayerContents returns the set of bytes that make up this layer.
     LayerContents() []byte
     // LayerPayload returns the set of bytes contained within this layer, not
     // including the layer itself.
     LayerPayload() []byte
    }
    //tcp數據包格式
    type TCP struct {
     BaseLayer
     SrcPort, DstPort                           TCPPort
     Seq                                        uint32
     Ack                                        uint32
     DataOffset                                 uint8
     FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool
     Window                                     uint16
     Checksum                                   uint16
     Urgent                                     uint16
     sPort, dPort                               []byte
     Options                                    []TCPOption
     Padding                                    []byte
     opts                                       [4]TCPOption
     tcpipchecksum
    }
    

    TCP結構體是實現了Layer接口的,其實Ethernet,IPV4,UDP等結構體也實現了Layer接口

    在上述代碼中,我們調用函數時,傳入的LayerType協議層的類型為layers.LayerTypeTCP,函數返回值為interface類型,必須轉換成TCP結構體

    tcp, _ := tcpLayer.(*layers.TCP)

    tcp是layers.TCP這個具體類型的指針,通過tcp則可以獲取數據包中tcp協議的相關字段。

    3.4 自定義層

    自定義層有助于實現當前不包含在gopacket layers包中的協議。

    import (
        "fmt"
        "github.com/google/gopacket"
    )
    // 創建自定義層數據結構,并實現Layer接口中的函數LayerType()、LayerContents()、LayerPayload()
    type CustomLayer struct {
        // This layer just has two bytes at the front
        SomeByte    byte
        AnotherByte byte
        restOfData  []byte
    }
    // 注冊自定義層類型,然后我們才可以使用它
    // 第一個參數是ID. 自定義層使用大于2000的數字,它必須是唯一的
    var CustomLayerType = gopacket.RegisterLayerType(
        2001,
        gopacket.LayerTypeMetadata{
            "CustomLayerType",
            gopacket.DecodeFunc(decodeCustomLayer),
        },
    )
    //自定義層實現LayerType
    func (l CustomLayer) LayerType() gopacket.LayerType {
        return CustomLayerType
    }
    //自定義層實現LayerContents
    func (l CustomLayer) LayerContents() []byte {
        return []byte{l.SomeByte, l.AnotherByte}
    }
    //自定義層實現LayerPayload
    func (l CustomLayer) LayerPayload() []byte {
        return l.restOfData
    }
    //實現自定義的解碼函數
    func decodeCustomLayer(data []byte, p gopacket.PacketBuilder) error {
        p.AddLayer(&CustomLayer{data[0], data[1], data[2:]})
        return p.NextDecoder(gopacket.LayerTypePayload)
    }
    func main() {
        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)
        }
    }
    

    結合上述代碼可知,實現自定義的層需要3步:

    1、創建自定義層的結構體,并實現Layer接口中的函數LayerType()、LayerContents()、LayerPayload()

    2、按照解碼函數簽名來實現自定義解碼函數,名稱可自行命名。

    解碼函數簽名如下:

    type DecodeFunc func([]byte, PacketBuilder) error

    3、使用gopacket.RegisterLayerType函數來注冊自定義層

    3.5 TCP流重組

    為什么需要tcp流重組?

    package main
    import (
     "bufio"
     "flag"
     "io"
     "log"
     "net/http"
     "time"
     "github.com/google/gopacket"
     "github.com/google/gopacket/examples/util"
     "github.com/google/gopacket/layers"
     "github.com/google/gopacket/pcap"
     "github.com/google/gopacket/tcpassembly"
     "github.com/google/gopacket/tcpassembly/tcpreader"
    )
    var iface = flag.String("i", "eth0", "Interface to get packets from")
    var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture")
    // Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces
    // httpStreamFactory implements tcpassembly.StreamFactory
    type httpStreamFactory struct{}
    // httpStream will handle the actual decoding of http requests.
    type httpStream struct {
     net, transport gopacket.Flow
     r              tcpreader.ReaderStream
    }
    func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
     hstream := &httpStream{
      net:       net,
      transport: transport,
      r:         tcpreader.NewReaderStream(),
     }
     go hstream.run() // Important... we must guarantee that data from the reader stream is read.
     // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
     return &hstream.r
    }
    func (h *httpStream) run() {
     buf := bufio.NewReader(&h.r)
     for {
      req, err := http.ReadRequest(buf)
      if err == io.EOF {
       // We must read until we see an EOF... very important!
       return
      } else if err != nil {
       log.Println("Error reading stream", h.net, h.transport, ":", err)
      } else {
       bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
       req.Body.Close()
       log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body")
      }
     }
    }
    func main() {
     defer util.Run()()
     var handle *pcap.Handle
     var err error
     // Set up pcap packet capture
     handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever)
     if err != nil {
      log.Fatal(err)
     }
     // Set up assembly
     streamFactory := &httpStreamFactory{}
     streamPool := tcpassembly.NewStreamPool(streamFactory)
     assembler := tcpassembly.NewAssembler(streamPool)
     // Read in packets, pass to assembler.
     packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
     packets := packetSource.Packets()
     ticker := time.Tick(time.Minute)
     for {
      select {
      case packet := <-packets:
       if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP {
        log.Println("Unusable packet")
        continue
       }
       tcp := packet.TransportLayer().(*layers.TCP)
       //將數據包進行重組
       assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
      case <-ticker:
       //每隔一分鐘,刷新之前兩分鐘內不活動的連接
       assembler.FlushOlderThan(time.Now().Add(time.Minute * -2))
      }
     }
    }
    

    基本步驟如下:

    1、創建httpStreamFactory結構體,實現tcpassembly.StreamFactory接口

    2、創建連接池

    streamPool := tcpassembly.NewStreamPool(streamFactory)

    3、創建重組器

    assembler := tcpassembly.NewAssembler(streamPool)

    4、將數據包添加到重組器中

    assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)

    總結

    首先,gopacket庫是google大廠背書,從使用文檔、質量、社區活躍度來說都很不錯

    其次,使用方式簡單,擴展性好。gopacket提供了自定義的接口,可根據自身需要進行定制化開發

    最后,gopacket定義的layers齊全,如果是實時捕獲數據后進行協議解析,采用其內置的layer即可,無需自己手動去解析繁雜的協議了。

    網絡流量解析函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    嚴格來說,生成式人工智能已經存在了近十年,但最近這項技術的蓬勃發展激發了人們應用它來尋找潛在信息安全挑戰的新興趣。要想找到這些挑戰,就必須進行“大海撈針式”的搜索,而這其中包括每天都會涌入網絡的全新二進制文件、文檔、域名和其他工件,任務的艱巨性可想而知。
    Rootkit 是植入操作系統最深處的c。盡管在紙面上它們似乎對攻擊者很有吸引力,但創建它們會帶來重大的技術挑戰,并且最輕微的編程錯誤都有可能使受害計算機完全崩潰。
    當前,以數字經濟為代表的新經濟成為經濟增長新引擎,數據作為核心生產要素成為了基礎戰略資源,數據安全的基礎保障作用也日益凸顯。伴隨而來的數據安全風險與日俱增,數據泄露、數據濫用等安全事件頻發,為個人隱私、企業商業秘密、國家重要數據等帶來了嚴重的安全隱患。近年來,國家對數據安全與個人信息保護進行了前瞻性戰略部署,開展了系統性的頂層設計。《中華人民共和國數據安全法》于2021年9月1日正式施行,《中華人
    是個抓取網絡數據包的庫,這么說可能還有點抽象,但是抓包工具大家可能都使用過。場景1:網絡流量分析對網絡設備流量進行實時采集以及數據包分析。場景2:偽造數據包不少網絡安全工具,需要偽造網絡數據包,填充上必要的協議字段后發送給對端設備,從而達到一些目的。
    將從流量中提取的協議結構化數據提交給消息隊列,供大數據處理子系統和安全檢測分析子系統獲取。基于網絡的入侵檢測主要依靠特定的檢測規則,存在規則被繞過和無法調查溯源的弱點。在上述安全檢測的基礎上,系統提供對惡意攻擊事件和威脅行為的進一步分析、調查功能,這部分主要包括協議數據統計與查詢、可視化告警事件調查、交互式安全分析。
    解析 DeathStalker
    2020-08-25 10:59:49
    DeathStalker:一個獨特的攻擊組織,似乎針對的是金融領域的律師事務所和公司。 Powersing工具鏈 總覽 攻擊者的最新方式依賴于相同的入侵媒介:帶有包含惡意LNK文件的附加存檔的魚叉式網絡釣魚電子郵件。 盡管看起來...
    為了維持資源的使用,云用戶應該被授權審查而不是修改他們自己的計量數據,因為這可能會導致所需支付的云服務費用被偽造。此外,安全組中的規則變更,也應實時、動態應用于現有或后續新建的虛擬機實例上。VPC在底層使用了如VLAN、命名空間等機制,以保證不同租戶的虛擬網絡的資源和流量是隔離。
    近期,紅雨滴云沙箱威脅狩獵流程監控到大量通過CDN隧道隱藏攻擊者真實C2的樣本,這表明借助CDN隱匿蹤跡已成為一種常用手段。并且我們還看到攻擊者采取多種手段試圖繞過終端安全軟件和網絡安全設備的檢測,比如利用DLL側加載(又稱“白加黑”)配合域前置技術針對終端和流量安全檢測進行繞過等。
    在此提醒廣大用戶,切勿打開社交媒體分享的來歷不明的鏈接,不點擊執行未知來源的郵件附件,不運行夸張標題的未知文件,不安裝非正規途徑來源的APP。做到及時備份重要文件,更新安裝補丁。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类