windows

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Rendered for windows/amd64

Overview

Package windows provides transparent per-process TCP interception for the Echo HTTP MITM proxy on Windows.

It uses WinDivert to intercept outbound TCP connections from specified processes at the kernel level, NAT-rewrites them to a local relay port, and the relay forwards the traffic to Echo using standard HTTP proxy protocol (CONNECT for HTTPS, absolute-URL for HTTP).

Architecture:

Target Process (chrome.exe)            Echo Proxy (:8899)
    │ outbound TCP                          ▲
    ▼                                       │ HTTP proxy protocol
WinDivert kernel intercept                  │
    │ NAT rewrite dst → 127.0.0.1:34010    │
    ▼                                       │
Relay Server (:34010) ──────────────────────┘
    (CONNECT / absolute-URL conversion)

Requirements:

  • Windows 10+ (64-bit)
  • Administrator privileges
  • WinDivert.dll and WinDivert64.sys in the executable directory

Example usage:

// Create and start Echo proxy
e, _ := echo.NewEcho(certPEM, keyPEM)
e.AddPlugin(&echo.Plugin{
    Match: "*.example.com",
    OnRequest: func(ctx *echo.Context) {
        // inspect/modify request
    },
})
go http.ListenAndServe(":8899", e)

// Create the interceptor targeting Echo
interceptor := windows.NewInterceptor("127.0.0.1:8899")

// Add rules for which processes to intercept
interceptor.AddRule(&windows.ProcessRule{
    ProcessName: "chrome.exe",
    TargetHosts: "*",
    TargetPorts: "*",
    Action:      windows.ActionProxy,
    Enabled:     true,
})

// Optionally monitor intercepted connections
interceptor.SetConnectionCallback(func(pid uint32, name string,
    srcIP, dstIP uint32, srcPort, dstPort uint16, action windows.Action) {
    fmt.Printf("[%s] %s:%d → %s:%d (%s)\n",
        name, windows.IP32to4(srcIP), srcPort,
        windows.IP32to4(dstIP), dstPort, action)
})

// Start intercepting (requires admin privileges)
if err := interceptor.Start(); err != nil {
    log.Fatal(err)
}
defer interceptor.Stop()

Index

Constants

View Source
const (
	LayerNetwork        = 0
	LayerNetworkForward = 1
)

WinDivert layer constants.

View Source
const (
	ShutdownRecv = 0x1
	ShutdownSend = 0x2
	ShutdownBoth = 0x3
)

WinDivert shutdown constants.

View Source
const (
	ParamQueueLen  = 0
	ParamQueueTime = 1
	ParamQueueSize = 2
)

WinDivert parameter constants.

View Source
const Localhost127 = 0x7F000001

Localhost127 is 127.0.0.1 in network byte order.

Variables

This section is empty.

Functions

func CalcChecksums

func CalcChecksums(buf []byte, addr *Address, flags uint64) error

CalcChecksums recalculates IP/TCP/UDP checksums for a packet.

func Close

func Close(h Handle) error

Close closes a WinDivert handle.

func IP4to32

func IP4to32(ip net.IP) uint32

IP4to32 converts a net.IP to a network-byte-order uint32.

func IP32to4

func IP32to4(ip uint32) string

IP32to4 converts a network-byte-order uint32 to a dotted IP string.

func IsBroadcastOrMulticast

func IsBroadcastOrMulticast(ip uint32) bool

IsBroadcastOrMulticast returns true if the IP (network byte order) is a broadcast, multicast, or APIPA address.

func Localhost127BE

func Localhost127BE() uint32

Localhost127BE returns 127.0.0.1 in big-endian (network byte order).

func ParsePacket

func ParsePacket(buf []byte) (*IPv4Header, *TCPHeader, bool)

ParsePacket attempts to parse buf as an IPv4+TCP packet. Returns the parsed headers and true on success, or nil, nil, false if the packet is not a valid IPv4 TCP packet.

func RecvRaw

func RecvRaw(h Handle, buf []byte, addr *Address) (int, error)

RecvRaw receives a packet using pre-allocated (VirtualAlloc) buffer and address.

func Send

func Send(h Handle, buf []byte, addr *Address) error

