/* * 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 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 *http_methods[] = { "GET ", "HEAD ", "POST ", "PUT ", "DELETE ", "CONNECT ", "OPTIONS ", "TRACE ", "PATCH ", "PROPFIND ", "PROPPATCH ", "MKCOL ", "COPY ", "MOVE ", "LOCK ", "UNLOCK ", }; 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); printf("\nOpening filter\n"); filter_num = 0; if (do_passivedpi) { /* Filter for inbound RST packets with ID = 0 or 1 */ filters[filter_num] = init("inbound and (ip.Id == 0x0001 or ip.Id == 0x0000) and " "(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst", WINDIVERT_FLAG_DROP); filter_num++; } /* * Filter for inbound HTTP redirection packets and * active DPI circumvention */ filters[filter_num] = init("(inbound and (ip.Id == 0x0001 or ip.Id == 0x0000) and tcp.SrcPort == 80 and tcp.Ack) " "or (inbound and (tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn) " "or (outbound and (tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack)", 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; 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_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)); //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); if (method_addr) { memmove(method_addr + 1, method_addr, (PVOID)host_addr - (PVOID)method_addr); } } 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 > host_addr) { /* Performing action only if User-Agent header goes after Host */ 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) { data_len = (PVOID)data_addr_rn - (PVOID)host_addr; /* Move one byte to the left from "Host:" * to the end of User-Agen */ memmove(host_addr - 1, host_addr, data_len); /* Put space in the end of User-Agent header */ *(char*)(data_addr_rn - 1) = ' '; //printf("Replaced Host header!\n"); } } } } WinDivertHelperCalcChecksums(packet, packetLen, 0); } } } /* 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); WinDivertHelperCalcChecksums(packet, packetLen, 0); } else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) { change_window_size(packet, https_fragment_size); WinDivertHelperCalcChecksums(packet, packetLen, 0); } } } if (should_reinject) { //printf("Re-injecting!\n"); WinDivertSend(w_filter, packet, packetLen, &addr, NULL); } } else { // error, ignore printf("Error receiving packet!\n"); break; } } }