/* * GoodbyeDPI — Passive DPI blocker and Active DPI circumvention utility. */ #include #include #include #include #include #include #include #include #include #include "windivert.h" #include "goodbyedpi.h" #include "utils/repl_str.h" #include "service.h" #include "dnsredir.h" #include "ttltrack.h" #include "blackwhitelist.h" #include "fakepackets.h" // My mingw installation does not load inet_pton definition for some reason WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr); #define GOODBYEDPI_VERSION "v0.1.7" #define die() do { sleep(20); exit(EXIT_FAILURE); } while (0) #define MAX_FILTERS 4 #define DIVERT_NO_LOCALNETSv4_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_LOCALNETSv4_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)" \ ")" #define DIVERT_NO_LOCALNETSv6_DST "(" \ "(ipv6.DstAddr > ::1) and " \ "(ipv6.DstAddr < 2001::0 or ipv6.DstAddr > 2001:1::0) and " \ "(ipv6.DstAddr < fc00::0 or ipv6.DstAddr > fe00::0) and " \ "(ipv6.DstAddr < fe80::0 or ipv6.DstAddr > fec0::0) and " \ "(ipv6.DstAddr < ff00::0 or ipv6.DstAddr > ffff::0)" \ ")" #define DIVERT_NO_LOCALNETSv6_SRC "(" \ "(ipv6.SrcAddr > ::1) and " \ "(ipv6.SrcAddr < 2001::0 or ipv6.SrcAddr > 2001:1::0) and " \ "(ipv6.SrcAddr < fc00::0 or ipv6.SrcAddr > fe00::0) and " \ "(ipv6.SrcAddr < fe80::0 or ipv6.SrcAddr > fec0::0) and " \ "(ipv6.SrcAddr < ff00::0 or ipv6.SrcAddr > ffff::0)" \ ")" /* #IPID# is a template to find&replace */ #define IPID_TEMPLATE "#IPID#" #define FILTER_STRING_TEMPLATE \ "(tcp and !impostor and !loopback and " \ "((inbound and (" \ "(" \ "(" \ "(ipv6 or (ip.Id >= 0x0 and ip.Id <= 0xF) " IPID_TEMPLATE \ ") and " \ "tcp.SrcPort == 80 and tcp.Ack" \ ") or " \ "((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)" \ ")" \ " and (" DIVERT_NO_LOCALNETSv4_SRC " or " DIVERT_NO_LOCALNETSv6_SRC "))) or " \ "(outbound and " \ "(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and " \ "(" DIVERT_NO_LOCALNETSv4_DST " or " DIVERT_NO_LOCALNETSv6_DST "))" \ "))" #define FILTER_PASSIVE_STRING_TEMPLATE "inbound and ip and tcp and " \ "!impostor and !loopback and " \ "((ip.Id <= 0xF and ip.Id >= 0x0) " IPID_TEMPLATE ") and " \ "(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and " \ DIVERT_NO_LOCALNETSv4_SRC #define SET_HTTP_FRAGMENT_SIZE_OPTION(fragment_size) do { \ if (!http_fragment_size) { \ http_fragment_size = (unsigned int)fragment_size; \ } \ else if (http_fragment_size != (unsigned int)fragment_size) { \ printf( \ "WARNING: HTTP fragment size is already set to %d, not changing.\n", \ http_fragment_size \ ); \ } \ } while (0) #define TCP_HANDLE_OUTGOING_ADJUST_TTL() do { \ if ((packet_v4 && tcp_handle_outgoing(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, \ ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \ &tcp_conn_info, 0)) \ || \ (packet_v6 && tcp_handle_outgoing(ppIpV6Hdr->SrcAddr, ppIpV6Hdr->DstAddr, \ ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \ &tcp_conn_info, 1))) \ { \ ttl_of_fake_packet = tcp_get_auto_ttl(tcp_conn_info.ttl, do_auto_ttl); \ if (do_tcp_verb) { \ printf("Connection TTL = %d, Fake TTL = %d\n", tcp_conn_info.ttl, ttl_of_fake_packet); \ } \ } \ } while (0) static int running_from_service = 0; 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 struct option long_options[] = { {"port", required_argument, 0, 'z' }, {"dns-addr", required_argument, 0, 'd' }, {"dns-port", required_argument, 0, 'g' }, {"dnsv6-addr", required_argument, 0, '!' }, {"dnsv6-port", required_argument, 0, '@' }, {"dns-verb", no_argument, 0, 'v' }, {"blacklist", required_argument, 0, 'b' }, {"ip-id", required_argument, 0, 'i' }, {"set-ttl", required_argument, 0, '$' }, {"auto-ttl", optional_argument, 0, '+' }, {"wrong-chksum",no_argument, 0, '%' }, {"wrong-seq", no_argument, 0, ')' }, {"native-frag", no_argument, 0, '*' }, {"reverse-frag",no_argument, 0, '(' }, {0, 0, 0, 0 } }; static char *filter_string = NULL; static char *filter_passive_string = NULL; static void add_filter_str(int proto, int port) { const char *udp = " or (udp and !impostor and !loopback and " \ "(udp.SrcPort == %d or udp.DstPort == %d))"; const char *tcp = " or (tcp and !impostor and !loopback and " \ "(tcp.SrcPort == %d or tcp.DstPort == %d))"; char *current_filter = filter_string; size_t new_filter_size = strlen(current_filter) + (proto == IPPROTO_UDP ? strlen(udp) : strlen(tcp)) + 16; char *new_filter = malloc(new_filter_size); strcpy(new_filter, current_filter); if (proto == IPPROTO_UDP) sprintf(&(new_filter[strlen(new_filter)]), udp, port, port); else sprintf(&(new_filter[strlen(new_filter)]), tcp, port, port); filter_string = new_filter; free(current_filter); } static void add_ip_id_str(int id) { char *newstr; const char *ipid = " or ip.Id == %d"; char *addfilter = malloc(strlen(ipid) + 16); sprintf(addfilter, ipid, id); newstr = repl_str(filter_string, IPID_TEMPLATE, addfilter); free(filter_string); filter_string = newstr; newstr = repl_str(filter_passive_string, IPID_TEMPLATE, addfilter); free(filter_passive_string); filter_passive_string = newstr; } static void finalize_filter_strings() { char *newstr; newstr = repl_str(filter_string, IPID_TEMPLATE, ""); free(filter_string); filter_string = newstr; newstr = repl_str(filter_passive_string, IPID_TEMPLATE, ""); free(filter_passive_string); filter_passive_string = newstr; } static char* dumb_memmem(const char* haystack, unsigned int hlen, const char* needle, size_t nlen) { // naive implementation if (nlen > hlen) return NULL; size_t i; for (i=0; i limitValue) { puts(msg); exit(EXIT_FAILURE); } return (unsigned short int)res; } BYTE atoub(const char *str, const char *msg) { long unsigned int res = strtoul(str, NULL, 10u); enum { limitValue=0xFFu }; if(res > limitValue) { puts(msg); exit(EXIT_FAILURE); } return (BYTE)res; } static HANDLE init(char *filter, UINT64 flags) { LPTSTR errormessage = NULL; DWORD errorcode = 0; filter = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags); if (filter != INVALID_HANDLE_VALUE) return filter; errorcode = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorcode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPTSTR)&errormessage, 0, NULL); printf("Error opening filter: %s", errormessage); LocalFree(errormessage); if (errorcode == 577) printf("Windows Server 2016 systems must have secure boot disabled to be " "able to load WinDivert driver.\n" "Windows 7 systems must be up-to-date or at least have KB3033929 installed.\n" "https://www.microsoft.com/en-us/download/details.aspx?id=46078\n\n" "WARNING! If you see this error on Windows 7, it means your system is horribly " "outdated and SHOULD NOT BE USED TO ACCESS THE INTERNET!\n" "Most probably, you don't have security patches installed and anyone in you LAN or " "public Wi-Fi network can get full access to your computer (MS17-010 and others).\n" "You should install updates IMMEDIATELY.\n"); return NULL; } static int deinit(HANDLE handle) { if (handle) { WinDivertClose(handle); return TRUE; } return FALSE; } void deinit_all() { for (int i = 0; i < filter_num; i++) { deinit(filters[i]); } } static void sigint_handler(int sig __attribute__((unused))) { deinit_all(); exit(EXIT_SUCCESS); } static void mix_case(char *pktdata, unsigned int pktlen) { unsigned int i; if (pktlen <= 0) return; for (i = 0; i < pktlen; i++) { if (i % 2) { pktdata[i] = (char) toupper(pktdata[i]); } } } static int is_passivedpi_redirect(const char *pktdata, unsigned int pktlen) { /* First check if this is HTTP 302 redirect */ if (memcmp(pktdata, http11_redirect_302, sizeof(http11_redirect_302)-1) == 0 || memcmp(pktdata, http10_redirect_302, sizeof(http10_redirect_302)-1) == 0) { /* Then check if this is a redirect to new http site with Connection: close */ if (dumb_memmem(pktdata, pktlen, location_http, sizeof(location_http)-1) && dumb_memmem(pktdata, pktlen, connection_close, sizeof(connection_close)-1)) { return TRUE; } } return FALSE; } static int find_header_and_get_info(const char *pktdata, unsigned int pktlen, const char *hdrname, char **hdrnameaddr, char **hdrvalueaddr, unsigned int *hdrvaluelen) { char *data_addr_rn; char *hdr_begin; *hdrvaluelen = 0u; *hdrnameaddr = NULL; *hdrvalueaddr = NULL; /* Search for the header */ hdr_begin = dumb_memmem(pktdata, pktlen, hdrname, strlen(hdrname)); if (!hdr_begin) return FALSE; if ((PVOID)pktdata > (PVOID)hdr_begin) return FALSE; /* Set header address */ *hdrnameaddr = hdr_begin; *hdrvalueaddr = (PVOID)hdr_begin + strlen(hdrname); /* Search for header end (\r\n) */ data_addr_rn = dumb_memmem(*hdrvalueaddr, pktlen - ((PVOID)*hdrvalueaddr - (PVOID)pktdata), "\r\n", 2); if (data_addr_rn) { *hdrvaluelen = (PVOID)data_addr_rn - (PVOID)*hdrvalueaddr; if (*hdrvaluelen > 0u && *hdrvaluelen <= 512u) return TRUE; } return FALSE; } static inline void change_window_size(const PWINDIVERT_TCPHDR ppTcpHdr, unsigned int size) { if (size >= 1 && size <= 0xFFFFu) { ppTcpHdr->Window = htons((u_short)size); } } /* HTTP method end without trailing space */ static PVOID find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) { unsigned int i; for (i = 0; i<(sizeof(http_methods) / sizeof(*http_methods)); i++) { if (memcmp(pkt, http_methods[i], strlen(http_methods[i])) == 0) { if (is_fragmented) *is_fragmented = 0; return (char*)pkt + strlen(http_methods[i]) - 1; } /* Try to find HTTP method in a second part of fragmented packet */ if ((http_frag == 1 || http_frag == 2) && memcmp(pkt, http_methods[i] + http_frag, strlen(http_methods[i]) - http_frag) == 0 ) { if (is_fragmented) *is_fragmented = 1; return (char*)pkt + strlen(http_methods[i]) - http_frag - 1; } } return NULL; } /** Fragment and send the packet. * * This function cuts off the end of the packet (step=0) or * the beginning of the packet (step=1) with fragment_size bytes. */ static PVOID send_native_fragment(HANDLE w_filter, WINDIVERT_ADDRESS addr, char *packet, UINT packetLen, PVOID packet_data, UINT packet_dataLen, int packet_v4, int packet_v6, PWINDIVERT_IPHDR ppIpHdr, PWINDIVERT_IPV6HDR ppIpV6Hdr, PWINDIVERT_TCPHDR ppTcpHdr, int fragment_size, int step) { char packet_bak[MAX_PACKET_SIZE]; memcpy(&packet_bak, packet, packetLen); UINT orig_packetLen = packetLen; if (step == 0) { if (packet_v4) ppIpHdr->Length = htons( ntohs(ppIpHdr->Length) - packet_dataLen + fragment_size ); else if (packet_v6) ppIpV6Hdr->Length = htons( ntohs(ppIpV6Hdr->Length) - packet_dataLen + fragment_size ); //printf("step0 (%d:%d), pp:%d, was:%d, now:%d\n", // packet_v4, packet_v6, ntohs(ppIpHdr->Length), // packetLen, packetLen - packet_dataLen + fragment_size); packetLen = packetLen - packet_dataLen + fragment_size; } else if (step == 1) { if (packet_v4) ppIpHdr->Length = htons( ntohs(ppIpHdr->Length) - fragment_size ); else if (packet_v6) ppIpV6Hdr->Length = htons( ntohs(ppIpV6Hdr->Length) - fragment_size ); //printf("step1 (%d:%d), pp:%d, was:%d, now:%d\n", packet_v4, packet_v6, ntohs(ppIpHdr->Length), // packetLen, packetLen - fragment_size); memmove(packet_data, packet_data + fragment_size, packet_dataLen - fragment_size); packetLen -= fragment_size; ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) + fragment_size); } addr.PseudoIPChecksum = 0; addr.PseudoTCPChecksum = 0; WinDivertHelperCalcChecksums( packet, packetLen, &addr, 0 ); WinDivertSend( w_filter, packet, packetLen, &addr, NULL ); memcpy(packet, &packet_bak, orig_packetLen); //printf("Sent native fragment of %d size (step%d)\n", packetLen, step); } int main(int argc, char *argv[]) { static enum packet_type_e { unknown, ipv4_tcp, ipv4_tcp_data, ipv4_udp_data, ipv6_tcp, ipv6_tcp_data, ipv6_udp_data } packet_type; int i, should_reinject, should_recalc_checksum = 0; int opt; int packet_v4, packet_v6; HANDLE w_filter = NULL; WINDIVERT_ADDRESS addr; char packet[MAX_PACKET_SIZE]; PVOID packet_data; UINT packetLen; UINT packet_dataLen; PWINDIVERT_IPHDR ppIpHdr; PWINDIVERT_IPV6HDR ppIpV6Hdr; PWINDIVERT_TCPHDR ppTcpHdr; PWINDIVERT_UDPHDR ppUdpHdr; conntrack_info_t dns_conn_info; tcp_conntrack_info_t tcp_conn_info; int do_passivedpi = 0, do_fragment_http = 0, do_fragment_http_persistent = 0, do_fragment_http_persistent_nowait = 0, do_fragment_https = 0, do_host = 0, do_host_removespace = 0, do_additional_space = 0, do_http_allports = 0, do_host_mixedcase = 0, do_dnsv4_redirect = 0, do_dnsv6_redirect = 0, do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0, do_fake_packet = 0, do_auto_ttl = 0, do_wrong_chksum = 0, do_wrong_seq = 0, do_native_frag = 0, do_reverse_frag = 0; unsigned int http_fragment_size = 0; unsigned int https_fragment_size = 0; unsigned int current_fragment_size = 0; BYTE ttl_of_fake_packet = 0; uint32_t dnsv4_addr = 0; struct in6_addr dnsv6_addr = {0}; struct in6_addr dns_temp_addr = {0}; uint16_t dnsv4_port = htons(53); uint16_t dnsv6_port = htons(53); char *host_addr, *useragent_addr, *method_addr; unsigned int host_len, useragent_len; int http_req_fragmented; char *hdr_name_addr = NULL, *hdr_value_addr = NULL; unsigned int hdr_value_len; // Make sure to search DLLs only in safe path, not in current working dir. SetDllDirectory(""); SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT); if (!running_from_service) { running_from_service = 1; if (service_register(argc, argv)) { /* We've been called as a service. Register service * and exit this thread. main() would be called from * service.c next time. * * Note that if service_register() succeedes it does * not return until the service is stopped. * That is why we should set running_from_service * before calling service_register and unset it * afterwards. */ return 0; } running_from_service = 0; } if (filter_string == NULL) filter_string = strdup(FILTER_STRING_TEMPLATE); if (filter_passive_string == NULL) filter_passive_string = strdup(FILTER_PASSIVE_STRING_TEMPLATE); printf( "GoodbyeDPI " GOODBYEDPI_VERSION ": Passive DPI blocker and Active DPI circumvention utility\n" "https://github.com/ValdikSS/GoodbyeDPI\n\n" ); if (argc == 1) { /* enable mode -1 by default */ http_fragment_size = https_fragment_size = 2; do_passivedpi = do_host = do_host_removespace \ = do_fragment_http = do_fragment_https \ = do_fragment_http_persistent \ = do_fragment_http_persistent_nowait = 1; } while ((opt = getopt_long(argc, argv, "1234prsaf:e:mwk:n", long_options, NULL)) != -1) { switch (opt) { case '1': do_passivedpi = do_host = do_host_removespace \ = do_fragment_http = do_fragment_https \ = do_fragment_http_persistent \ = do_fragment_http_persistent_nowait = 1; break; case '2': do_passivedpi = do_host = do_host_removespace \ = do_fragment_http = do_fragment_https \ = do_fragment_http_persistent \ = do_fragment_http_persistent_nowait = 1; https_fragment_size = 40u; break; case '3': do_passivedpi = do_host = do_host_removespace \ = do_fragment_https = 1; https_fragment_size = 40u; break; case '4': do_passivedpi = do_host = do_host_removespace = 1; break; case 'p': do_passivedpi = 1; break; case 'r': do_host = 1; break; case 's': do_host_removespace = 1; break; case 'a': do_additional_space = 1; do_host_removespace = 1; break; case 'm': do_host_mixedcase = 1; break; case 'f': do_fragment_http = 1; SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, "Fragment size should be in range [0 - 0xFFFF]\n")); break; case 'k': do_fragment_http_persistent = 1; do_native_frag = 1; SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, "Fragment size should be in range [0 - 0xFFFF]\n")); break; case 'n': do_fragment_http_persistent = 1; do_fragment_http_persistent_nowait = 1; do_native_frag = 1; break; case 'e': do_fragment_https = 1; https_fragment_size = atousi(optarg, "Fragment size should be in range [0 - 65535]\n"); break; case 'w': do_http_allports = 1; break; case 'z': /* i is used as a temporary variable here */ i = atoi(optarg); if (i <= 0 || i > 65535) { printf("Port parameter error!\n"); exit(EXIT_FAILURE); } if (i != 80 && i != 443) add_filter_str(IPPROTO_TCP, i); i = 0; break; case 'i': /* i is used as a temporary variable here */ i = atousi(optarg, "IP ID parameter error!\n"); add_ip_id_str(i); i = 0; break; case 'd': if ((inet_pton(AF_INET, optarg, dns_temp_addr.s6_addr) == 1) && !do_dnsv4_redirect) { do_dnsv4_redirect = 1; if (inet_pton(AF_INET, optarg, &dnsv4_addr) != 1) { puts("DNS address parameter error!"); exit(EXIT_FAILURE); } add_filter_str(IPPROTO_UDP, 53); flush_dns_cache(); break; } puts("DNS address parameter error!"); exit(EXIT_FAILURE); break; case '!': if ((inet_pton(AF_INET6, optarg, dns_temp_addr.s6_addr) == 1) && !do_dnsv6_redirect) { do_dnsv6_redirect = 1; if (inet_pton(AF_INET6, optarg, dnsv6_addr.s6_addr) != 1) { puts("DNS address parameter error!"); exit(EXIT_FAILURE); } add_filter_str(IPPROTO_UDP, 53); flush_dns_cache(); break; } puts("DNS address parameter error!"); exit(EXIT_FAILURE); break; case 'g': if (!do_dnsv4_redirect) { puts("--dns-port should be used with --dns-addr!\n" "Make sure you use --dns-addr and pass it before " "--dns-port"); exit(EXIT_FAILURE); } dnsv4_port = atousi(optarg, "DNS port parameter error!"); if (dnsv4_port != 53) { add_filter_str(IPPROTO_UDP, dnsv4_port); } dnsv4_port = htons(dnsv4_port); break; case '@': if (!do_dnsv6_redirect) { puts("--dnsv6-port should be used with --dnsv6-addr!\n" "Make sure you use --dnsv6-addr and pass it before " "--dnsv6-port"); exit(EXIT_FAILURE); } dnsv6_port = atousi(optarg, "DNS port parameter error!"); if (dnsv6_port != 53) { add_filter_str(IPPROTO_UDP, dnsv6_port); } dnsv6_port = htons(dnsv6_port); break; case 'v': do_dns_verb = 1; do_tcp_verb = 1; break; case 'b': do_blacklist = 1; if (!blackwhitelist_load_list(optarg)) { printf("Can't load blacklist from file!\n"); exit(EXIT_FAILURE); } break; case '$': do_fake_packet = 1; ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!"); break; case '+': do_fake_packet = 1; do_auto_ttl = 2; if (optarg) { do_auto_ttl = atoub(optarg, "Set Auto TTL parameter error!"); } else if (argv[optind] && argv[optind][0] != '-') { do_auto_ttl = atoub(argv[optind], "Set Auto TTL parameter error!"); } break; case '%': do_fake_packet = 1; do_wrong_chksum = 1; break; case ')': do_fake_packet = 1; do_wrong_seq = 1; break; case '*': do_native_frag = 1; do_fragment_http_persistent = 1; do_fragment_http_persistent_nowait = 1; break; case '(': do_reverse_frag = 1; do_native_frag = 1; do_fragment_http_persistent = 1; do_fragment_http_persistent_nowait = 1; break; default: puts("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" " -m mix Host header case (test.com -> tEsT.cOm)\n" " -f [value] set HTTP fragmentation to value\n" " -k [value] enable HTTP persistent (keep-alive) fragmentation and set it to value\n" " -n do not wait for first segment ACK when -k is enabled\n" " -e [value] set HTTPS fragmentation to value\n" " -w try to find and parse HTTP traffic on all processed ports (not only on port 80)\n" " --port [value] additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n" " --ip-id [value] handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n" " --dns-addr [value] redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n" " --dns-port [value] redirect UDPv4 DNS requests to the supplied port (53 by default)\n" " --dnsv6-addr [value] redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n" " --dnsv6-port [value] redirect UDPv6 DNS requests to the supplied port (53 by default)\n" " --dns-verb print verbose DNS redirection messages\n" " --blacklist [txtfile] perform HTTP tricks only to host names and subdomains from\n" " supplied text file. This option can be supplied multiple times.\n" " --set-ttl [value] activate Fake Request Mode and send it with supplied TTL value.\n" " DANGEROUS! May break websites in unexpected ways. Use with care.\n" " --auto-ttl [decttl] activate Fake Request Mode, automatically detect TTL and decrease\n" " it from standard 64 or 128 by decttl (128/64 - TTL - 2 by default).\n" " --wrong-chksum activate Fake Request Mode and send it with incorrect TCP checksum.\n" " May not work in a VM or with some routers, but is safer than set-ttl.\n" " Could be combined with --set-ttl\n" " --wrong-seq activate Fake Request Mode and send it with TCP SEQ/ACK in the past.\n" " --native-frag fragment (split) the packets by sending them in smaller packets, without\n" " shrinking the Window Size. Works faster (does not slow down the connection)\n" " and better.\n" " --reverse-frag fragment (split) the packets just as --native-frag, but send them in the\n" " reversed order. Works with the websites which could not handle segmented\n" " HTTPS TLS ClientHello (because they receive the TCP flow \"combined\").\n" "\n" " -1 -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode, default)\n" " -2 -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible)\n" " -3 -p -r -s -e 40 (better speed for HTTP and HTTPS)\n" " -4 -p -r -s (best speed)"); exit(EXIT_FAILURE); } } if (!http_fragment_size) http_fragment_size = 2; if (!https_fragment_size) https_fragment_size = 2; printf("Block passive: %d\nFragment HTTP: %d\nFragment persistent HTTP: %d\n" "Fragment HTTPS: %d\nNative fragmentation (splitting): %d\n" "Fragments sending in reverse: %d\n" "hoSt: %d\nHost no space: %d\nAdditional space: %d\n" "Mix Host: %d\nHTTP AllPorts: %d\nHTTP Persistent Nowait: %d\n" "DNS redirect: %d\nDNSv6 redirect: %d\n" "Fake requests, TTL: %hu (auto: %hu)\nFake requests, wrong checksum: %d\n" "Fake requests, wrong SEQ/ACK: %d\n", do_passivedpi, (do_fragment_http ? http_fragment_size : 0), (do_fragment_http_persistent ? http_fragment_size : 0), (do_fragment_https ? https_fragment_size : 0), do_native_frag, do_reverse_frag, do_host, do_host_removespace, do_additional_space, do_host_mixedcase, do_http_allports, do_fragment_http_persistent_nowait, do_dnsv4_redirect, do_dnsv6_redirect, ttl_of_fake_packet, do_auto_ttl, do_wrong_chksum, do_wrong_seq ); 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"); finalize_filter_strings(); filter_num = 0; if (do_passivedpi) { /* IPv4 only filter for inbound RST packets with ID [0x0; 0xF] */ filters[filter_num] = init( filter_passive_string, WINDIVERT_FLAG_DROP); if (filters[filter_num] == NULL) die(); filter_num++; } /* * IPv4 & IPv6 filter for inbound HTTP redirection packets and * active DPI circumvention */ filters[filter_num] = init(filter_string, 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)) { debug("Got %s packet, len=%d!\n", addr.Direction ? "inbound" : "outbound", packetLen); should_reinject = 1; should_recalc_checksum = 0; ppIpHdr = (PWINDIVERT_IPHDR)NULL; ppIpV6Hdr = (PWINDIVERT_IPV6HDR)NULL; ppTcpHdr = (PWINDIVERT_TCPHDR)NULL; ppUdpHdr = (PWINDIVERT_UDPHDR)NULL; packet_v4 = packet_v6 = 0; packet_type = unknown; // Parse network packet and set it's type if ((packet_v4 = WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr, NULL, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen))) { packet_type = ipv4_tcp_data; } else if ((packet_v6 = WinDivertHelperParsePacket(packet, packetLen, NULL, &ppIpV6Hdr, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen))) { packet_type = ipv6_tcp_data; } else if ((packet_v4 = WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr, NULL, NULL, NULL, &ppTcpHdr, NULL, NULL, NULL))) { packet_type = ipv4_tcp; } else if ((packet_v6 = WinDivertHelperParsePacket(packet, packetLen, NULL, &ppIpV6Hdr, NULL, NULL, &ppTcpHdr, NULL, NULL, NULL))) { packet_type = ipv6_tcp; } else if ((packet_v4 = WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr, NULL, NULL, NULL, NULL, &ppUdpHdr, &packet_data, &packet_dataLen))) { packet_type = ipv4_udp_data; } else if ((packet_v6 = WinDivertHelperParsePacket(packet, packetLen, NULL, &ppIpV6Hdr, NULL, NULL, NULL, &ppUdpHdr, &packet_data, &packet_dataLen))) { packet_type = ipv6_udp_data; } debug("packet_type: %d, packet_v4: %d, packet_v6: %d\n", packet_type, packet_v4, packet_v6); if (packet_type == ipv4_tcp_data || packet_type == ipv6_tcp_data) { //printf("Got parsed packet, len=%d!\n", packet_dataLen); /* Got a TCP 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)) { if (packet_v4) { //printf("Dropping HTTP Redirect packet!\n"); should_reinject = 0; } else if (packet_v6 && WINDIVERT_IPV6HDR_GET_FLOWLABEL(ppIpV6Hdr) == 0x0) { /* Contrary to IPv4 where we get only packets with IP ID 0x0-0xF, * for IPv6 we got all the incoming data packets since we can't * filter them in a driver. * * Handle only IPv6 Flow Label == 0x0 for now */ //printf("Dropping HTTP Redirect packet!\n"); should_reinject = 0; } } } /* Handle OUTBOUND packet on port 443, search for something that resembles * TLS handshake, send fake request. */ else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND && ((do_fragment_https ? packet_dataLen == https_fragment_size : 0) || packet_dataLen > 16) && ppTcpHdr->DstPort != htons(80) && (do_fake_packet || do_native_frag) ) { if (packet_dataLen >=2 && memcmp(packet_data, "\x16\x03", 2) == 0) { if (do_fake_packet) { if (do_auto_ttl) { TCP_HANDLE_OUTGOING_ADJUST_TTL(); } send_fake_https_request(w_filter, &addr, packet, packetLen, packet_v6, ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); } if (do_native_frag) { // Signal for native fragmentation code handler should_recalc_checksum = 1; } } } /* Handle OUTBOUND packet on port 80, search for Host header */ else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND && packet_dataLen > 16 && (do_http_allports ? 1 : (ppTcpHdr->DstPort == htons(80))) && find_http_method_end(packet_data, (do_fragment_http ? http_fragment_size : 0u), &http_req_fragmented) && (do_host || do_host_removespace || do_host_mixedcase || do_fragment_http_persistent || do_fake_packet)) { /* Find Host header */ if (find_header_and_get_info(packet_data, packet_dataLen, http_host_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len) && hdr_value_len > 0 && hdr_value_len <= HOST_MAXLEN && (do_blacklist ? blackwhitelist_check_hostname(hdr_value_addr, hdr_value_len) : 1)) { host_addr = hdr_value_addr; host_len = hdr_value_len; if (do_native_frag) { // Signal for native fragmentation code handler should_recalc_checksum = 1; } if (do_fake_packet) { if (do_auto_ttl) { TCP_HANDLE_OUTGOING_ADJUST_TTL(); } send_fake_http_request(w_filter, &addr, packet, packetLen, packet_v6, ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); } if (do_host_mixedcase) { mix_case(host_addr, host_len); should_recalc_checksum = 1; } if (do_host) { /* Replace "Host: " with "hoSt: " */ memcpy(hdr_name_addr, http_host_replace, strlen(http_host_replace)); should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } /* If removing space between host header and its value * and adding additional space between Method and Request-URI */ if (do_additional_space && do_host_removespace) { /* End of "Host:" without trailing space */ method_addr = find_http_method_end(packet_data, (do_fragment_http ? http_fragment_size : 0), NULL); if (method_addr) { memmove(method_addr + 1, method_addr, (PVOID)host_addr - (PVOID)method_addr - 1); should_recalc_checksum = 1; } } /* If just removing space between host header and its value */ else if (do_host_removespace) { if (find_header_and_get_info(packet_data, packet_dataLen, http_useragent_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len)) { useragent_addr = hdr_value_addr; useragent_len = hdr_value_len; /* 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. */ if (useragent_addr && useragent_len > 0) { /* useragent_addr is in the beginning of User-Agent value */ if (useragent_addr > host_addr) { /* Move one byte to the LEFT from "Host:" * to the end of User-Agent */ memmove(host_addr - 1, host_addr, (PVOID)useragent_addr + useragent_len - (PVOID)host_addr); host_addr -= 1; /* Put space in the end of User-Agent header */ *(char*)((PVOID)useragent_addr + useragent_len - 1) = ' '; should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } else { /* User-Agent goes BEFORE Host header */ /* Move one byte to the RIGHT from the end of User-Agent * to the "Host:" */ memmove((PVOID)useragent_addr + useragent_len + 1, (PVOID)useragent_addr + useragent_len, (PVOID)host_addr - 1 - ((PVOID)useragent_addr + useragent_len)); /* Put space in the end of User-Agent header */ *(char*)((PVOID)useragent_addr + useragent_len) = ' '; should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } } /* if (host_len <= HOST_MAXLEN && useragent_addr) */ } /* if (find_header_and_get_info http_useragent) */ } /* else if (do_host_removespace) */ } /* if (find_header_and_get_info http_host) */ } /* Handle OUTBOUND packet with data */ /* * should_recalc_checksum mean we have detected a packet to handle and * modified it in some way. * Handle native fragmentation here, incl. sending the packet. */ if (should_reinject && should_recalc_checksum && do_native_frag) { current_fragment_size = 0; if (do_fragment_http && ppTcpHdr->DstPort == htons(80)) { current_fragment_size = http_fragment_size; } else if (do_fragment_https && ppTcpHdr->DstPort != htons(80)) { current_fragment_size = https_fragment_size; } if (current_fragment_size) { send_native_fragment(w_filter, addr, packet, packetLen, packet_data, packet_dataLen,packet_v4, packet_v6, ppIpHdr, ppIpV6Hdr, ppTcpHdr, current_fragment_size, do_reverse_frag); send_native_fragment(w_filter, addr, packet, packetLen, packet_data, packet_dataLen,packet_v4, packet_v6, ppIpHdr, ppIpV6Hdr, ppTcpHdr, current_fragment_size, !do_reverse_frag); continue; } } } /* Handle TCP packet with data */ /* Else if we got TCP packet without data */ else if (packet_type == ipv4_tcp || packet_type == ipv6_tcp) { /* If we got INBOUND SYN+ACK packet */ if (addr.Direction == WINDIVERT_DIRECTION_INBOUND && ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1) { //printf("Changing Window Size!\n"); /* * Window Size is changed even if do_fragment_http_persistent * is enabled as there could be non-HTTP data on port 80 */ if (do_fake_packet && do_auto_ttl) { if (!((packet_v4 && tcp_handle_incoming(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, ppTcpHdr->SrcPort, ppTcpHdr->DstPort, 0, ppIpHdr->TTL)) || (packet_v6 && tcp_handle_incoming(&ppIpV6Hdr->SrcAddr, &ppIpV6Hdr->DstAddr, ppTcpHdr->SrcPort, ppTcpHdr->DstPort, 1, ppIpV6Hdr->HopLimit)))) { if (do_tcp_verb) puts("[TCP WARN] Can't add TCP connection record."); } } if (!do_native_frag) { if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) { change_window_size(ppTcpHdr, http_fragment_size); should_recalc_checksum = 1; } else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) { change_window_size(ppTcpHdr, https_fragment_size); should_recalc_checksum = 1; } } } } /* Else if we got UDP packet with data */ else if ((do_dnsv4_redirect && (packet_type == ipv4_udp_data)) || (do_dnsv6_redirect && (packet_type == ipv6_udp_data))) { if (addr.Direction == WINDIVERT_DIRECTION_INBOUND) { if ((packet_v4 && dns_handle_incoming(&ppIpHdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, &dns_conn_info, 0)) || (packet_v6 && dns_handle_incoming(ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, &dns_conn_info, 1))) { /* Changing source IP and port to the values * from DNS conntrack */ if (packet_v4) ppIpHdr->SrcAddr = dns_conn_info.dstip[0]; else if (packet_v6) ipv6_copy_addr(ppIpV6Hdr->SrcAddr, dns_conn_info.dstip); ppUdpHdr->DstPort = dns_conn_info.srcport; ppUdpHdr->SrcPort = dns_conn_info.dstport; should_recalc_checksum = 1; } else { if (dns_is_dns_packet(packet_data, packet_dataLen, 0)) should_reinject = 0; if (do_dns_verb && !should_reinject) { printf("[DNS] Error handling incoming packet: srcport = %hu, dstport = %hu\n", ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort)); } } } else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND) { if ((packet_v4 && dns_handle_outgoing(&ppIpHdr->SrcAddr, ppUdpHdr->SrcPort, &ppIpHdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, 0)) || (packet_v6 && dns_handle_outgoing(ppIpV6Hdr->SrcAddr, ppUdpHdr->SrcPort, ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, 1))) { /* Changing destination IP and port to the values * from configuration */ if (packet_v4) { ppIpHdr->DstAddr = dnsv4_addr; ppUdpHdr->DstPort = dnsv4_port; } else if (packet_v6) { ipv6_copy_addr(ppIpV6Hdr->DstAddr, (uint32_t*)dnsv6_addr.s6_addr); ppUdpHdr->DstPort = dnsv6_port; } should_recalc_checksum = 1; } else { if (dns_is_dns_packet(packet_data, packet_dataLen, 1)) should_reinject = 0; if (do_dns_verb && !should_reinject) { printf("[DNS] Error handling outgoing packet: srcport = %hu, dstport = %hu\n", ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort)); } } } } if (should_reinject) { //printf("Re-injecting!\n"); if (should_recalc_checksum) { WinDivertHelperCalcChecksums(packet, packetLen, &addr, NULL); } WinDivertSend(w_filter, packet, packetLen, &addr, NULL); } } else { // error, ignore printf("Error receiving packet!\n"); break; } } }