Send injects a packet through a WinDivert handle.

func SetDLLPath

func SetDLLPath(dllPath string)

SetDLLPath sets the path to WinDivert.dll. Must be called before Start(). The WinDivert64.sys driver file must be in the same directory as the DLL.

func SetParam

func SetParam(h Handle, param int, value uint64) error

SetParam sets a WinDivert parameter.

func Shutdown

func Shutdown(h Handle, how int) error

Shutdown shuts down a WinDivert handle (unblocks Recv/Send).

Types

type Action

type Action int

Action represents what to do with matched traffic.

const (
	// ActionProxy routes traffic through the Echo proxy.
	ActionProxy Action = iota
	// ActionDirect allows traffic to pass through unmodified.
	ActionDirect
	// ActionBlock drops the traffic silently.
	ActionBlock
)

func (Action) String

func (a Action) String() string

type Address

type Address struct {
	Timestamp int64 // 8 bytes: packet timestamp
	Layer     uint8 // 1 byte: layer
	Event     uint8 // 1 byte: event
	Flags     uint8 // 1 byte: bit flags (Sniffed:1, Outbound:1, Loopback:1, Impostor:1, IPv6:1, IPChecksum:1, TCPChecksum:1, UDPChecksum:1)

	DataLen uint32 // 4 bytes: data length
	// contains filtered or unexported fields
}

Address matches the WINDIVERT_ADDRESS structure (64 bytes). We access fields through methods using unsafe pointer arithmetic to match the C bit-field layout.

func Recv

func Recv(h Handle, buf []byte) (int, *Address, error)

Recv receives a packet from a WinDivert handle. Returns the number of bytes read and the packet address.

func (*Address) Loopback

func (a *Address) Loopback() bool

Loopback returns true if the packet is loopback.

func (*Address) Outbound

func (a *Address) Outbound() bool

Outbound returns true if the packet was outbound.

func (*Address) SetOutbound

func (a *Address) SetOutbound(outbound bool)

SetOutbound sets or clears the outbound flag.

type ConnEntry

type ConnEntry struct {
	SrcPort      uint16
	SrcIP        uint32 // network byte order
	OrigDestIP   uint32 // network byte order
	OrigDestPort uint16
	LastActivity time.Time
	// contains filtered or unexported fields
}

ConnEntry represents a tracked NAT connection.

type ConnectionCallback

type ConnectionCallback func(pid uint32, processName string, srcIP, dstIP uint32, srcPort, dstPort uint16, action Action)

ConnectionCallback is called when a new connection is intercepted.

type Handle

type Handle uintptr

Handle is a WinDivert handle.

func Open

func Open(filter string, layer int16, priority int16, flags uint64) (Handle, error)

Open opens a WinDivert handle with the given filter.

type IPv4Header

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

IPv4Header provides read/write access to IPv4 header fields in a raw packet buffer.

func (*IPv4Header) DstAddr

func (h *IPv4Header) DstAddr() uint32

DstAddr returns the destination IP address as a 4-byte network-order uint32.

func (*IPv4Header) DstIP

func (h *IPv4Header) DstIP() net.IP

DstIP returns the destination IP as net.IP.

func (*IPv4Header) HeaderLen

func (h *IPv4Header) HeaderLen() int

HeaderLen returns the IPv4 header length in bytes.

func (*IPv4Header) IsLoopbackDst

func (h *IPv4Header) IsLoopbackDst() bool

IsLoopbackDst returns true if the destination IP is 127.x.x.x.

func (*IPv4Header) IsLoopbackSrc

func (h *IPv4Header) IsLoopbackSrc() bool

IsLoopbackSrc returns true if the source IP is 127.x.x.x.

func (*IPv4Header) Protocol

func (h *IPv4Header) Protocol() uint8

Protocol returns the IP protocol number (6 = TCP, 17 = UDP).

func (*IPv4Header) SetDstAddr

func (h *IPv4Header) SetDstAddr(ip uint32)

SetDstAddr sets the destination IP address (network byte order).

func (*IPv4Header) SetSrcAddr

func (h *IPv4Header) SetSrcAddr(ip uint32)

SetSrcAddr sets the source IP address (network byte order).

