From 317e6f1099178e157fbc1fc0f5f835b65662e3e2 Mon Sep 17 00:00:00 2001 From: basil00 Date: Mon, 12 Aug 2019 07:50:49 +0800 Subject: [PATCH] Implement new packet parser(s) for WinDivert. The new parser should better handle fragments as well as be consistent with the kernel-mode parser. The new parser can also handle truncated packets. --- CHANGELOG | 2 + VERSION | 2 +- dll/windivert.c | 2 + dll/windivert_helper.c | 72 +++++++- dll/windivert_shared.c | 370 +++++++++++++++++++------------------ include/windivert_device.h | 2 +- sys/windivert.c | 179 +++++++++++------- sys/windivert.rc | 8 +- test/test.c | 95 +++++++++- test/test_data.c | 53 ++++++ 10 files changed, 525 insertions(+), 260 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 740ed43..fdb22dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -308,3 +308,5 @@ WinDivert 2.1.0 - Fix BSOD caused by packets with missing or incomplete transport headers (introduced in 2.0.0). - Fix missing Flow.EndpointId and Flow.ParentEndpointId for IPv6 flows. +WinDivert 2.2.0 + - Implement new packet parser that correctly handles IP fragments. diff --git a/VERSION b/VERSION index 7ec1d6d..ccbccc3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 +2.2.0 diff --git a/dll/windivert.c b/dll/windivert.c index 323c27c..ae100e2 100644 --- a/dll/windivert.c +++ b/dll/windivert.c @@ -85,6 +85,8 @@ static UINT32 WinDivertDivTen128(UINT32 *a); #define UINT32_MAX 0xFFFFFFFF #endif +#define IPPROTO_MH 135 + #ifdef _MSC_VER #pragma intrinsic(memcpy) diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c index 0130db9..fa50e86 100644 --- a/dll/windivert_helper.c +++ b/dll/windivert_helper.c @@ -496,11 +496,79 @@ static PTOKEN_INFO WinDivertTokenLookup(PTOKEN_INFO token_info, return NULL; } +/* + * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. + */ +extern BOOL WinDivertHelperParsePacket(const VOID *pPacket, UINT packetLen, + PWINDIVERT_IPHDR *ppIPHeader, PWINDIVERT_IPV6HDR *ppIPv6Header, + UINT8 *pProtocol, PWINDIVERT_ICMPHDR *ppICMPHeader, + PWINDIVERT_ICMPV6HDR *ppICMPv6Header, PWINDIVERT_TCPHDR *ppTCPHeader, + PWINDIVERT_UDPHDR *ppUDPHeader, PVOID *ppData, UINT *pDataLen, + PVOID *ppNext, UINT *pNextLen) +{ + WINDIVERT_PACKET info; + if (!WinDivertHelperParsePacketEx(pPacket, packetLen, &info)) + { + return FALSE; + } + if (info.Truncated) + { + return FALSE; + } + + if (pProtocol != NULL) + { + *pProtocol = info.Protocol; + } + if (ppIPHeader != NULL) + { + *ppIPHeader = info.IPHeader; + } + if (ppIPv6Header != NULL) + { + *ppIPv6Header = info.IPv6Header; + } + if (ppICMPHeader != NULL) + { + *ppICMPHeader = info.ICMPHeader; + } + if (ppICMPv6Header != NULL) + { + *ppICMPv6Header = info.ICMPv6Header; + } + if (ppTCPHeader != NULL) + { + *ppTCPHeader = info.TCPHeader; + } + if (ppUDPHeader != NULL) + { + *ppUDPHeader = info.UDPHeader; + } + if (ppData != NULL) + { + *ppData = info.Payload; + } + if (pDataLen != NULL) + { + *pDataLen = info.PayloadLength; + } + if (ppNext != NULL) + { + *ppNext = (info.Extended? (PVOID)((UINT8 *)pPacket + packetLen): NULL); + } + if (pNextLen != NULL) + { + *pNextLen = (info.Extended? + packetLen - (info.HeaderLength + info.PayloadLength): 0); + } + + return TRUE; +} + /* * Expand a "macro" value. */ -static BOOL WinDivertExpandMacro(KIND kind, WINDIVERT_LAYER layer, - UINT32 *val) +static BOOL WinDivertExpandMacro(KIND kind, WINDIVERT_LAYER layer, UINT32 *val) { switch (kind) { diff --git a/dll/windivert_shared.c b/dll/windivert_shared.c index 0536d42..8029125 100644 --- a/dll/windivert_shared.c +++ b/dll/windivert_shared.c @@ -73,6 +73,21 @@ static UINT64 WinDivertMul64(UINT64 a, UINT64 b) #define WINDIVERT_MUL64(a, b) ((a) * (b)) #endif /* WIN32 */ +/* + * IPv6 fragment header. + */ +typedef struct +{ + UINT8 NextHdr; + UINT8 Reserved; + UINT16 FragOff0; + UINT32 Id; +} WINDIVERT_IPV6FRAGHDR, *PWINDIVERT_IPV6FRAGHDR; +#define WINDIVERT_IPV6FRAGHDR_GET_FRAGOFF(hdr) \ + (((hdr)->FragOff0) & 0xF8FF) +#define WINDIVERT_IPV6FRAGHDR_GET_MF(hdr) \ + ((((hdr)->FragOff0) & 0x0100) != 0) + #include "windivert_hash.c" /* @@ -96,6 +111,29 @@ typedef struct UINT32 NextHdr:8; } WINDIVERT_PSEUDOV6HDR, *PWINDIVERT_PSEUDOV6HDR; +/* + * Packet info. + */ +typedef struct +{ + UINT32 HeaderLength:17; + UINT32 FragOff:13; + UINT32 Fragment:1; + UINT32 MF:1; + UINT32 PayloadLength:16; + UINT32 Protocol:8; + UINT32 Truncated:1; + UINT32 Extended:1; + UINT32 Reserved1:6; + PWINDIVERT_IPHDR IPHeader; + PWINDIVERT_IPV6HDR IPv6Header; + PWINDIVERT_ICMPHDR ICMPHeader; + PWINDIVERT_ICMPV6HDR ICMPv6Header; + PWINDIVERT_TCPHDR TCPHeader; + PWINDIVERT_UDPHDR UDPHeader; + UINT8 *Payload; +} WINDIVERT_PACKET, *PWINDIVERT_PACKET; + /* * Streams. */ @@ -283,61 +321,11 @@ static void WinDivertSerializeFilter(PWINDIVERT_STREAM stream, WinDivertPutNul(stream); } -/* - * Skip well-known IPv6 extension headers. - */ -static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len) -{ - UINT hdrlen; - - while (TRUE) - { - if (*len <= 2) - { - return IPPROTO_NONE; - } - - hdrlen = (UINT)*(*header + 1); - switch (proto) - { - case IPPROTO_FRAGMENT: - hdrlen = 8; - break; - case IPPROTO_AH: - hdrlen += 2; - hdrlen *= 4; - break; - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - hdrlen++; - hdrlen *= 8; - break; - case IPPROTO_NONE: - return proto; - default: - return proto; - } - - if (hdrlen >= *len) - { - return IPPROTO_NONE; - } - - proto = **header; - *header += hdrlen; - *len -= hdrlen; - } -} - /* * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. */ -extern BOOL WinDivertHelperParsePacket(const VOID *pPacket, UINT packetLen, - PWINDIVERT_IPHDR *ppIpHdr, PWINDIVERT_IPV6HDR *ppIpv6Hdr, UINT8 *pProtocol, - PWINDIVERT_ICMPHDR *ppIcmpHdr, PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, - PWINDIVERT_TCPHDR *ppTcpHdr, PWINDIVERT_UDPHDR *ppUdpHdr, PVOID *ppData, - UINT *pDataLen, PVOID *ppNext, UINT *pNextLen) +static BOOL WinDivertHelperParsePacketEx(const VOID *pPacket, UINT packetLen, + PWINDIVERT_PACKET pInfo) { PWINDIVERT_IPHDR ip_header = NULL; PWINDIVERT_IPV6HDR ipv6_header = NULL; @@ -345,183 +333,181 @@ extern BOOL WinDivertHelperParsePacket(const VOID *pPacket, UINT packetLen, PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; PWINDIVERT_TCPHDR tcp_header = NULL; PWINDIVERT_UDPHDR udp_header = NULL; - UINT16 header_len; + PWINDIVERT_IPV6FRAGHDR frag_header; UINT8 protocol = 0; - PVOID data = NULL, next = NULL; - UINT data_len = 0, next_len = 0, packet_len; - BOOL success = FALSE; + UINT8 *data = NULL; + UINT packet_len, total_len, header_len, data_len = 0, frag_off = 0; + BOOL MF = FALSE, fragment = FALSE, is_ext_header; if (pPacket == NULL || packetLen < sizeof(WINDIVERT_IPHDR)) { - goto WinDivertHelperParsePacketExit; + return FALSE; } - data = (PVOID)pPacket; + data = (UINT8 *)pPacket; data_len = packetLen; ip_header = (PWINDIVERT_IPHDR)data; switch (ip_header->Version) { case 4: - if (data_len < sizeof(WINDIVERT_IPHDR) || - ip_header->HdrLength < 5) + if (packetLen < sizeof(WINDIVERT_IPHDR) || + ip_header->HdrLength < 5) { - ip_header = NULL; - goto WinDivertHelperParsePacketExit; + return FALSE; } - packet_len = (UINT)ntohs(ip_header->Length); - header_len = ip_header->HdrLength*sizeof(UINT32); - protocol = ip_header->Protocol; - if (data_len < header_len || data_len < packet_len || - packet_len < header_len) + total_len = (UINT)ntohs(ip_header->Length); + protocol = ip_header->Protocol; + header_len = ip_header->HdrLength * sizeof(UINT32); + if (total_len < header_len || packetLen < header_len) { - ip_header = NULL; - goto WinDivertHelperParsePacketExit; + return FALSE; } - else if (packet_len < data_len) - { - next = (PVOID)((UINT8 *)data + packet_len); - next_len = data_len - packet_len; - } - data = (PVOID)((UINT8 *)data + header_len); - data_len = packet_len - header_len; + frag_off = ntohs(WINDIVERT_IPHDR_GET_FRAGOFF(ip_header)); + MF = (WINDIVERT_IPHDR_GET_MF(ip_header) != 0); + fragment = (MF || frag_off != 0); + packet_len = (total_len < packetLen? total_len: packetLen); + data += header_len; + data_len = packet_len - header_len; break; + case 6: - ip_header = NULL; + ip_header = NULL; ipv6_header = (PWINDIVERT_IPV6HDR)data; - if (data_len < sizeof(WINDIVERT_IPV6HDR) || - data_len < ntohs(ipv6_header->Length) + - sizeof(WINDIVERT_IPV6HDR)) + if (packetLen < sizeof(WINDIVERT_IPV6HDR)) { - ipv6_header = NULL; - goto WinDivertHelperParsePacketExit; + return FALSE; } - protocol = ipv6_header->NextHdr; - packet_len = ntohs(ipv6_header->Length) + sizeof(WINDIVERT_IPV6HDR); - if (packet_len < data_len) + protocol = ipv6_header->NextHdr; + total_len = (UINT)ntohs(ipv6_header->Length) + + sizeof(WINDIVERT_IPV6HDR); + packet_len = (total_len < packetLen? total_len: packetLen); + data += sizeof(WINDIVERT_IPV6HDR); + data_len = packet_len - sizeof(WINDIVERT_IPV6HDR); + + while (frag_off == 0 && data_len >= 2) { - next = (PVOID)((UINT8 *)data + packet_len); - next_len = data_len - packet_len; + header_len = (UINT)data[1]; + is_ext_header = TRUE; + switch (protocol) + { + case IPPROTO_FRAGMENT: + header_len = 8; + if (fragment || data_len < header_len) + { + is_ext_header = FALSE; + break; + } + frag_header = (PWINDIVERT_IPV6FRAGHDR)data; + frag_off = ntohs( + WINDIVERT_IPV6FRAGHDR_GET_FRAGOFF(frag_header)); + MF = WINDIVERT_IPV6FRAGHDR_GET_MF(frag_header); + fragment = TRUE; + break; + case IPPROTO_AH: + header_len += 2; + header_len *= 4; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_MH: + header_len++; + header_len *= 8; + break; + default: + is_ext_header = FALSE; + break; + } + if (!is_ext_header || data_len < header_len) + { + break; + } + protocol = data[0]; + data += header_len; + data_len -= header_len; } - data = (PVOID)((UINT8 *)data + sizeof(WINDIVERT_IPV6HDR)); - data_len = packet_len - sizeof(WINDIVERT_IPV6HDR); - protocol = WinDivertSkipExtHeaders(protocol, (UINT8 **)&data, - &data_len); break; + default: - ip_header = NULL; - goto WinDivertHelperParsePacketExit; + return FALSE; + } + + if (frag_off != 0) + { + goto WinDivertHelperParsePacketExit; } - data = (data_len == 0? NULL: data); - success = TRUE; switch (protocol) { case IPPROTO_TCP: tcp_header = (PWINDIVERT_TCPHDR)data; if (data_len < sizeof(WINDIVERT_TCPHDR) || - tcp_header->HdrLength < 5 || - data_len < tcp_header->HdrLength*sizeof(UINT32)) + tcp_header->HdrLength < 5) { tcp_header = NULL; goto WinDivertHelperParsePacketExit; } - header_len = tcp_header->HdrLength*sizeof(UINT32); - data = ((UINT8 *)data + header_len); - data_len -= header_len; + header_len = tcp_header->HdrLength * sizeof(UINT32); + header_len = (header_len > data_len? data_len: header_len); break; + case IPPROTO_UDP: - udp_header = (PWINDIVERT_UDPHDR)data; - if (data_len < sizeof(WINDIVERT_UDPHDR) || - ntohs(udp_header->Length) != data_len) + if (data_len < sizeof(WINDIVERT_UDPHDR)) { - udp_header = NULL; goto WinDivertHelperParsePacketExit; } - data = ((UINT8 *)data + sizeof(WINDIVERT_UDPHDR)); - data_len -= sizeof(WINDIVERT_UDPHDR); + udp_header = (PWINDIVERT_UDPHDR)data; + header_len = sizeof(WINDIVERT_UDPHDR); break; + case IPPROTO_ICMP: - icmp_header = (PWINDIVERT_ICMPHDR)data; if (ip_header == NULL || data_len < sizeof(WINDIVERT_ICMPHDR)) { - icmp_header = NULL; goto WinDivertHelperParsePacketExit; } - data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPHDR)); - data_len -= sizeof(WINDIVERT_ICMPHDR); + icmp_header = (PWINDIVERT_ICMPHDR)data; + header_len = sizeof(WINDIVERT_ICMPHDR); break; + case IPPROTO_ICMPV6: - icmpv6_header = (PWINDIVERT_ICMPV6HDR)data; if (ipv6_header == NULL || data_len < sizeof(WINDIVERT_ICMPV6HDR)) { - icmpv6_header = NULL; goto WinDivertHelperParsePacketExit; } - data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPV6HDR)); - data_len -= sizeof(WINDIVERT_ICMPV6HDR); + icmpv6_header = (PWINDIVERT_ICMPV6HDR)data; + header_len = sizeof(WINDIVERT_ICMPV6HDR); break; - default: - break; - } - if (data_len == 0) - { - data = NULL; + default: + goto WinDivertHelperParsePacketExit; } + data += header_len; + data_len -= header_len; WinDivertHelperParsePacketExit: - if (pProtocol != NULL) + if (pInfo == NULL) { - *pProtocol = protocol; + return TRUE; } - if (ppIpHdr != NULL) - { - *ppIpHdr = ip_header; - } - if (ppIpv6Hdr != NULL) - { - *ppIpv6Hdr = ipv6_header; - } - if (ppIcmpHdr != NULL) - { - *ppIcmpHdr = icmp_header; - } - if (ppIcmpv6Hdr != NULL) - { - *ppIcmpv6Hdr = icmpv6_header; - } - if (ppTcpHdr != NULL) - { - *ppTcpHdr = tcp_header; - } - if (ppUdpHdr != NULL) - { - *ppUdpHdr = udp_header; - } - if (ppData != NULL) - { - *ppData = data; - } - if (pDataLen != NULL) - { - *pDataLen = data_len; - } - if (ppNext != NULL) - { - *ppNext = next; - } - if (pNextLen != NULL) - { - *pNextLen = next_len; - } - - if (ppNext == NULL && pNextLen == NULL && next != NULL) - { - success = FALSE; - } - - return success; + data = (data_len == 0? NULL: data); + pInfo->Protocol = (UINT32)protocol; + pInfo->Fragment = (fragment? 1: 0); + pInfo->MF = (MF? 1: 0); + pInfo->FragOff = (UINT32)frag_off; + pInfo->Truncated = (total_len < packetLen? 1: 0); + pInfo->Extended = (total_len > packetLen? 1: 0); + pInfo->Reserved1 = 0; + pInfo->IPHeader = ip_header; + pInfo->IPv6Header = ipv6_header; + pInfo->ICMPHeader = icmp_header; + pInfo->ICMPv6Header = icmpv6_header; + pInfo->TCPHeader = tcp_header; + pInfo->UDPHeader = udp_header; + pInfo->Payload = data; + pInfo->HeaderLength = (UINT32)(packet_len - data_len); + pInfo->PayloadLength = (UINT32)data_len; + return TRUE; } /* @@ -539,44 +525,59 @@ extern BOOL WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, PWINDIVERT_ICMPV6HDR icmpv6_header; PWINDIVERT_TCPHDR tcp_header; PWINDIVERT_UDPHDR udp_header; + WINDIVERT_PACKET info; UINT payload_len, checksum_len; + BOOL truncated; - if (!WinDivertHelperParsePacket(pPacket, packetLen, &ip_header, - &ipv6_header, NULL, &icmp_header, &icmpv6_header, &tcp_header, - &udp_header, NULL, &payload_len, NULL, NULL)) + if (!WinDivertHelperParsePacketEx(pPacket, packetLen, &info)) { return FALSE; } + ip_header = info.IPHeader; if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM)) { ip_header->Checksum = 0; ip_header->Checksum = WinDivertCalcChecksum(NULL, 0, ip_header, - ip_header->HdrLength*sizeof(UINT32)); + ip_header->HdrLength * sizeof(UINT32)); if (pAddr != NULL) { pAddr->IPChecksum = 1; } } - + + payload_len = info.PayloadLength; + truncated = (info.Truncated || info.MF || info.FragOff != 0); + + icmp_header = info.ICMPHeader; if (icmp_header != NULL) { if ((flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) != 0) { return TRUE; } + if (truncated) + { + return FALSE; + } icmp_header->Checksum = 0; icmp_header->Checksum = WinDivertCalcChecksum(NULL, 0, icmp_header, payload_len + sizeof(WINDIVERT_ICMPHDR)); return TRUE; } - + + icmpv6_header = info.ICMPv6Header; if (icmpv6_header != NULL) { if ((flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) != 0) { return TRUE; } + if (truncated) + { + return FALSE; + } + ipv6_header = info.IPv6Header; checksum_len = payload_len + sizeof(WINDIVERT_ICMPV6HDR); pseudo_header_len = WinDivertInitPseudoHeader(NULL, ipv6_header, IPPROTO_ICMPV6, checksum_len, pseudo_header); @@ -585,14 +586,20 @@ extern BOOL WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, pseudo_header_len, icmpv6_header, checksum_len); return TRUE; } - + + tcp_header = info.TCPHeader; if (tcp_header != NULL) { if ((flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) != 0) { return TRUE; } - checksum_len = payload_len + tcp_header->HdrLength*sizeof(UINT32); + if (truncated) + { + return FALSE; + } + checksum_len = payload_len + tcp_header->HdrLength * sizeof(UINT32); + ipv6_header = info.IPv6Header; pseudo_header_len = WinDivertInitPseudoHeader(ip_header, ipv6_header, IPPROTO_TCP, checksum_len, pseudo_header); tcp_header->Checksum = 0; @@ -604,15 +611,21 @@ extern BOOL WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, } return TRUE; } - + + udp_header = info.UDPHeader; if (udp_header != NULL) { if ((flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) != 0) { return TRUE; } + if (truncated) + { + return FALSE; + } // Full UDP checksum checksum_len = payload_len + sizeof(WINDIVERT_UDPHDR); + ipv6_header = info.IPv6Header; pseudo_header_len = WinDivertInitPseudoHeader(ip_header, ipv6_header, IPPROTO_UDP, checksum_len, pseudo_header); udp_header->Checksum = 0; @@ -626,6 +639,7 @@ extern BOOL WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, { pAddr->UDPChecksum = 1; } + return TRUE; } return TRUE; diff --git a/include/windivert_device.h b/include/windivert_device.h index 7eec0f0..0022318 100644 --- a/include/windivert_device.h +++ b/include/windivert_device.h @@ -45,7 +45,7 @@ #include "windivert.h" #define WINDIVERT_VERSION_MAJOR 2 -#define WINDIVERT_VERSION_MINOR 1 +#define WINDIVERT_VERSION_MINOR 2 #define WINDIVERT_MAGIC_DLL 0x4C4C447669645724ull #define WINDIVERT_MAGIC_SYS 0x5359537669645723ull diff --git a/sys/windivert.c b/sys/windivert.c index 7b61122..4bc61f4 100644 --- a/sys/windivert.c +++ b/sys/windivert.c @@ -289,6 +289,7 @@ typedef struct flow_s *flow_t; #define UINT8_MAX 0xFF #define UINT16_MAX 0xFFFF #define UINT32_MAX 0xFFFFFFFF +#define IPPROTO_MH 135 /* * Global state. @@ -5284,8 +5285,8 @@ static BOOL windivert_lookup_data(PNET_BUFFER buffer, UINT offset, INT idx, /* * Parse packet headers. */ -static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, - BOOL frag_mode, PWINDIVERT_IPHDR *ip_header_ptr, +static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer, + BOOL ipv4, BOOL frag_mode, PWINDIVERT_IPHDR *ip_header_ptr, PWINDIVERT_IPV6HDR *ipv6_header_ptr, PWINDIVERT_ICMPHDR *icmp_header_ptr, PWINDIVERT_ICMPV6HDR *icmpv6_header_ptr, PWINDIVERT_TCPHDR *tcp_header_ptr, PWINDIVERT_UDPHDR *udp_header_ptr, UINT8 *proto_ptr, UINT *header_len_ptr, @@ -5298,7 +5299,9 @@ static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; PWINDIVERT_TCPHDR tcp_header = NULL; PWINDIVERT_UDPHDR udp_header = NULL; - UINT8 proto = 0; + PWINDIVERT_IPV6FRAGHDR frag_header; + UINT8 protocol = 0; + UINT16 frag_off = 0; UINT header_len = 0; NTSTATUS status; @@ -5340,14 +5343,14 @@ static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, DEBUG("FILTER: REJECT (bad IPv4 packet)"); return FALSE; } + frag_off = RtlUshortByteSwap(WINDIVERT_IPHDR_GET_FRAGOFF(ip_header)); if (!frag_mode && - (WINDIVERT_IPHDR_GET_MF(ip_header) != 0 || - WINDIVERT_IPHDR_GET_FRAGOFF(ip_header) != 0)) + (WINDIVERT_IPHDR_GET_MF(ip_header) != 0 || frag_off != 0)) { DEBUG("FILTER: REJECT (fragment)"); return FALSE; } - proto = ip_header->Protocol; + protocol = ip_header->Protocol; NdisAdvanceNetBufferDataStart(buffer, ip_header_len, FALSE, NULL); } else @@ -5374,26 +5377,32 @@ static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, DEBUG("FILTER: REJECT (bad IPv6 packet)"); return FALSE; } - proto = ipv6_header->NextHdr; + protocol = ipv6_header->NextHdr; NdisAdvanceNetBufferDataStart(buffer, ip_header_len, FALSE, NULL); // Skip extension headers: - while (TRUE) + frag_header = NULL; + while (frag_off == 0) { - UINT8 *ext_header; - UINT ext_header_len; - BOOL isexthdr = TRUE; - - ext_header = (UINT8 *)NdisGetDataBuffer(buffer, 2, NULL, 1, 0); - if (ext_header == NULL) - { - break; - } - - ext_header_len = (UINT)ext_header[1]; - switch (proto) + UINT8 *ext_header = NULL; + UINT ext_header_len = 0; + BOOL is_ext_header; + switch (protocol) { case IPPROTO_FRAGMENT: + if (frag_header != NULL) + { + is_ext_header = FALSE; + break; + } + frag_header = (PWINDIVERT_IPV6FRAGHDR) + NdisGetDataBuffer(buffer, 8, NULL, 1, 0); + ext_header = (UINT8 *)frag_header; + if (frag_header == NULL) + { + is_ext_header = FALSE; + break; + } if (!frag_mode) { DEBUG("FILTER: REJECT (fragment)"); @@ -5401,36 +5410,48 @@ static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, 0, NULL); return FALSE; } + frag_off = RtlUshortByteSwap( + WINDIVERT_IPV6FRAGHDR_GET_FRAGOFF(frag_header)); ext_header_len = 8; + is_ext_header = TRUE; break; + case IPPROTO_AH: - ext_header_len += 2; - ext_header_len *= 4; - break; case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_ROUTING: - ext_header_len++; - ext_header_len *= 8; + case IPPROTO_MH: + ext_header = (UINT8 *)NdisGetDataBuffer(buffer, 2, NULL, + 1, 0); + if (ext_header == NULL) + { + is_ext_header = FALSE; + break; + } + ext_header_len = (UINT)ext_header[1]; + if (protocol == IPPROTO_AH) + { + ext_header_len += 2; + ext_header_len *= 4; + } + else + { + ext_header_len++; + ext_header_len *= 8; + } + is_ext_header = TRUE; break; default: - isexthdr = FALSE; + + is_ext_header = FALSE; break; } - if (!isexthdr) + if (!is_ext_header || ip_header_len + ext_header_len > total_len) { break; } - - proto = ext_header[0]; - if (ip_header_len + ext_header_len > total_len) - { - DEBUG("FILTER: REJECT (bad IPv6 extension header)"); - NdisRetreatNetBufferDataStart(buffer, ip_header_len, - 0, NULL); - return FALSE; - } + protocol = ext_header[0]; ip_header_len += ext_header_len; NdisAdvanceNetBufferDataStart(buffer, ext_header_len, FALSE, NULL); @@ -5438,42 +5459,64 @@ static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, } header_len = ip_header_len; - switch (proto) + if (frag_off == 0) { - case IPPROTO_ICMP: - icmp_header = (PWINDIVERT_ICMPHDR)NdisGetDataBuffer(buffer, - sizeof(WINDIVERT_ICMPHDR), NULL, 1, 0); - header_len += (icmp_header == NULL? 0: sizeof(WINDIVERT_ICMPHDR)); - break; - case IPPROTO_ICMPV6: - icmpv6_header = (PWINDIVERT_ICMPV6HDR)NdisGetDataBuffer(buffer, - sizeof(WINDIVERT_ICMPV6HDR), NULL, 1, 0); - header_len += - (icmpv6_header == NULL? 0: sizeof(WINDIVERT_ICMPV6HDR)); - break; - case IPPROTO_TCP: - tcp_header = (PWINDIVERT_TCPHDR)NdisGetDataBuffer(buffer, - sizeof(WINDIVERT_TCPHDR), NULL, 1, 0); - if (tcp_header != NULL) - { - UINT tcp_header_len = tcp_header->HdrLength * sizeof(UINT32); - if (header_len + tcp_header_len > total_len) + switch (protocol) + { + case IPPROTO_ICMP: + if (ip_header == NULL) { - // Bad TCP options: - tcp_header = NULL; break; } - header_len += tcp_header_len; - } - break; - case IPPROTO_UDP: - udp_header = (PWINDIVERT_UDPHDR)NdisGetDataBuffer(buffer, - sizeof(WINDIVERT_UDPHDR), NULL, 1, 0); - header_len += (udp_header == NULL? 0: sizeof(WINDIVERT_UDPHDR)); - break; - default: - break; + icmp_header = (PWINDIVERT_ICMPHDR)NdisGetDataBuffer(buffer, + sizeof(WINDIVERT_ICMPHDR), NULL, 1, 0); + header_len += + (icmp_header == NULL? 0: sizeof(WINDIVERT_ICMPHDR)); + break; + + case IPPROTO_ICMPV6: + if (ipv6_header == NULL) + { + break; + } + icmpv6_header = (PWINDIVERT_ICMPV6HDR)NdisGetDataBuffer(buffer, + sizeof(WINDIVERT_ICMPV6HDR), NULL, 1, 0); + header_len += + (icmpv6_header == NULL? 0: sizeof(WINDIVERT_ICMPV6HDR)); + break; + + case IPPROTO_TCP: + tcp_header = (PWINDIVERT_TCPHDR)NdisGetDataBuffer(buffer, + sizeof(WINDIVERT_TCPHDR), NULL, 1, 0); + if (tcp_header != NULL) + { + if (tcp_header->HdrLength < 5) + { + tcp_header = NULL; + } + else + { + UINT tcp_header_len = + tcp_header->HdrLength * sizeof(UINT32); + tcp_header_len = + (header_len + tcp_header_len > total_len? + total_len - header_len: tcp_header_len); + header_len += tcp_header_len; + } + } + break; + + case IPPROTO_UDP: + udp_header = (PWINDIVERT_UDPHDR)NdisGetDataBuffer(buffer, + sizeof(WINDIVERT_UDPHDR), NULL, 1, 0); + header_len += + (udp_header == NULL? 0: sizeof(WINDIVERT_UDPHDR)); + break; + default: + break; + } } + status = NdisRetreatNetBufferDataStart(buffer, ip_header_len, 0, NULL); if (!NT_SUCCESS(status)) { @@ -5488,7 +5531,7 @@ static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, *icmpv6_header_ptr = icmpv6_header; *tcp_header_ptr = tcp_header; *udp_header_ptr = udp_header; - *proto_ptr = proto; + *proto_ptr = protocol; *header_len_ptr = header_len; *payload_len_ptr = total_len - header_len; diff --git a/sys/windivert.rc b/sys/windivert.rc index ea1820e..1f27518 100644 --- a/sys/windivert.rc +++ b/sys/windivert.rc @@ -38,20 +38,20 @@ #define VER_FILETYPE VFT_DRV #define VER_FILESUBTYPE VFT2_DRV_NETWORK #define VER_FILEDESCRIPTION_STR \ - "The WinDivert 2.0 driver " \ + "The WinDivert 2.2 driver " \ "[URL: https://reqrypt.org/windivert.html] " \ "[Bitcoin: 1C5vZVSbizPeZ8ydTYhUfm4LA2cNwBfcYh]" #define VER_INTERNALNAME_STR "WinDivert.sys" #define VER_ORIGINALFILENAME_STR "WinDivert.sys" -#define VER_PRODUCTVERSION 2.1 -#define VER_PRODUCTVERSION_STR "2.1" +#define VER_PRODUCTVERSION 2.2 +#define VER_PRODUCTVERSION_STR "2.2" #define VER_COMPANYNAME_STR "Basil" #define VER_LEGALCOPYRIGHT_YEARS "2011-2019" #define VER_LEGALCOPYRIGHT_STR \ "Copyright \251 " VER_COMPANYNAME_STR " " VER_LEGALCOPYRIGHT_YEARS #define VER_FILEVERSION VER_PRODUCTVERSION #define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR -#define VER_PRODUCTNAME_STR "WinDivert 2.0 driver" +#define VER_PRODUCTNAME_STR "WinDivert 2.2 driver" #include "common.ver" diff --git a/test/test.c b/test/test.c index 492c5a1..946b3c0 100644 --- a/test/test.c +++ b/test/test.c @@ -43,6 +43,7 @@ #include "windivert.h" #define MAX_PACKET 2048 +#define MIN(a, b) ((a) < (b)? (a): (b)) /* * Packet data. @@ -112,6 +113,30 @@ static const struct packet pkt_ipv6_exthdrs_udp = sizeof(ipv6_exthdrs_udp), "ipv6_exthdrs_udp" }; +static const struct packet pkt_ipv4_fragment_0 = +{ + ipv4_fragment_0, + sizeof(ipv4_fragment_0), + "ipv4_fragemnt_0" +}; +static const struct packet pkt_ipv4_fragment_1 = +{ + ipv4_fragment_1, + sizeof(ipv4_fragment_1), + "ipv4_fragment_1" +}; +static const struct packet pkt_ipv6_fragment_0 = +{ + ipv6_fragment_0, + sizeof(ipv6_fragment_0), + "ipv6_fragment_0" +}; +static const struct packet pkt_ipv6_fragment_1 = +{ + ipv6_fragment_1, + sizeof(ipv6_fragment_1), + "ipv6_fragment_1" +}; static const struct test tests[] = { {"event = PACKET", &pkt_echo_request, TRUE}, @@ -309,6 +334,9 @@ static const struct test tests[] = {"localAddr == 10.0.0.1 && remoteAddr == 8.8.8.8 && localPort == 8 && " "remotePort == 0 && protocol == 1", &pkt_echo_request, TRUE}, {"packet[0] == 0x45", &pkt_echo_request, TRUE}, + {"ip.MF or ip.FragOff != 0", &pkt_echo_request, FALSE}, + {"icmp.Body != 123 || icmp.Body == 123", &pkt_echo_request, TRUE}, + {"length == 84 && ip.Length == 84", &pkt_echo_request, TRUE}, {"tcp", &pkt_http_request, TRUE}, {"protocol == TCP", &pkt_http_request, TRUE}, {"outbound and tcp and tcp.DstPort == 80", &pkt_http_request, TRUE}, @@ -720,6 +748,7 @@ static const struct test tests[] = {"icmpv6.Body == 0x10720003", &pkt_ipv6_echo_reply, TRUE}, {"ipv6.DstAddr >= 1000", &pkt_ipv6_echo_reply, FALSE}, {"ipv6.DstAddr <= 1", &pkt_ipv6_echo_reply, TRUE}, + {"length == 104 && ipv6.Length == 64", &pkt_ipv6_echo_reply, TRUE}, {"ip and !loopback and (outbound? tcp.DstPort == 80 or" " tcp.DstPort == 443 or udp.DstPort == 53 :" " icmp.Type == 11 and icmp.Code == 0)", &pkt_ipv6_echo_reply, FALSE}, @@ -855,12 +884,47 @@ static const struct test tests[] = &pkt_ipv6_exthdrs_udp, FALSE}, {"localAddr == ::1 and remoteAddr == 1 and localPort == 4660 and " "remotePort == 43690 and protocol == 17", &pkt_ipv6_exthdrs_udp, TRUE}, + {"ip.MF or ip.FragOff != 0", &pkt_ipv4_fragment_0, TRUE}, + {"icmp", &pkt_ipv4_fragment_0, TRUE}, + {"icmp.Body != 123 || icmp.Body == 123", &pkt_ipv4_fragment_0, TRUE}, + {"length == 84 || ip.Length == 84", &pkt_ipv4_fragment_0, FALSE}, + {"ip.HdrLength == 5 and ip.TOS == 0 and ip.Length == 28 and " + "ip.Id == 0x1234 and ip.FragOff == 0 and ip.MF == 1 and ip.DF == 0 and " + "ip.TTL == 64 and ip.Protocol == 1 and ip.SrcAddr == 0xFFFF0A000001 and " + "ip.DstAddr == 0xFFFF08080808 and icmp.Type == 8 and icmp.Code == 0 and " + "icmp.Body == 0x0D560001", &pkt_ipv4_fragment_0, TRUE}, + {"ip.MF or ip.FragOff != 0", &pkt_ipv4_fragment_1, TRUE}, + {"icmp", &pkt_ipv4_fragment_1, FALSE}, + {"icmp.Body != 123 || icmp.Body == 123", &pkt_ipv4_fragment_1, FALSE}, + {"length == 84 || ip.Length == 84", &pkt_ipv4_fragment_1, FALSE}, + {"ip.HdrLength == 5 and ip.TOS == 0 and ip.Length == 76 and " + "ip.Id == 0x1234 and ip.FragOff == 1 and ip.MF == 0 and ip.DF == 0 and " + "ip.TTL == 64 and ip.Protocol == 1 and ip.SrcAddr == 0xFFFF0A000001 and " + "ip.DstAddr == 0xFFFF08080808", &pkt_ipv4_fragment_1, TRUE}, + {"icmpv6", &pkt_ipv6_fragment_0, TRUE}, + {"length == 104 || ipv6.Length == 64", &pkt_ipv6_fragment_0, FALSE}, + {"ipv6.TrafficClass == 0x00000000 and ipv6.FlowLabel == 0x0000 and " + "ipv6.Length == 32 and ipv6.NextHdr == 44 and ipv6.HopLimit == 31 and " + "ipv6.SrcAddr == 0:0:0:0:0:0:0:1 and ipv6.DstAddr == 0:0:0:0:0:0:0:1 and " + "icmpv6.Type == 129 and icmpv6.Code == 0 and icmpv6.Body == 0x10720003", + &pkt_ipv6_fragment_0, TRUE}, + {"icmpv6", &pkt_ipv6_fragment_1, FALSE}, + {"length == 104 || ipv6.Length == 64", &pkt_ipv6_fragment_1, FALSE}, + {"ipv6.TrafficClass == 0x00000000 and ipv6.FlowLabel == 0x0000 and " + "ipv6.Length == 48 and ipv6.NextHdr == 44 and ipv6.HopLimit == 31 and " + "ipv6.SrcAddr == 0:0:0:0:0:0:0:1 and ipv6.DstAddr == 0:0:0:0:0:0:0:1", + &pkt_ipv6_fragment_1, TRUE}, }; +/* + * Test range. + */ +static size_t lo = 0, hi = UINT_MAX; + /* * Main. */ -int main(void) +int main(int argc, char **argv) { HANDLE upper_handle, lower_handle; HANDLE console, monitor; @@ -869,6 +933,25 @@ int main(void) LARGE_INTEGER freq; UINT64 diff; size_t i; + size_t num_tests = sizeof(tests) / sizeof(struct test), passed_tests; + + switch (argc) + { + case 1: + break; + case 3: + lo = atoi(argv[1]); + hi = atoi(argv[2]); + if (hi >= lo) + { + break; + } + // Fallthrough + default: + fprintf(stderr, "usage: %s [low high]\n", argv[0]); + exit(EXIT_FAILURE); + } + hi = MIN(num_tests, hi); // Open handles to: // (1) stop normal traffic from interacting with the tests; and @@ -902,8 +985,8 @@ int main(void) Sleep(150); // Run tests: - size_t num_tests = sizeof(tests) / sizeof(struct test), passed_tests = 0; - for (i = 0; i < num_tests; i++) + passed_tests = 0; + for (i = lo; i < num_tests && i <= hi; i++) { const char *filter = tests[i].filter; const char *packet = tests[i].packet->packet; @@ -961,10 +1044,10 @@ int main(void) } printf("\npassed = %.2f%%\n", - ((double)passed_tests / (double)num_tests) * 100.0); + ((double)passed_tests / (double)(hi - lo)) * 100.0); first = TRUE; - for (i = 0; i < num_tests; i++) + for (i = lo; i < num_tests && i <= hi; i++) { const char *filter = tests[i].filter; char *name = tests[i].packet->name; @@ -1274,7 +1357,7 @@ static DWORD monitor_worker(LPVOID arg) } size_t num_tests = sizeof(tests) / sizeof(struct test); - for (i = 0; i < num_tests; i++) + for (i = lo; i < num_tests && i <= hi; i++) { // (1) Read the reflected filter: WinDivertHelperCompileFilter(tests[i].filter, WINDIVERT_LAYER_NETWORK, diff --git a/test/test_data.c b/test/test_data.c index b8226b7..23c364c 100644 --- a/test/test_data.c +++ b/test/test_data.c @@ -181,3 +181,56 @@ static const unsigned char ipv6_exthdrs_udp[] = 0x72, 0x6c, 0x64, 0x21, 0x01 }; +// IPV4 FRAGMENT #0 +static const unsigned char ipv4_fragment_0[] = +{ + 0x45, 0x00, 0x00, 0x1C, 0x12, 0x34, 0x20, 0x00, + 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x3c, 0xd2, + 0x0d, 0x56, 0x00, 0x01 +}; + +// IPV4 FRAGMENT #1 +static const unsigned char ipv4_fragment_1[] = +{ + 0x45, 0x00, 0x00, 0x4C, 0x12, 0x34, 0x00, 0x01, + 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x8b, 0xa6, 0x60, 0x54, + 0x00, 0x00, 0x00, 0x00, 0xf9, 0x08, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37 +}; + +// IPV6 FRAGMENT #0 +static const unsigned char ipv6_fragment_0[] = +{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2c, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x3a, 0x00, 0x00, 0x01, 0xc7, 0xf6, 0xce, 0x53, + 0x81, 0x00, 0x6e, 0xd6, 0x10, 0x72, 0x00, 0x03, + 0xa4, 0xd5, 0x69, 0x54, 0x00, 0x00, 0x00, 0x00, + 0xab, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +// IPV6 FRAGMENT #1 +static const unsigned char ipv6_fragment_1[] = +{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x3a, 0x00, 0x00, 0x18, 0xc7, 0xf6, 0xce, 0x53, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 +};