/* * GoodbyeDPI — Passive DPI blocker and Active DPI circumvention utility. */ #include #include #include #include #include #include #include "windivert.h" #define die() do { printf("Something went wrong!\n" \ "Make sure you're running this program with administrator privileges\n"); \ sleep(10); exit(EXIT_FAILURE); } while (0) #define MAX_FILTERS 4 #define MAX_PACKET_SIZE 1516 #define IPV4_HDR_LEN 20 #define TCP_HDR_LEN 20 #define IPV4_TOTALLEN_OFFSET 2 #define TCP_WINDOWSIZE_OFFSET 14 #define DIVERT_NO_LOCALNETS_DST "(" \ "(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \ "(ip.DstAddr < 10.0.0.0 or ip.DstAddr > 10.255.255.255) and " \ "(ip.DstAddr < 192.168.0.0 or ip.DstAddr > 192.168.255.255) and " \ "(ip.DstAddr < 172.16.0.0 or ip.DstAddr > 172.31.255.255) and " \ "(ip.DstAddr < 169.254.0.0 or ip.DstAddr > 169.254.255.255)" \ ")" #define DIVERT_NO_LOCALNETS_SRC "(" \ "(ip.SrcAddr < 127.0.0.1 or ip.SrcAddr > 127.255.255.255) and " \ "(ip.SrcAddr < 10.0.0.0 or ip.SrcAddr > 10.255.255.255) and " \ "(ip.SrcAddr < 192.168.0.0 or ip.SrcAddr > 192.168.255.255) and " \ "(ip.SrcAddr < 172.16.0.0 or ip.SrcAddr > 172.31.255.255) and " \ "(ip.SrcAddr < 169.254.0.0 or ip.SrcAddr > 169.254.255.255)" \ ")" static HANDLE filters[MAX_FILTERS]; static int filter_num = 0; static const char *http10_redirect_302 = "HTTP/1.0 302 "; static const char *http11_redirect_302 = "HTTP/1.1 302 "; static const char *http_host_find = "\r\nHost: "; static const char *http_host_replace = "\r\nhoSt: "; static const char *http_useragent_find = "\r\nUser-Agent: "; static const char *location_http = "\r\nLocation: http://"; static const char *connection_close = "\r\nConnection: close"; static const char *http_methods[] = { "GET ", "HEAD ", "POST ", "PUT ", "DELETE ", "CONNECT ", "OPTIONS ", }; static char* dumb_memmem(const char* haystack, int hlen, const char* needle, int nlen) { // naive implementation if (nlen > hlen) return 0; int i; for (i=0; i 65535) { printf(fragment_size_message); exit(EXIT_FAILURE); } break; case 'e': do_fragment_https = 1; https_fragment_size = atoi(optarg); if (https_fragment_size <= 0 || https_fragment_size > 65535) { printf(fragment_size_message); exit(EXIT_FAILURE); } break; default: printf("Usage: goodbyedpi.exe [OPTION...]\n" " -p block passive DPI\n" " -r replace Host with hoSt\n" " -s remove space between host header and its value\n" " -a additional space between Method and Request-URI (enables -s, may break sites)\n" " -f [value] set HTTP fragmentation to value\n" " -e [value] set HTTPS fragmentation to value\n" "\n" " -1 -p -r -s -f 2 -e 2 (most compatible mode, default)\n" " -2 -p -r -s -f 2 -e 40 (better speed yet still compatible)\n" " -3 -p -r -s -e 40 (even better speed)\n" " -4 -p -r -s (best speed)\n"); exit(EXIT_FAILURE); } } printf("Block passive: %d, Fragment HTTP: %d, Fragment HTTPS: %d, " "hoSt: %d, Host no space: %d, Additional space: %d\n", do_passivedpi, (do_fragment_http ? http_fragment_size : 0), (do_fragment_https ? https_fragment_size : 0), do_host, do_host_removespace, do_additional_space); if (do_fragment_http && http_fragment_size > 2) { printf("WARNING: HTTP fragmentation values > 2 are not fully compatible " "with other options. Please use values <= 2 or disable HTTP fragmentation " "completely.\n"); } printf("\nOpening filter\n"); filter_num = 0; if (do_passivedpi) { /* IPv4 filter for inbound RST packets with ID = 0 or 1 */ filters[filter_num] = init( "inbound and ip and tcp and " "(ip.Id == 0x0001 or ip.Id == 0x0000) and " "(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and " DIVERT_NO_LOCALNETS_SRC, WINDIVERT_FLAG_DROP); filter_num++; } /* * IPv4 filter for inbound HTTP redirection packets and * active DPI circumvention */ filters[filter_num] = init("ip and tcp and " "(inbound and ((" "((ip.Id == 0x0001 or ip.Id == 0x0000) and tcp.SrcPort == 80 and tcp.Ack) or " "((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)" ") and " DIVERT_NO_LOCALNETS_SRC ") or " "(outbound and " "(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and " DIVERT_NO_LOCALNETS_DST ")" ")", 0); w_filter = filters[filter_num]; filter_num++; for (i = 0; i < filter_num; i++) { if (filters[i] == NULL) die(); } printf("Filter activated!\n"); signal(SIGINT, sigint_handler); while (1) { if (WinDivertRecv(w_filter, packet, sizeof(packet), &addr, &packetLen)) { //printf("Got %s packet, len=%d!\n", addr.Direction ? "inbound" : "outbound", // packetLen); should_reinject = 1; should_recalc_checksum = 0; if (WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr, NULL, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen)) { //printf("Got parsed packet, len=%d!\n", packet_dataLen); /* Got a packet WITH DATA */ /* Handle INBOUND packet with data and find HTTP REDIRECT in there */ if (addr.Direction == WINDIVERT_DIRECTION_INBOUND && packet_dataLen > 16) { /* If INBOUND packet with DATA (tcp.Ack) */ /* Drop packets from filter with HTTP 30x Redirect */ if (do_passivedpi && is_passivedpi_redirect(packet_data, packet_dataLen)) { //printf("Dropping HTTP Redirect packet!\n"); should_reinject = 0; } } /* Handle OUTBOUND packet, search for Host header */ else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND && packet_dataLen > 16 && ppTcpHdr->DstPort == htons(80) && find_http_method_end(packet_data, (do_fragment_http ? http_fragment_size : 0)) && (do_host || do_host_removespace)) { data_addr = find_host_header(packet_data, packet_dataLen); if (data_addr) { if (do_host) { /* Replace "Host: " with "hoSt: " */ memcpy(data_addr, http_host_replace, strlen(http_host_replace)); should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } if (do_additional_space && do_host_removespace) { /* End of "Host:" without trailing space */ host_addr = data_addr + strlen(http_host_find) - 1; method_addr = find_http_method_end(packet_data, (do_fragment_http ? http_fragment_size : 0)); if (method_addr) { memmove(method_addr + 1, method_addr, (PVOID)host_addr - (PVOID)method_addr); should_recalc_checksum = 1; } } else if (do_host_removespace) { host_addr = data_addr + strlen(http_host_find); data_addr_rn = dumb_memmem(host_addr, packet_dataLen - ((PVOID)host_addr - packet_data), "\r\n", 2); if (data_addr_rn) { /* We move Host header value by one byte to the left and then * "insert" stolen space to the end of User-Agent value because * some web servers are not tolerant to additional space in the * end of Host header. * * Nothing is done if User-Agent header is missing. */ host_len = data_addr_rn - host_addr; useragent_addr = find_useragent_header(packet_data, packet_dataLen); if (host_len <= 253 && useragent_addr) { useragent_addr += strlen(http_useragent_find); /* useragent_addr is in the beginning of User-Agent value */ data_len = packet_dataLen - ((PVOID)useragent_addr - packet_data); data_addr_rn = dumb_memmem(useragent_addr, data_len, "\r\n", 2); /* data_addr_rn is in the end of User-Agent value */ if (data_addr_rn) { if (useragent_addr > host_addr) { /* User-Agent goes AFTER Host header */ data_len = (PVOID)data_addr_rn - (PVOID)host_addr; /* Move one byte to the LEFT from "Host:" * to the end of User-Agent */ memmove(host_addr - 1, host_addr, data_len); /* Put space in the end of User-Agent header */ *(char*)(data_addr_rn - 1) = ' '; should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } else { /* User-Agent goes BEFORE Host header */ data_len = (PVOID)host_addr - (PVOID)data_addr_rn - 1; /* Move one byte to the RIGHT from the end of User-Agent * to the "Host:" */ memmove(data_addr_rn + 1, data_addr_rn, data_len); /* Put space in the end of User-Agent header */ *(char*)(data_addr_rn) = ' '; should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } } /* if (dara_addr_rn) */ } /* if (host_len <= 253 && useragent_addr) */ } /* if (data_addr_rn) */ } /* else if (do_host_removespace) */ } /* if (data_addr) */ } /* Handle OUTBOUND packet with data */ } /* Handle packet with data */ /* Else if we got TCP packet without data */ else if (WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr, NULL, NULL, NULL, &ppTcpHdr, NULL, NULL, NULL)) { /* If we got SYN+ACK packet */ if (addr.Direction == WINDIVERT_DIRECTION_INBOUND && ppTcpHdr->Syn == 1) { //printf("Changing Window Size!\n"); if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) { change_window_size(packet, http_fragment_size); should_recalc_checksum = 1; } else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) { change_window_size(packet, https_fragment_size); should_recalc_checksum = 1; } } } if (should_reinject) { //printf("Re-injecting!\n"); if (should_recalc_checksum) { WinDivertHelperCalcChecksums(packet, packetLen, 0); } else { WinDivertHelperCalcChecksums(packet, packetLen, WINDIVERT_HELPER_NO_REPLACE); } WinDivertSend(w_filter, packet, packetLen, &addr, NULL); } } else { // error, ignore printf("Error receiving packet!\n"); break; } } }