func (*IPv4Header) SrcAddr

func (h *IPv4Header) SrcAddr() uint32

SrcAddr returns the source IP address as a 4-byte network-order uint32.

func (*IPv4Header) SrcIP

func (h *IPv4Header) SrcIP() net.IP

SrcIP returns the source IP as net.IP.

func (*IPv4Header) TotalLen

func (h *IPv4Header) TotalLen() uint16

TotalLen returns the total packet length from the IPv4 header.

type Interceptor

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

Interceptor orchestrates the WinDivert-based per-process transparent proxy. It intercepts outbound TCP traffic from specified processes and redirects it through the local Relay server to the Echo HTTP proxy.

func NewInterceptor

func NewInterceptor(echoAddr string) *Interceptor

NewInterceptor creates a new Windows transparent proxy interceptor. echoAddr is the address of the Echo proxy (e.g., "127.0.0.1:8899").

func (*Interceptor) AddRule

func (i *Interceptor) AddRule(rule *ProcessRule) uint32

AddRule adds a process interception rule and returns its ID.

func (*Interceptor) DisableRule

func (i *Interceptor) DisableRule(ruleID uint32) error

DisableRule disables a rule by ID.

func (*Interceptor) EnableRule

func (i *Interceptor) EnableRule(ruleID uint32) error

EnableRule enables a rule by ID.

func (*Interceptor) RemoveRule

func (i *Interceptor) RemoveRule(ruleID uint32) error

RemoveRule removes a rule by ID.

func (*Interceptor) SetConnectionCallback

func (i *Interceptor) SetConnectionCallback(cb ConnectionCallback)

SetConnectionCallback sets an optional callback invoked when a new connection is intercepted and a rule decision is made.

func (*Interceptor) SetDNSViaProxy

func (i *Interceptor) SetDNSViaProxy(enabled bool)

SetDNSViaProxy controls whether DNS (port 53) traffic is also proxied. By default, DNS traffic is allowed to pass through directly.

func (*Interceptor) Start

func (i *Interceptor) Start() error

Start begins intercepting traffic. Requires administrator privileges.

func (*Interceptor) Stop

func (i *Interceptor) Stop() error

Stop shuts down the interceptor and waits for all goroutines to finish.

type NATTable

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

NATTable is a hash table tracking NAT-rewritten connections. It uses 256 buckets keyed by source port, with per-bucket locking.

func NewNATTable

func NewNATTable() *NATTable

NewNATTable creates a new NAT connection tracking table.

func (*NATTable) Add

func (t *NATTable) Add(srcPort uint16, srcIP, destIP uint32, destPort uint16)

Add inserts or updates a connection entry keyed by srcPort.

func (*NATTable) Cleanup

func (t *NATTable) Cleanup()

Cleanup removes stale entries older than connStaleTimeout. Should be called periodically from a background goroutine.

func (*NATTable) Clear

func (t *NATTable) Clear()

Clear removes all entries from the table.

func (*NATTable) IsTracked

func (t *NATTable) IsTracked(srcPort uint16) bool

IsTracked returns true if the source port has an active NAT entry.

func (*NATTable) Lookup

func (t *NATTable) Lookup(srcPort uint16) (destIP uint32, destPort uint16, ok bool)

Lookup returns the original destination IP and port for the given source port. Returns false if not found. Updates last activity time on hit.

func (*NATTable) Remove

func (t *NATTable) Remove(srcPort uint16)

Remove deletes the NAT entry for the given source port.

type ProcessResolver

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

ProcessResolver resolves connection source ports to process names.

func NewProcessResolver

func NewProcessResolver() *ProcessResolver

NewProcessResolver creates a new process resolver.

func (*ProcessResolver) CleanupCache

func (pr *ProcessResolver) CleanupCache()

CleanupCache removes expired entries from the PID cache.

func (*ProcessResolver) ClearCache

func (pr *ProcessResolver) ClearCache()

ClearCache removes all entries from the PID cache.

func (*ProcessResolver) GetPIDFromConnection

func (pr *ProcessResolver) GetPIDFromConnection(srcIP uint32, srcPort uint16) uint32

GetPIDFromConnection looks up the PID that owns the given TCP connection.

