godivert

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 22, 2024 License: LGPL-3.0 Imports: 14 Imported by: 0

README

GoDivert

Test Results

Go bindings for WinDivert v2.2

Fork of williamfhe/godivert with some fixes and improvements. This version is pinned to WinDivert 2.2 and implements all its features.

WinDivert is a user-mode packet capture-and-divert package for Windows.

Requirements (these are my own, fork at will and/or PR to change)

  • WinDivert 2.2 (it might not work with other versions)
  • Windows with Administrator privileges
  • Go 1.19 or later

Installation

go get github.com/deblasis/godivert

Introduction

The binding's documentation can be found Here.

If you don't have the WinDivert dll installed on your System or you want to load a specific WinDivert dll you should do :

godivert.LoadDLL("PathToThe64bitDLL", "PathToThe32bitDLL")

The path can be a relative path to the .exe current directory or an absolute path.

Note that the driver must be in the same directory as the dll. LoadDLL will then load the dll depending on your OS architecture.

To start create a new instance of WinDivertHandle by calling NewWinDivertHandle and passing the filter as a parameter.

Documentation of the filter can be found Here.

winDivert, err := godivert.NewWinDivertHandle("Your filter here")

WinDivertHandle is struct that you can use to call WinDivert's function like Recv or Send.

You can divert a packet from the network stack by using winDivert.Recv() where winDivert is an instance of WinDivertHandle.

packet, err := winDivert.Recv()

You can then choose to send the packet or modify it.

packet.SetDstPort(1234) // Sets the destination port
packet.Send(winDivert) // Sends the packet back on the network stack

You can get and set values from the packet's header by using the header package. Documentation on this package can be found Here .

As the packet has been modified the checksums have to be recalculated before sending it back on the network stack.

It is done automatically if the packet has been modified when calling packet.Send but you can do it manually by calling packet.CalcNewChecksum.

To receive packets you can also use winDivert.Packets.

packetChan, err := winDivert.Packets()

Here packetChan is a channel of *godivert.Packet coming directly from the network stack.

Note that all packets diverted are guaranteed to match the filter given in godivert.NewWinDivertHandle("You filter here")

Examples

Capturing and Printing a Packet
package main

import (
    "fmt"
    "github.com/deblasis/godivert"
)

func main() {
    winDivert, err := godivert.NewWinDivertHandle("true")
    if err != nil {
        panic(err)
    }

    packet, err := winDivert.Recv()
    if err != nil {
        panic(err)
    }
    defer winDivert.Close()

    fmt.Println(packet)

    packet.Send(winDivert)

}

Wait for a packet and print it.

Blocking Protocol by IP

package main

import (
    "net"
    "time"
    "github.com/deblasis/godivert"
)

var cloudflareDNS = net.ParseIP("1.1.1.1")

func checkPacket(wd *godivert.WinDivertHandle, packetChan <-chan *godivert.Packet) {
    for packet := range packetChan {
        if !packet.DstIP().Equal(cloudflareDNS) {
            packet.Send(wd)
        }
    }
}

func main() {
    winDivert, err := godivert.NewWinDivertHandle("icmp")
    if err != nil {
        panic(err)
    }
    defer winDivert.Close()

    packetChan, err := winDivert.Packets()
    if err != nil {
        panic(err)
    }

    go checkPacket(winDivert, packetChan)

    time.Sleep(1 * time.Minute)
}

Forbid all ICMP packets to reach 1.1.1.1 for 1 minute.

Try it :

ping 1.1.1.1
Packet Count
package main

import (
    "fmt"
    "time"
    "github.com/deblasis/godivert"
    "github.com/deblasis/godivert/header"
)

var icmpv4, icmpv6, udp, tcp, unknown, served uint

func checkPacket(wd *godivert.WinDivertHandle, packetChan  <- chan *godivert.Packet) {
    for packet := range packetChan {
        countPacket(packet)
        wd.Send(packet)
    }
}

func countPacket(packet *godivert.Packet) {
    served++
    switch packet.NextHeaderType() {
    case header.ICMPv4:
        icmpv4++
    case header.ICMPv6:
        icmpv6++
    case header.TCP:
        tcp++
    case header.UDP:
        udp++
    default:
        unknown++
    }
}


