WinDivert is a user-mode capture/sniffing/modification/blocking/re-injection package for Windows Vista, Windows Server 2008, Windows 7, and Windows 8. WinDivert can be used to implement user-mode packet filters, packet sniffers, firewalls, NAT, VPNs, tunneling applications, etc., without the need to write kernel-mode code.
The main features of the WinDivert are:
WinDivert provides similar functionality to divert sockets from FreeBSD/MacOS, NETLINK sockets from Linux, and some commercial packages such as WinPkFilter for Windows. WinDivert also supports passive packet sniffing similar to Winpcap.
Note that pre-built WinDivert binary distributions are available from the WinDivert website. Most users do not need to build their own version of WinDivert from source.
The source code for WinDivert is available for download at
https://github.com/basil00/DivertTo build the WinDivert drivers from source:
wddk-build.batThis will build the install\WDDK\i386\WinDivert32.sys driver.
To build the WinDivert user-mode library (WinDivert.dll) and sample programs:
sh mingw-build.shThis will the user-mode library and sample programs which will be placed in the install\MINGW subdirectory.
The generated WinDivert.dll/WinDivert.lib files should be compatible with all major compilers, including both MinGW and Visual Studio.
If you built your own WinDivert32.sys/WinDivert64.sys drivers, they must be digitally signed before they can be used. See Driver Signing Requirements for Windows for more information.
Note that the pre-built WinDivert32.sys/WinDivert64.sys drivers from the official WinDivert distribution are already digitally signed.
WinDivert does not require any special installation. Depending on your target configuration, simply place the following files in your application's home directory:
| Application Type | Target Windows Type | Files Required |
|---|---|---|
| 32-bit | 32-bit Windows only | WinDivert.dll (32-bit version) and WinDivert32.sys |
| 64-bit | 64-bit Windows only | WinDivert.dll (64-bit version) and WinDivert64.sys |
| 32-bit | Both 32-bit and 64-bit Windows | WinDivert.dll (32-bit version), WinDivert32.sys, and WinDivert64.sys |
The WinDivert driver is automatically (and silently) installed on demand whenever your application calls WinDivertOpen(). The calling application must have Administrator privileges.
To uninstall, simply delete the WinDivert.dll, WinDivert32.sys, and WinDivert64.sys files. If already running, the WinDivert driver will be automatically uninstalled during the next machine reboot. The WinDivert driver can also be manually removed by issuing the following commands at the command prompt
sc stop WinDivert2.0 sc delete WinDivert2.0Note that this is not recommended as it will interfere with other applications that depend on WinDivert.
To use the WinDivert package, a program/application must:
#include "windivert.h"
typedef enum
{
WINDIVERT_LAYER_NETWORK = 0,
WINDIVERT_LAYER_NETWORK_FORWARD,
WINDIVERT_LAYER_FLOW,
WINDIVERT_LAYER_SOCKET,
WINDIVERT_LAYER_REFLECT,
} WINDIVERT_LAYER, *PWINDIVERT_LAYER;
|
WinDivert supports several layers for diverting or capturing network packets/events. Each layer has its own capabilities, such as the ability to block events or to inject new events, etc. The list of supported WinDivert layers is summarized below:
| Layer | Capability | Description | |||
|---|---|---|---|---|---|
| Block? | Inject? | Data? | PID? | ||
| WINDIVERT_LAYER_NETWORK | ✔ | ✔ | ✔ | Network packets to/from the local machine. | |
| WINDIVERT_LAYER_NETWORK_FORWARD | ✔ | ✔ | ✔ | Network packets passing through the local machine. | |
| WINDIVERT_LAYER_FLOW | ✔ | Network flow established/deleted events. | |||
| WINDIVERT_LAYER_SOCKET | ✔ | ✔ | Socket operation events. | ||
| WINDIVERT_LAYER_REFLECT | ✔ | ✔ | WinDivert handle events. | ||
Here, the layer capabilities are:
The WINDIVERT_LAYER_NETWORK (and
WINDIVERT_LAYER_NETWORK_FORWARD) layers
allow the user application to capture/block/inject network packets passing
to/from (and through) the local machine.
These represent the traditional
WinDivert layers.
Only a single event is supported:
The WINDIVERT_LAYER_FLOW layer captures information about
network flow establishment/deletion events.
Here, a flow represents either (1) a
TCP connection, or (2) an implicit flow
created by the first
sent/received packet for non-TCP traffic, e.g., UDP.
The WINDIVERT_LAYER_FLOW layer supports two events:
Old flows are deleted when the corresponding connection is closed (for TCP), or on a timeout (non-TCP). Flow events can be captured, but never blocked or injected. Process ID information is available at this layer. Due to technical limitations, the WINDIVERT_LAYER_FLOW layer cannot capture events that occurred before the WinDivert handle was opened.
The WINDIVERT_LAYER_SOCKET layer captures/blocks events that correspond to socket operations, such as:
Socket events can be blocked but not injected. Process ID information is available at this layer. Due to technical limitations, the WINDIVERT_LAYER_SOCKET layer cannot capture events that occurred before the WinDivert handle was opened.
Finally, the WINDIVERT_LAYER_REFLECT layer captures events related to WinDivert itself, such as:
These events can be captured, but not injected nor blocked.
Process ID information is available at this layer,
meaning that it is possible to determine which (if any) process is using
WinDivert.
The layer also returns a pseudo packet
that encodes the filter string
associated with the event.
The WINDIVERT_LAYER_REFLECT layer can also capture events that
occurred before the handle was opened.
typedef struct
{
UINT32 IfIdx;
UINT32 SubIfIdx;
} WINDIVERT_DATA_NETWORK, *PWINDIVERT_DATA_NETWORK;
typedef struct
{
UINT32 ProcessId;
UINT32 LocalAddr[4];
UINT32 RemoteAddr[4];
UINT16 LocalPort;
UINT16 RemotePort;
UINT8 Protocol;
} WINDIVERT_DATA_FLOW, *PWINDIVERT_DATA_FLOW;
typedef struct
{
UINT32 ProcessId;
UINT32 LocalAddr[4];
UINT32 RemoteAddr[4];
UINT16 LocalPort;
UINT16 RemotePort;
UINT8 Protocol;
} WINDIVERT_DATA_SOCKET, *PWINDIVERT_DATA_SOCKET;
typedef struct
{
INT64 Timestamp;
UINT32 ProcessId;
WINDIVERT_LAYER Layer;
UINT64 Flags;
INT16 Priority;
} WINDIVERT_DATA_REFLECT, *PWINDIVERT_DATA_REFLECT;
typedef struct
{
INT64 Timestamp;
UINT64 Layer:8;
UINT64 Event:8;
UINT64 Outbound:1;
UINT64 Loopback:1;
UINT64 Impostor:1;
UINT64 IPv6:1;
UINT64 PseudoIPChecksum:1;
UINT64 PseudoTCPChecksum:1;
UINT64 PseudoUDPChecksum:1;
union
{
WINDIVERT_DATA_NETWORK Network;
WINDIVERT_DATA_FLOW Flow;
WINDIVERT_DATA_SOCKET Socket;
WINDIVERT_DATA_REFLECT Reflect;
};
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;
|
Fields
impostorpackets, 0 otherwise.
Remarks
The WINDIVERT_ADDRESS structure
represents the "address" of a captured or injected packet.
The address includes the packet's timestamp, layer, event, flags, and
layer-specific data.
All fields are set by WinDivertRecv()
when the packet/event is captured.
Only some fields are used by
WinDivertSend() when a packet
is injected.
The Timestamp indicates when the packet/event was first captured by WinDivert. It uses the same clock as QueryPerformanceCounter().
The Layer indicates the layer parameter (WINDIVERT_LAYER_*) that was passed to WinDivertOpen(). It is included in the address to make the structure self-contained.
The Event indicates the layer-specific event (WINDIVERT_EVENT_*) that was captured.
The Outbound flag is set for outbound packets/events, and is cleared for inbound or direction-less packets/events.
The Loopback flag is set for loopback packets. Note that Windows considers any packet originating from, and destined to, the current machine to be a loopback packet, so loopback packets are not limited to localhost addresses. Note that WinDivert considers loopback packets to be outbound only, and will not capture loopback packets on the inbound path.
The Impostor flag is set for impostor packets. An impostor packet is any packet injected by another driver rather than originating from the network or Windows TCP/IP stack. Impostor packets are problematic since they can cause infinite loops, where a packet injected by WinDivertSend() is captured again by WinDivertRecv(). For more information, see WinDivertSend().
The IPv6 flag is set for IPv6 packets/events, and cleared for IPv4 packets/events.
The Pseudo*Checksum flags indicate whether the packet uses full or pseudo checksums. Pseudo checksums are used when IP/TCP/UDP checksum offloading is enabled, meaning that the network hardware calculates/validates checksums rather than the Windows TCP/IP stack. Pseudo checksums may be arbitrary values. Typically, modified packets should preserve the Pseudo*Checksum flags.
The Network.* fields are only valid at the WINDIVERT_LAYER_NETWORK and WINDIVERT_LAYER_NETWORK_FORWARD layers. The Network.IfIdx/Network.SubIfIdx indicate the packet's network adapter (a.k.a. interface) index. These values are ignored for outbound packets.
The Flow.* fields are only valid at the WINDIVERT_LAYER_FLOW layer. The Flow.ProcessId is the ID of the process that created the flow (for outbound), or receives the flow (for inbound). The (Flow.LocalAddr, Flow.LocalPort, Flow.RemoteAddr, Flow.RemotePort, Flow.Protocol) fields form the network 5-tuple associated with the flow. For IPv4, the Flow.LocalAddr and Flow.RemoteAddr fields will be IPv4-mapped IPv6 addresses, e.g. the IPv4 address X.Y.Z.W will be represented by ::ffff:X.Y.Z.W.
The Socket.* fields are only valid at the WINDIVERT_LAYER_SOCKET layer. The Socket.ProcessId is the ID of the process that executed the socket operation. The (Socket.LocalAddr, Socket.LocalPort, Socket.RemoteAddr, Socket.RemotePort, Socket.Protocol) fields form the network 5-tuple associated with the operation. For IPv4, the Socket.LocalAddr and Socket.RemoteAddr fields will be IPv4-mapped IPv6 addresses. The WINDIVERT_EVENT_SOCKET_BIND and WINDIVERT_EVENT_SOCKET_LISTEN events will occur before a connection attempt has been made, meaning that the Socket.RemoteAddr and Socket.RemotePort fields for these events will be zero.
The Reflect.* fields are only valid at the WINDIVERT_LAYER_REFLECT layer. The Reflect.ProcessId is the ID of the process that opened the WinDivert handle. The Reflect.Timestamp field is a timestamp indicating when the handle was opened, using the same clock as QueryPerformanceCounter(). The Reflect.Layer, Reflect.Flags, and Reflect.Priority fields correspond to the WinDivertOpen() parameters of the opened handle.
Most address fields are ignored by WinDivertSend(). The exceptions are Outbound (for WINDIVERT_LAYER_NETWORK only), Impostor, PseudoIPChecksum, PseudoTCPChecksum, PseudoUDPChecksum, Network.IfIdx and Network.SubIfIdx.
HANDLE WinDivertOpen(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__in INT16 priority,
__in UINT64 flags
);
|
Parameters
Return Value
A valid WinDivert handle on success, or
INVALID_HANDLE_VALUE if an error occurred.
Use GetLastError() to get the reason for the error.
Common errors include:
| Name | Code | Description |
|---|---|---|
| ERROR_FILE_NOT_FOUND | 2 | The driver files WinDivert32.sys or WinDivert64.sys were not found. |
| ERROR_ACCESS_DENIED | 5 | The calling application does not have Administrator privileges. |
| ERROR_INVALID_PARAMETER | 87 | This indicates an invalid packet filter string, layer, priority, or flags. |
| ERROR_INVALID_IMAGE_HASH | 577 | The WinDivert32.sys or WinDivert64.sys driver does not have a valid digital signature (see the driver signing requirements above). |
| ERROR_SERVICE_DOES_NOT_EXIST | 1060 | The handle was opened with the WINDIVERT_FLAG_NO_INSTALL flag and the WinDivert driver is not already installed. |
| ERROR_DRIVER_BLOCKED | 1275 |
This error occurs for various reasons, including:
|
| EPT_S_NOT_REGISTERED | 1753 | This error occurs when the Base Filtering Engine service has been disabled. |
Remarks
Opens a WinDivert handle for the given filter.
Unless otherwise specified by flags, any packet or event that
matches the filter will be diverted to the handle.
Diverted packets/events can be read by the application with
WinDivertRecv().
A typical application is only interested in a subset of all network traffic or events. In this case the filter should match as closely as possible to the subset of interest. This avoids unnecessary overheads introduced by diverting packets to the user-mode application. See the filter language section for more information.
The layer of the WinDivert handle is determined by the layer parameter. See WINDIVERT_LAYER for more information. Currently the following layers are supported:
| Layer | Description |
|---|---|
| WINDIVERT_LAYER_NETWORK = 0 | Network packets to/from the local machine. This is the default layer. |
| WINDIVERT_LAYER_NETWORK_FORWARD | Network packets passing through the local machine. |
| WINDIVERT_LAYER_FLOW | Network flow established/deleted events. |
| WINDIVERT_LAYER_SOCKET | Socket operation events. |
| WINDIVERT_LAYER_REFLECT | WinDivert handle events. |
Different WinDivert handles can be assigned different priorities by the priority parameter. Packets are diverted to higher priority handles before lower priority handles. Packets injected by a handle are then diverted to the next priority handle, and so on, provided the packet matches the handle's filter. A packet is only diverted once per priority level, so handles should not share priority levels unless they use mutually exclusive filters. Otherwise it is not defined which handle will receive the packet first. Lower priority values represent higher priorities, with -30000 being the highest priority, 0 the middle (and a good default) priority, and 30000 the lowest priority.
Different flags affect how the opened handle behaves. The following flags are supported:
| Flag | Description |
|---|---|
| WINDIVERT_FLAG_SNIFF | This flag opens the WinDivert handle in packet sniffing mode. In packet sniffing mode the original packet is not dropped-and-diverted (the default) but copied-and-diverted. This mode is useful for implementing packet sniffing tools similar to those applications that currently use Winpcap. |
| WINDIVERT_FLAG_DROP | This flag indicates that the user application does not intend to read matching packets with WinDivertRecv(), instead the packets should be silently dropped. This is useful for implementing simple packet filters using the WinDivert filter language. |
| WINDIVERT_FLAG_RECV_ONLY |
This flags forces the handle into receive onlymode which effectively disables WinDivertSend(). This means that it is possible to block/capture packets or events but not inject them. |
| WINDIVERT_FLAG_READ_ONLY | An alias for WINDIVERT_FLAG_RECV_ONLY. |
| WINDIVERT_FLAG_SEND_ONLY |
This flags forces the handle into send onlymode which effectively disables WinDivertRecv(). This means that it is possible to inject packets or events, but not block/capture them. |
| WINDIVERT_FLAG_WRITE_ONLY | An alias for WINDIVERT_FLAG_SEND_ONLY. |
| WINDIVERT_FLAG_RECV_PARTIAL |
This flags enables partial receives. By default, if a packet is too big for the buffer passed to WinDivertRecv(), the operation will fail with ERROR_INSUFFICIENT_BUFFER. This flag silences this error and only copies part of the packet up to the buffer size. The WINDIVERT_FLAG_RECV_PARTIAL behavior was the default on older (pre-2.0) versions of WinDivert. |
WINDIVERT_FLAG_NO_INSTALL | This flags causes WinDivertOpen() to fail with ERROR_SERVICE_DOES_NOT_EXIST if the WinDivert driver is not already installed. This flag is useful for querying the WinDivert state using a WINDIVERT_LAYER_REFLECT handle. |
Note that any combination of (WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_DROP) or (WINDIVERT_FLAG_RECV_ONLY | WINDIVERT_FLAG_SEND_ONLY) or (WINDIVERT_FLAG_RECV_PARTIAL | WINDIVERT_FLAG_SEND_ONLY) are considered invalid.
Some layers have mandatory flags, as listed below:
| Layer | Required Flags |
|---|---|
| WINDIVERT_LAYER_FLOW | WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_RECV_ONLY |
| WINDIVERT_LAYER_SOCKET | WINDIVERT_FLAG_RECV_ONLY |
| WINDIVERT_LAYER_REFLECT | WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_RECV_ONLY |
BOOL WinDivertRecv(
__in HANDLE handle,
__out PVOID pPacket,
__in UINT packetLen,
__out_opt PWINDIVERT_ADDRESS pAddr,
__out_opt UINT *recvLen
);
|
Parameters
Return Value
TRUE if a packet was successfully received, or FALSE if
an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Receives a diverted packet that matched the filter passed to
WinDivertOpen().
The received packet is guaranteed to match the filter.
The contents of the captured packet are written to pPacket. If the captured packet is larger than the pPacket buffer length, then the packet will be truncated. If recvLen is non-NULL, then the total number of bytes written to pPacket is placed there. If non-NULL, the address of the captured packet is written to pAddr.
An application should call WinDivertRecv() as soon as possible after a successful call to WinDivertOpen(). When a WinDivert handle is open, any packet that matches the filter will be captured and queued until handled by WinDivertRecv(). Packets are not queued indefinitely, and if not handled in a timely manner, any captured packet may be dropped. The amount of time a packet is queued can be controlled with the WinDivertSetParam() function.
Captured packets are guaranteed to have correct checksums, or pseudo checksums, as indicated by the Pseudo*Checksum flags from the WINDIVERT_ADDRESS.
WinDivertRecv() should not be used on any WinDivert handle created with the WINDIVERT_FLAG_DROP set.
BOOL WinDivertRecvEx(
__in HANDLE handle,
__out PVOID pPacket,
__in UINT packetLen,
__in UINT64 flags,
__out_opt PWINDIVERT_ADDRESS pAddr,
__out_opt UINT *recvLen,
__inout_opt LPOVERLAPPED lpOverlapped
);
|
Parameters
Return Value
TRUE if a packet was successfully received, or FALSE
otherwise.
Use GetLastError() to get the reason.
The error code ERROR_IO_PENDING indicates that the overlapped
operation has been successfully initiated and that completion will be
indicated at a later time.
All other codes indicate an error.
Remarks
This function is equivalent to
WinDivertRecv() except that it
supports overlapped I/O via the lpOverlapped parameter.
BOOL WinDivertSend(
__in HANDLE handle,
__in PVOID pPacket,
__in UINT packetLen,
__in PWINDIVERT_ADDRESS pAddr,
__out_opt UINT *sendLen
);
|
Parameters
Return Value
TRUE if a packet was successfully injected, or FALSE if
an error occurred.
Use GetLastError() to get the reason for the error.
Common errors include:
| Name | Code | Description |
|---|---|---|
| ERROR_HOST_UNREACHABLE | 1232 |
This error occurs when an impostor packet (with
pAddr->Impostor set to 1) is injected and the
ip.TTL or ipv6.HopLimit field goes to zero.
This is a defense of last resortagainst infinite loops caused by impostor packets. |
Note that a return value of TRUE does not necessarily mean the packet was accepted by the Windows TCP/IP stack. For better error messages (at the cost of performance), pass the WINDIVERT_FLAG_DEBUG flag to WinDivertOpen().
Remarks
Injects a packet into the network stack.
The injected packet may be one received from
WinDivertRecv(), or a
modified version, or a completely new packet.
Injected packets can be captured and diverted again by other WinDivert
handles with lower priorities.
The pAddr parameter determines how the packet is injected. If the Direction field is WINDIVERT_DIRECTION_OUTBOUND, the packet is injected into the outbound path (i.e. a packet leaving this computer). Else, if Direction is WINDIVERT_DIRECTION_INBOUND, the packet is injected into the inbound path (i.e. a packet arriving at this computer). Note that the Direction field, and not the IP addresses in the injected packet, is used to determine the packet's direction.
For packets injected into the inbound path, the IfIdx and SubIfIdx fields are assumed to contain valid interface numbers. These may be retrieved from WinDivertRecv() (for packet modification), or from the IP Helper API.
For outbound injected packets, the IfIdx and SubIfIdx fields are currently ignored and may be arbitrary values. Injecting an inbound packet on the outbound path may work (for some types of packets), however this should be considered "undocumented" behavior, and may be changed in the future.
For impostor packets (where the Impostor field of pAddr set to 1) WinDivert will automatically decrement the ip.TTL or ipv6.HopLimit fields for the injected packet. This is to mitigate infinite loops since WinDivert cannot prevent impostor packets from being captured again by WinDivertRecv().
Injected packets must have the correct checksums. Correct checksums can be calculated using the WinDivertHelperCalcChecksums() function. Note that packets returned by WinDivertRecv() are guaranteed to have correct checksums.
When pseudo checksums are used, the WinDivertSend() function may modify the contents of the memory pointed to by the pPacket and pAddr parameters. Thus, it is important that this memory is not read-only.
BOOL WinDivertSendEx(
__in HANDLE handle,
__in PVOID pPacket,
__in UINT packetLen,
__in UINT64 flags,
__in PWINDIVERT_ADDRESS pAddr,
__out_opt UINT *sendLen,
__inout_opt LPOVERLAPPED lpOverlapped
);
|
Parameters
Return Value
TRUE if a packet was successfully injected, or FALSE
otherwise.
Use GetLastError() to get the reason.
The error code ERROR_IO_PENDING indicates that the overlapped
operation has been successfully initiated and that completion will be
indicated at a later time.
All other codes indicate an error.
Remarks
This function is equivalent to
WinDivertSend() except that it
supports overlapped I/O via the lpOverlapped parameter.
BOOL WinDivertShutdown(
__in HANDLE handle,
__in WINDIVERT_SHUTDOWN how);
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
This operation causes all or part of a WinDivert handle to be shut down.
The possible values for how are:
| How | Description |
|---|---|
| WINDIVERT_SHUTDOWN_RECV | Stop new packets being queued for WinDivertRecv(). |
| WINDIVERT_SHUTDOWN_SEND | Stop new packets being injected via WinDivertSend(). |
| WINDIVERT_SHUTDOWN_BOTH | Equivalent to (WINDIVERT_SHUTDOWN_RECV | WINDIVERT_SHUTDOWN_SEND). |
Note that previously queued packets can still be received after WINDIVERT_SHUTDOWN_RECV. When the packet queue is empty, WinDivertRecv() will fail with ERROR_NO_DATA.
BOOL WinDivertClose(
__in HANDLE handle
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Closes a WinDivert handle created by
WinDivertOpen().
BOOL WinDivertSetParam(
__in HANDLE handle,
__in WINDIVERT_PARAM param,
__in UINT64 value);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Sets a WinDivert parameter.
Currently, the following WinDivert parameters are defined.
| Parameter | Description |
|---|---|
| WINDIVERT_PARAM_QUEUE_LEN | Sets the maximum length of the packet queue for WinDivertRecv(). Currently the default value is 2048, the minimum is 16, and the maximum is 16384. |
| WINDIVERT_PARAM_QUEUE_TIME | Sets the minimum time, in milliseconds, a packet can be queued before it is automatically dropped. Packets cannot be queued indefinitely, and ideally, packets should be processed by the application as soon as is possible. Note that this sets the minimum time a packet can be queued before it can be dropped. The actual time may be exceed this value. Currently the default value is 1000 (1s), the minimum is 20 (20ms), and the maximum is 8000 (8s). |
| WINDIVERT_PARAM_QUEUE_SIZE | Sets the maximum number of bytes that can be stored in the packet queue for WinDivertRecv(). Currently the default value is 4194304 (4MB), the minimum is 65535 (64KB), and the maximum is 33554432 (32MB). |
BOOL WinDivertGetParam(
__in HANDLE handle,
__in WINDIVERT_PARAM param,
__out UINT64 *pValue);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Gets a WinDivert parameter.
See WinDivertSetParam() for the list
of parameters.
typedef struct
{
UINT8 HdrLength:4;
UINT8 Version:4;
UINT8 TOS;
UINT16 Length;
UINT16 Id;
UINT16 ...;
UINT8 TTL;
UINT8 Protocol;
UINT16 Checksum;
UINT32 SrcAddr;
UINT32 DstAddr;
} WINDIVERT_IPHDR, *PWINDIVERT_IPHDR;
|
Fields
See here
for more information.
Remarks
IPv4 header definition.
The following fields can only be get/set using the following macro definitions:
typedef struct
{
UINT32 Version:4;
UINT32 ...:28;
UINT16 Length;
UINT8 NextHdr;
UINT8 HopLimit;
UINT32 SrcAddr[4];
UINT32 DstAddr[4];
} WINDIVERT_IPV6HDR, *PWINDIVERT_IPV6HDR;
|
Remarks
IPv6 header definition.
The following fields can only be get/set using the following macro definitions:
typedef struct
{
UINT8 Type;
UINT8 Code;
UINT16 Checksum;
UINT32 Body;
} WINDIVERT_ICMPHDR, *PWINDIVERT_ICMPHDR;
|
Remarks
ICMP header definition.
typedef struct
{
UINT8 Type;
UINT8 Code;
UINT16 Checksum;
UINT32 Body;
} WINDIVERT_ICMPV6HDR, *PWINDIVERT_ICMPV6HDR;
|
Remarks
ICMPv6 header definition.
typedef struct
{
UINT16 SrcPort;
UINT16 DstPort;
UINT32 SeqNum;
UINT32 AckNum;
UINT16 Reserved1:4;
UINT16 HdrLength:4;
UINT16 Fin:1;
UINT16 Syn:1;
UINT16 Rst:1;
UINT16 Psh:1;
UINT16 Ack:1;
UINT16 Urg:1;
UINT16 Reserved2:2;
UINT16 Window;
UINT16 Checksum;
UINT16 UrgPtr;
} WINDIVERT_TCPHDR, *PWINDIVERT_TCPHDR;
|
Remarks
TCP header definition.
typedef struct
{
UINT16 SrcPort;
UINT16 DstPort;
UINT16 Length;
UINT16 Checksum;
} WINDIVERT_UDPHDR, *PWINDIVERT_UDPHDR;
|
Remarks
UDP header definition.
BOOL WinDivertHelperParsePacket(
__in PVOID pPacket,
__in UINT packetLen,
__out_opt PWINDIVERT_IPHDR *ppIpHdr,
__out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr,
__out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr,
__out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
__out_opt PWINDIVERT_TCPHDR *ppTcpHdr,
__out_opt PWINDIVERT_UDPHDR *ppUdpHdr,
__out_opt PVOID *ppData,
__out_opt UINT *pDataLen
);
|
Parameters
Return Value
TRUE if all expected (non-NULL) outputs were present,
FALSE otherwise.
Note that FALSE may sometimes be a legitimate return value, e.g.,
when both ppIpHdr and ppIpv6Hdr are non-NULL.
Remarks
Parses a raw packet (e.g. from WinDivertRecv()) into the
various packet headers and/or payloads that may or may not be present.
Each output parameter may be NULL or non-NULL. For non-NULL parameters, this function will write the pointer to the corresponding header/payload if it exists, or will write NULL otherwise. Any non-NULL pointer that is returned
This function does not do any verification of the header/payload contents beyond checking the header length and any other minimal information required for parsing.
UINT64 WinDivertHelperHashPacket(
__in const VOID *pPacket,
__in UINT packetLen,
__in UINT64 seed = 0
);
|
Parameters
Return Value
A 64bit hash value.
Remarks
Calculates a 64bit hash value of the given packet.
Note that the hash function depends on the packet's
IP and transport headers only, and not the payload of the packet.
That said, a weak dependency on the payload will exist if the
TCP/UDP checksums are valid.
The hash function itself is based on the
xxHash algorithm
and is not cryptographic.
The optional seed value is also incorporated into the hash.
BOOL WinDivertHelperParseIPv4Address(
__in const char *addrStr,
__out_opt UINT32 *pAddr
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Parses an IPv4 address stored in addrStr.
If pAddr is non-NULL, the result is be stored in
host-byte-order.
Use WinDivertHelperHtonl()
to convert the result into network-byte-order.
BOOL WinDivertHelperParseIPv6Address(
__in const char *addrStr,
__out_opt UINT32 *pAddr
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Parses an IPv6 address stored in addrStr.
If pAddr is non-NULL, the buffer assumed
to be large enough to hold a 16-byte IPv6 address.
The result is stored in host-byte-order.
Use WinDivertHelperHtonIpv6Address()
to convert the result into network-byte-order.
BOOL WinDivertHelperFormatIPv4Address(
__in UINT32 addr,
__out char *buffer,
__in UINT bufLen
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Convert an IPv4 address into a string.
BOOL WinDivertHelperFormatIPv6Address(
__in const UINT32 *pAddr,
__out char *buffer,
__in UINT bufLen
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Convert an IPv6 address into a string.
UINT WinDivertHelperCalcChecksums(
__inout PVOID pPacket,
__in UINT packetLen,
__in_opt PWINDIVERT_ADDRESS pAddr,
__in UINT64 flags
);
|
Parameters
Return Value
The number of checksums calculated.
Remarks
(Re)calculates the checksum for any IPv4/ICMP/ICMPv6/TCP/UDP checksum present
in the given packet.
Individual checksum calculations may be disabled via the appropriate flag.
Typically this function should be invoked on a modified packet before it is
injected with WinDivertSend().
By default this function will calculate each checksum from scratch, even if the existing checksum is correct. This may be inefficient for some applications. For better performance, incremental checksum calculations should be used instead (not provided by this API).
If pAddr is non-NULL, this function will avoid calculating the full checksum if the Pseudo*Checksum flag is set in the corresponding WINDIVERT_ADDRESS structure. The address structure should be the same as the one passed to WinDivertSend() in order to (re)inject the packet.
BOOL WinDivertHelperCompileFilter(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__out_opt char *object,
__in UINT objLen,
__out_opt const char **errorStr,
__out_opt UINT *errorPos
);
|
Parameters
Return Value
TRUE if the packet filter compilation is successful, FALSE
otherwise.
Remarks
Compiles the given packet filter string into a compact object
representation that is optionally stored in object if non-NULL.
The object
representation is a valid null terminated C string, but is
otherwise opaque and not meant to be human readable.
The object representation can be passed to all WinDivert functions,
such as WinDivertOpen(), in place of the
human-readable filter string equivalent.
The compilation operation will succeed if the given filter string is valid with respect to the filter language. Otherwise, if the filter is invalid, then a human readable description of the error is returned by errorStr (if non-NULL), and the error's position is returned by errorPos (if non-NULL).
Note that all strings returned through errorStr are global static objects, and therefore do not need to be deallocated.
BOOL WinDivertHelperEvalFilter(
__in const char *filter,
__in PVOID pPacket,
__in UINT packetLen,
__in PWINDIVERT_ADDRESS pAddr
);
|
Parameters
Return Value
TRUE if the packet matches the filter string,
FALSE otherwise.
Remarks
Evaluates the given packet against the given packet filter string.
This function returns TRUE if the packet matches, and
returns FALSE otherwise.
This function also returns FALSE if an error occurs, in which case GetLastError() can be used to get the reason for the error. Otherwise, if no error occurred, GetLastError() will return 0.
Note that this function is relatively slow since the packet filter string will be (re)compiled for each call. This overhead can be minimized by pre-compiling the filter string into the object representation using the WinDivertHelperCompileFilter() function.
BOOL WinDivertHelperEvalFilter(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__out char *buffer,
__in UINT bufLen
);
|
Parameters
Return Value
TRUE if successful, FALSE if an error occurred.
Use GetLastError() to get the reason for the error.
Remarks
Formats the given filter string or object.
This function is mainly useful for decompiling
the filter object
representation back into a human-readable filter string representation.
One application is the WINDIVERT_LAYER_REFLECT layer,
where a pseudo packet containing a filter object is returned by
WinDivertRecv().
UINT16 WinDivertHelperNtohs(
__in UINT16 x
);
UINT32 WinDivertHelperNtohl(
__in UINT32 x
);
UINT64 WinDivertHelperNtohll(
__in UINT64 x
);
void WinDivertHelperNtohIpv6Address(
__in const UINT *inAddr,
__out UINT *outAddr
);
|
Return Value
The output value in host byte order.
Remarks
Converts a value/IPv6-address from network to host byte-order.
UINT16 WinDivertHelperHtons(
__in UINT16 x
);
UINT32 WinDivertHelperHtonl(
__in UINT32 x
);
UINT64 WinDivertHelperHtonll(
__in UINT64 x
);
void WinDivertHelperHtonIpv6Address(
__in const UINT *inAddr,
__out UINT *outAddr
);
|
Return Value
The output value in network byte order.
Remarks
Converts a value/IPv6-address from host to network byte-order.
The WinDivertOpen() function accepts a string containing a filter expression. Only packets that match the filter expression are diverted. Any other packet is allowed to continue as per normal.
Filter allows an application to select only the subset of traffic that is of interest. For example, a URL blacklist filter would only be interested in packets that contain URLs. This could be achieved via the following filter.
HANDLE handle = WinDivertOpen(
"outbound and "
"tcp.PayloadLength > 0 and "
"tcp.DstPort == 80", 0, 0, 0);
This filter specifies that we should only divert traffic that is
A filter is a Boolean expression of the form:
FILTER := true | false | FILTER and FILTER | FILTER or FILTER | (FILTER) | (FILTER? FILTER: FILTER) | TEST
C-style syntax &&, ||, and ! may also
be used instead of and, or, and not, respectively.
C-style conditional operators are also supported,
where the expression (A? B: C) evaluates to:
TEST := TEST0 | not TEST0
TEST0 := FIELD | FIELD op VAL
where op is one of the following:
| Operator | Description |
|---|---|
| == or = | Equal |
| != | Not equal |
| < | Less-than |
| > | Greater-than |
| <= | Less-than-or-equal |
| >= | Greater-than-or-equal |
and VAL is a decimal number, hexadecimal number, or IP address. If the "op VAL" is missing, the test is implicitly "FIELD != 0".
Finally a field is some property about the packet. The possible fields are:
| Field | Layer | Description | ||||
|---|---|---|---|---|---|---|
| NETWORK | FORWARD | FLOW | SOCKET | REFLECT | ||
| zero | ✔ | ✔ | ✔ | ✔ | ✔ | The value zero |
| event | ✔ | ✔ | ✔ | ✔ | ✔ | The event |
| outbound | ✔ | ✔ | Is outbound? | |||
| inbound | ✔ | ✔ | Is inbound? | |||
| ifIdx | ✔ | ✔ | Interface index | |||
| subIfIdx | ✔ | ✔ | Sub-interface index | |||
| loopback | ✔ | ✔ | ✔ | Is loopback packet? | ||
| impostor | ✔ | ✔ | Is impostor packet? | |||
| processId | ✔ | ✔ | ✔ | Process ID | ||
| random8 | ✔ | ✔ | 8-bit random number | |||
| random16 | ✔ | ✔ | 16-bit random number | |||
| random32 | ✔ | ✔ | 32-bit random number | |||
| layer | ✔ | The handle's layer | ||||
| priority | ✔ | The handle's priority | ||||
| packet[i] | ✔ | ✔ | The ith 8-bit word of the packet | |||
| packet16[i] | ✔ | ✔ | The ith 16-bit word of the packet | |||
| packet32[i] | ✔ | ✔ | The ith 32-bit word of the packet | |||
| ip | ✔ | ✔ | ✔ | ✔ | Is IPv4? | |
| ipv6 | ✔ | ✔ | ✔ | ✔ | Is IPv6? | |
| icmp | ✔ | ✔ | ✔ | ✔ | Is ICMP? | |
| icmpv6 | ✔ | ✔ | ✔ | ✔ | Is ICMPv6? | |
| tcp | ✔ | ✔ | ✔ | ✔ | Is TCP? | |
| udp | ✔ | ✔ | ✔ | ✔ | Is UDP? | |
| protocol | ✔ | ✔ | ✔ | The protocol | ||
| localAddr | ✔ | ✔ | ✔ | The local address | ||
| localPort | ✔ | ✔ | ✔ | The local port | ||
| remoteAddr | ✔ | ✔ | ✔ | The remote address | ||
| remotePort | ✔ | ✔ | ✔ | The remote port | ||
| ip.* | ✔ | ✔ | IPv4 fields (see WINDIVERT_IPHDR) | |||
| ipv6.* | ✔ | ✔ | IPv6 fields (see WINDIVERT_IPV6HDR) | |||
| icmp.* | ✔ | ✔ | ICMP fields (see WINDIVERT_ICMPHDR) | |||
| icmpv6.* | ✔ | ✔ | ICMPV6 fields (see WINDIVERT_ICMPV6HDR) | |||
| tcp.* | ✔ | ✔ | TCP fields (see WINDIVERT_TCPHDR) | |||
| tcp.PayloadLength | ✔ | ✔ | The TCP payload length | |||
| tcp.Payload[i] | ✔ | ✔ | The ith 8-bit word of the TCP payload | |||
| tcp.Payload16[i] | ✔ | ✔ | The ith 16-bit word of the TCP payload | |||
| tcp.Payload32[i] | ✔ | ✔ | The ith 32-bit word of the TCP payload | |||
| udp.* | ✔ | ✔ | UDP fields (see WINDIVERT_UDPHDR) | |||
| udp.PayloadLength | ✔ | ✔ | The UDP payload length | |||
| udp.Payload[i] | ✔ | ✔ | The ith 8-bit word of the UDP payload | |||
| udp.Payload16[i] | ✔ | ✔ | The ith 16-bit word of the UDP payload | |||
| udp.Payload32[i] | ✔ | ✔ | The ith 32-bit word of the UDP payload | |||
A test also fails if the field is missing. E.g. the test "tcp.DstPort == 80" will fail if the packet does not contain a TCP header.
HANDLE handle = WinDivertOpen(
"outbound and !loopback and "
"(tcp.DstPort == 80 or udp.DstPort == 53)",
0, 0, 0
);
HANDLE handle = WinDivertOpen(
"inbound and "
"tcp.Syn",
0, 0, 0
);
HANDLE handle = WinDivertOpen("true", 0, 0, 0);
HANDLE handle = WinDivertOpen("false", 0, 0, 0);
This is useful for packet injection.
The purpose of the filter is to help applications select the subset of all network traffic that the application is interested in. Ideally the filter should be
Some samples have been provided to demonstrate the WinDivert API. The sample programs are:
The samples are intended for educational purposes only, and are not fully-featured applications.
The following basic template for a WinDivert application. The basic idea is to open a WinDivert handle, then enter a capture-modify-reinject loop:
HANDLE handle; // WinDivert handle
WINDIVERT_ADDRESS addr; // Packet address
char packet[MAXBUF]; // Packet buffer
UINT packetLen;
handle = WinDivertOpen("...", 0, 0, 0); // Open some filter
if (handle == INVALID_HANDLE_VALUE)
{
// Handle error
exit(1);
}
// Main capture-modify-inject loop:
while (TRUE)
{
if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packetLen))
{
// Handle recv error
continue;
}
// Modify packet.
WinDivertHelperCalcChecksums(packet, packetLen, &addr, 0);
if (!WinDivertSend(handle, packet, packetLen, &addr, NULL))
{
// Handle send error
continue;
}
}
For applications that do not need to modify the packet, a better approach is
to open the WinDivert handle with the WINDIVERT_FLAG_SNIFF flag set,
and not re-inject the packet with
WinDivertSend().
See the netdump.exe sample program for an example of this usage.
There are some limitations to the WinDivert package. They are
WinDivert is dual-licensed, and is available under the GNU Lesser General Public License (LGPL) Version 3 or the GNU General Public License (GPL) Version 2. Please see the notices below:
LGPL version 3:
WinDivert is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
GPL version 2:
WinDivert is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.