func (*ProcessResolver) GetProcessName

func (pr *ProcessResolver) GetProcessName(pid uint32) (string, bool)

GetProcessName returns the full executable path for the given PID.

func (*ProcessResolver) IsSelf

func (pr *ProcessResolver) IsSelf(pid uint32) bool

IsSelf returns true if the PID matches this process.

type ProcessRule

type ProcessRule struct {
	ID          uint32
	ProcessName string // "chrome.exe", "fire*", "*"
	TargetHosts string // "*", "192.168.*.*", "10.0.0.1;172.16.0.0"
	TargetPorts string // "*", "80;443", "8000-9000"
	Action      Action
	Enabled     bool
}

ProcessRule defines a rule for matching process traffic.

type Relay

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

Relay accepts transparent TCP connections on a local port, looks up the original destination via the NAT table, and forwards the traffic to the Echo HTTP proxy using standard HTTP proxy protocol (CONNECT or absolute URL).

func NewRelay

func NewRelay(listenAddr, echoAddr string, nat *NATTable) *Relay

NewRelay creates a new TCP relay.

func (*Relay) Start

func (r *Relay) Start() error

Start begins listening for connections.

func (*Relay) Stop

func (r *Relay) Stop()

Stop shuts down the relay and waits for all connections to finish.

type RuleManager

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

RuleManager manages an ordered list of process rules.

func NewRuleManager

func NewRuleManager() *RuleManager

NewRuleManager creates a new rule manager.

func (*RuleManager) AddRule

func (rm *RuleManager) AddRule(rule *ProcessRule) uint32

AddRule adds a rule and returns its assigned ID.

func (*RuleManager) BuildPortFilter

func (rm *RuleManager) BuildPortFilter() (string, bool)

BuildPortFilter returns a WinDivert filter expression covering all target ports from enabled rules. Returns ("", false) if any enabled rule uses wildcard ports ("*" or empty), meaning the filter cannot be narrowed. Rules must be added before calling Start() for port-specific filtering.

func (*RuleManager) DisableRule

func (rm *RuleManager) DisableRule(id uint32) error

DisableRule disables a rule by ID.

func (*RuleManager) EnableRule

func (rm *RuleManager) EnableRule(id uint32) error

EnableRule enables a rule by ID.

func (*RuleManager) HasActiveRules

func (rm *RuleManager) HasActiveRules() bool

HasActiveRules returns true if there is at least one enabled rule.

func (*RuleManager) Match

func (rm *RuleManager) Match(processPath string, destIP uint32, destPort uint16) Action

Match evaluates rules in order and returns the action for the given process/dest. processPath is the full path of the process executable. destIP is in network byte order, destPort is in host byte order.

func (*RuleManager) RemoveRule

func (rm *RuleManager) RemoveRule(id uint32) error

RemoveRule removes a rule by ID.

type TCPHeader

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

TCPHeader provides read/write access to TCP header fields in a raw packet buffer.

func (*TCPHeader) DataOffset

func (h *TCPHeader) DataOffset() int

DataOffset returns the TCP header length in bytes.

func (*TCPHeader) DstPort

func (h *TCPHeader) DstPort() uint16

DstPort returns the TCP destination port.

func (*TCPHeader) Flags

func (h *TCPHeader) Flags() uint8

Flags returns the TCP flags byte.

func (*TCPHeader) IsFIN

func (h *TCPHeader) IsFIN() bool

IsFIN returns true if the FIN flag is set.

func (*TCPHeader) IsRST

func (h *TCPHeader) IsRST() bool

IsRST returns true if the RST flag is set.

func (*TCPHeader) IsSYN

func (h *TCPHeader) IsSYN() bool

IsSYN returns true if the SYN flag is set (and ACK is not).

func (*TCPHeader) SetDstPort

func (h *TCPHeader) SetDstPort(port uint16)

SetDstPort sets the TCP destination port.

func (*TCPHeader) SetSrcPort

func (h *TCPHeader) SetSrcPort(port uint16)

SetSrcPort sets the TCP source port.

func (*TCPHeader) SrcPort

func (h *TCPHeader) SrcPort() uint16

SrcPort returns the TCP source port.

Jump to

Keyboard shortcuts

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