func main() {
    winDivert, err := godivert.NewWinDivertHandle("true")
    if err != nil {
        panic(err)
    }

    fmt.Println("Starting")
    defer winDivert.Close()

    packetChan, err := winDivert.Packets()
    if err != nil {
        panic(err)
    }

    n := 50
    for i := 0; i < n; i++ {
        go checkPacket(winDivert, packetChan)
    }

    time.Sleep(15 * time.Second)

    fmt.Println("Stopping...")

    fmt.Printf("Served: %d packets\n", served)

    fmt.Printf("ICMPv4=%d ICMPv6=%d UDP=%d TCP=%d Unknown=%d", icmpv4, icmpv6, udp, tcp, unknown)
}

Count all protocols passing by for 15 seconds.

Testing

Tests require administrator privileges and WinDivert 2.2 files. You can run tests locally with:

# Download WinDivert files first
Invoke-WebRequest -Uri "https://reqrypt.org/download/WinDivert-2.2.2-A.zip" -OutFile "WinDivert.zip"
Expand-Archive -Path "WinDivert.zip" -DestinationPath "."
Copy-Item "WinDivert-2.2.2-A/x64/WinDivert.dll" -Destination "."
Copy-Item "WinDivert-2.2.2-A/x64/WinDivert.lib" -Destination "."
Copy-Item "WinDivert-2.2.2-A/x64/WinDivert64.sys" -Destination "."

# Run tests (as administrator)
go test -v ./...

Test results are available in several places:

  1. GitHub Actions - Full test runs
  2. Pull Request comments - Detailed test results for each PR
  3. Checks tab - Test failures and annotations
  4. Job summary - Overview of test results

Documentation

Index

Constants

View Source
const (
	PacketBufferSize   = 2048
	PacketChanCapacity = 256

	WinDivertDirectionOutbound Direction = false
	WinDivertDirectionInbound  Direction = true
)
View Source
const (
	WinDivertFlagSniff uint8 = 1 << iota
	WinDivertFlagDrop  uint8 = 1 << iota
	WinDivertFlagDebug uint8 = 1 << iota
)
View Source
const (
	MaxPacketSize = 65535
)

Variables

This section is empty.

Functions

func CheckDLL

func CheckDLL() error

CheckDLL verifies that the WinDivert driver is installed and accessible

func GetBuffer added in v1.0.1

func GetBuffer(size int) []byte

GetBuffer with size class optimization

func GetMarshalBuffer added in v1.0.1

func GetMarshalBuffer(size int) (*struct {
	buf  []byte
	hdr  [header.MarshalHeaderSize]byte
	addr [header.AddressSize]byte
}, bool)

GetMarshalBuffer returns a buffer for marshal operations

func HelperCompileFilter

func HelperCompileFilter(filter string) (bool, int)

Take the given filter and check if it contains any error https://reqrypt.org/windivert-doc.html#divert_helper_check_filter

func HelperEvalFilter

func HelperEvalFilter(packet *Packet, filter string) (bool, error)

Take a packet and compare it with the given filter Returns true if the packet matches the filter https://reqrypt.org/windivert-doc.html#divert_helper_eval_filter

func HelperFormatIPv4Address

func HelperFormatIPv4Address(addr uint32) (string, error)

HelperFormatIPv4Address formats an IPv4 address as a string

func HelperFormatIPv6Address

func HelperFormatIPv6Address(addr [4]uint32) (string, error)

HelperFormatIPv6Address formats an IPv6 address as a string

func HelperHashPacket

func HelperHashPacket(packet []byte, seed uint64) (uint64, error)

HelperHashPacket calculates a 64-bit hash of the packet

func HelperHtonIPv6Address

func HelperHtonIPv6Address(inAddr [4]uint32) [4]uint32

func HelperHtonl

func HelperHtonl(x uint32) uint32

func HelperHtonll

func HelperHtonll(x uint64) uint64

func HelperHtons

func HelperHtons(x uint16) uint16

func HelperNtohIPv6Address

func HelperNtohIPv6Address(inAddr [4]uint32) [4]uint32

func HelperNtohl

func HelperNtohl(x uint32) uint32

func HelperNtohll

func HelperNtohll(x uint64) uint64

func HelperNtohs

func HelperNtohs(x uint16) uint16

Byte order conversion helpers

func HelperParseIPv4Address

func HelperParseIPv4Address(addrStr string) (uint32, error)

HelperParseIPv4Address parses an IPv4 address string

func HelperParseIPv6Address

func HelperParseIPv6Address(addrStr string) ([4]uint32, error)

HelperParseIPv6Address parses an IPv6 address string

func IsDLLLoaded

func IsDLLLoaded() bool

Helper to check if DLL is loaded

func LoadDLL

func LoadDLL(path64, path32 string) error

LoadDLL loads the WinDivert DLL and initializes the proc addresses

func ProcessPacketBatch added in v1.0.1

func ProcessPacketBatch(packets []*Packet)

Process multiple packets at once

func PutBuffer added in v1.0.1

func PutBuffer(buf []byte)

PutBuffer with optimized size class lookup

func PutMarshalBuffer added in v1.0.1

func PutMarshalBuffer(buf *struct {
	buf  []byte
	hdr  [header.MarshalHeaderSize]byte
	addr [header.AddressSize]byte
}, pooled bool)

PutMarshalBuffer returns a marshal buffer to the pool

func UnloadDLL

func UnloadDLL() error

UnloadDLL cleans up resources associated with the WinDivert DLL

Types

type Direction

type Direction bool

func (Direction) String

func (d Direction) String() string

type Flags

type Flags uint64
const (
	// Packet sniffing mode - packets will continue to their destination
	FlagSniff Flags = 0x0001

	// Packet dropping mode - matching packets will be dropped
	FlagDrop Flags = 0x0002

	// Receive-only mode - packets can only be read, not injected
	FlagRecvOnly Flags = 0x0004
	FlagReadOnly       = FlagRecvOnly // Alias for FlagRecvOnly

	// Send-only mode - packets can only be injected, not read
	FlagSendOnly  Flags = 0x0008
	FlagWriteOnly       = FlagSendOnly // Alias for FlagSendOnly

	// Skip driver installation check
	FlagNoInstall Flags = 0x0010

	// Handle IP fragments
	FlagFragments Flags = 0x0020
)

func (*Flags) Add

func (f *Flags) Add(flags Flags)

Add adds the given flags

func (*Flags) Clear

func (f *Flags) Clear()

Clear removes all flags

func (Flags) Has

func (f Flags) Has(flags Flags) bool

Has checks if the flags contain all the given flags

func (Flags) IsValid

func (f Flags) IsValid() bool

IsValid checks if the flags combination is valid according to WinDivert rules

func (*Flags) Remove

func (f *Flags) Remove(flags Flags)

Remove removes the given flags

type Layer

type Layer uint8
const (
	LayerNetwork Layer = 0
	LayerForward Layer = 1
	LayerFlow    Layer = 2
	LayerSocket  Layer = 3
	LayerReflect Layer = 4
)

type Option

type Option func(*WinDivertHandleConfig)

Option is a function that configures a WinDivertHandle

func WithFlags

func WithFlags(flags Flags) Option

WithFlags sets the flags for the WinDivertHandle

func WithLayer

func WithLayer(layer Layer) Option

WithLayer sets the layer for the WinDivertHandle

func WithPriority

func WithPriority(priority int16) Option

WithPriority sets the priority for the WinDivertHandle

type Packet

type Packet struct {
	Raw       []byte
	Addr      *WinDivertAddress
	PacketLen uint

	IpHdr      header.IPHeader
	NextHeader header.ProtocolHeader
	// contains filtered or unexported fields
}

Represents a packet

func GetPacket added in v1.0.1

func GetPacket() *Packet

Add helper method to get a packet from pool

func (*Packet) CalcNewChecksum

func (p *Packet) CalcNewChecksum(wd *WinDivertHandle)

Recalculate the packet's checksum Shortcut for WinDivertHelperCalcChecksum

func (*Packet) Direction

func (p *Packet) Direction() Direction

Returns the Direction of the packet WinDivertDirectionInbound (true) for inbound Packets WinDivertDirectionOutbound (false) for outbound packets Shortcut for Addr.Direction()

func (*Packet) DstIP

func (p *Packet) DstIP() net.IP

Returns the destination IP of the packet Shortcut for IpHdr.DstIP()

func (*Packet) DstPort

func (p *Packet) DstPort() (uint16, error)

Returns the destination port of the packet Shortcut for NextHeader.DstPort()

func (*Packet) EvalFilter

func (p *Packet) EvalFilter(filter string) (bool, error)

Check the packet with the filter Returns true if the packet matches the filter

func (*Packet) IpVersion

func (p *Packet) IpVersion() int

Returns the version of the IP protocol Shortcut for ipHdr.Version()

func (*Packet) MarshalBinary added in v1.0.1

func (p *Packet) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface

func (*Packet) NextHeaderProtocolName

func (p *Packet) NextHeaderProtocolName() string

Returns the name of the protocol

func (*Packet) NextHeaderType

func (p *Packet) NextHeaderType() uint8

Returns the IP Protocol number of the next Header https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers

func (*Packet) ParseHeaders

func (p *Packet) ParseHeaders()

Parse the packet's headers

func (*Packet) Release added in v1.0.1

func (p *Packet) Release()

Add helper method to return packet to pool

func (*Packet) Reset added in v1.0.1

func (p *Packet) Reset()

Reset resets the packet to its zero state for reuse

func (*Packet) Send

func (p *Packet) Send(wd *WinDivertHandle) (uint, error)

Inject the packet on the Network Stack If the packet has been modified calls WinDivertHelperCalcChecksum to get a new checksum

func (*Packet) SetDstIP

func (p *Packet) SetDstIP(ip net.IP)

Sets the destination IP of the packet

func (*Packet) SetDstPort

func (p *Packet) SetDstPort(port uint16) error

Sets the destination port of the packet Shortcut for NextHeader.SetDstPort()

func (*Packet) SetSrcIP

func (p *Packet) SetSrcIP(ip net.IP)

Sets the source IP of the packet

func (*Packet) SetSrcPort

func (p *Packet) SetSrcPort(port uint16) error

Sets the source port of the packet Shortcut for NextHeader.SetSrcPort()

func (*Packet) SrcIP

func (p *Packet) SrcIP() net.IP

Returns the source IP of the packet Shortcut for IpHdr.SrcIP()

func (*Packet) SrcPort

func (p *Packet) SrcPort() (uint16, error)

Returns the source port of the packet Shortcut for NextHeader.SrcPort()

func (*Packet) String

func (p *Packet) String() string

func (*Packet) UnmarshalBinary added in v1.0.1

func (p *Packet) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface

func (*Packet) VerifyParsed

func (p *Packet) VerifyParsed()

Check if the headers have already been parsed and call ParseHeaders() if not

type ParsedPacket

type ParsedPacket struct {
	Raw    []byte
	IPv4   *header.IPv4Header
	IPv6   *header.IPv6Header
	ICMP   *header.ICMPv4Header
	ICMPv6 *header.ICMPv6Header
	TCP    *header.TCPHeader
	UDP    *header.UDPHeader
}

ParsedPacket represents a parsed network packet

func HelperParsePacket

func HelperParsePacket(packet []byte) (*ParsedPacket, error)

HelperParsePacket parses a raw packet into its components

type WinDivertAddress

type WinDivertAddress struct {
	Timestamp int64
	IfIdx     uint32
	SubIfIdx  uint32
	Flags     uint8
	Reserved1 uint8
	Reserved2 uint16
	Reserved3 uint32
}

Represents a WinDivertAddress struct See : https://reqrypt.org/windivert-doc.html#divert_address

func NewWinDivertAddress added in v1.0.1

func NewWinDivertAddress() *WinDivertAddress

NewWinDivertAddress creates a new WinDivertAddress with default values

func (*WinDivertAddress) Direction

func (w *WinDivertAddress) Direction() Direction

Returns the direction of the packet WinDivertDirectionInbound (true) for inbounds packets WinDivertDirectionOutbounds (false) for outbounds packets

func (*WinDivertAddress) GetFlags

func (w *WinDivertAddress) GetFlags() uint8

func (*WinDivertAddress) GetLayer

func (w *WinDivertAddress) GetLayer() uint8

func (*WinDivertAddress) Impostor

func (w *WinDivertAddress) Impostor() bool

Returns true if the packet is an impostor

func (*WinDivertAddress) Loopback

func (w *WinDivertAddress) Loopback() bool

Returns true if the packet is a loopback packet

func (*WinDivertAddress) MarshalBinary added in v1.0.1

func (w *WinDivertAddress) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface

func (*WinDivertAddress) PseudoIPChecksum

func (w *WinDivertAddress) PseudoIPChecksum() bool

Returns true if the packet uses a pseudo IP checksum

func (*WinDivertAddress) PseudoTCPChecksum

func (w *WinDivertAddress) PseudoTCPChecksum() bool

Returns true if the packet uses a pseudo TCP checksum

func (*WinDivertAddress) PseudoUDPChecksum

func (w *WinDivertAddress) PseudoUDPChecksum() bool

Returns true if the packet uses a pseudo UDP checksum

func (*WinDivertAddress) SetFlags

func (w *WinDivertAddress) SetFlags(flags uint8)

Add helper methods to handle flags through Flags field

func (*WinDivertAddress) SetLayer

func (w *WinDivertAddress) SetLayer(layer uint8)

func (*WinDivertAddress) Size added in v1.0.1

func (w *WinDivertAddress) Size() int

Size returns the binary size of WinDivertAddress

func (*WinDivertAddress) String

func (w *WinDivertAddress) String() string

func (*WinDivertAddress) UnmarshalBinary added in v1.0.1

func (w *WinDivertAddress) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface

type WinDivertHandle

type WinDivertHandle struct {
	// contains filtered or unexported fields
}

Used to call WinDivert's functions

func NewWinDivertHandle

func NewWinDivertHandle(filter string, opts ...Option) (*WinDivertHandle, error)

NewWinDivertHandle creates a new WinDivertHandle with the given filter and options

func (*WinDivertHandle) Close

func (wd *WinDivertHandle) Close() error

Close the Handle See https://reqrypt.org/windivert-doc.html#divert_close

func (*WinDivertHandle) GetParam

func (wd *WinDivertHandle) GetParam(param uint) (uint64, error)

GetParam gets a WinDivert parameter

func (*WinDivertHandle) HelperCalcChecksum

func (wd *WinDivertHandle) HelperCalcChecksum(packet *Packet) error

Calls WinDivertHelperCalcChecksum to calculate the packet's checksum

func (*WinDivertHandle) Packets

func (wd *WinDivertHandle) Packets(ctx context.Context) (<-chan *Packet, error)

Create a new channel that will be used to pass captured packets and returns it calls recvLoop to maintain a loop

func (*WinDivertHandle) Recv

func (wd *WinDivertHandle) Recv() (*Packet, error)

Recv receives a packet from the network stack

func (*WinDivertHandle) RecvBatch

func (wd *WinDivertHandle) RecvBatch(maxPackets int) ([]*Packet, error)

RecvBatch receives multiple packets in a single call for better performance

func (*WinDivertHandle) RecvEx

func (wd *WinDivertHandle) RecvEx(packet []byte, addr *WinDivertAddress, flags uint64, overlapped *windows.Overlapped) (uint, error)

RecvEx receives a packet with extended options

func (*WinDivertHandle) Send

func (wd *WinDivertHandle) Send(packet *Packet) (uint, error)

Send injects a packet into the network stack

func (*WinDivertHandle) SendEx

func (wd *WinDivertHandle) SendEx(packet []byte, addr *WinDivertAddress, flags uint64, overlapped *windows.Overlapped) (uint, error)

SendEx sends a packet with extended options

func (*WinDivertHandle) SetParam

func (wd *WinDivertHandle) SetParam(param uint, value uint64) error

SetParam sets a WinDivert parameter

func (*WinDivertHandle) Shutdown

func (wd *WinDivertHandle) Shutdown(how uint) error

Shutdown shuts down the handle

type WinDivertHandleConfig

type WinDivertHandleConfig struct {
	// contains filtered or unexported fields
}

WinDivertHandleConfig holds configuration for WinDivertHandle

Directories

Path Synopsis
examples
blockPackets command
packetCount command
readPacket command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL