From f2de8fce8d309139fa362ae7cdb3a9afaa8e55a8 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 5 Oct 2019 02:38:29 +0300 Subject: [PATCH] Fake Request Mode --- README.md | 7 +- src/fakepackets.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++ src/fakepackets.h | 16 +++++ src/goodbyedpi.c | 58 ++++++++++++++-- src/goodbyedpi.h | 1 + 5 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 src/fakepackets.c create mode 100644 src/fakepackets.h diff --git a/README.md b/README.md index 83b795c..23c9213 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ Usage: goodbyedpi.exe [OPTION...] --dns-verb print verbose DNS redirection messages --blacklist [txtfile] perform HTTP tricks only to host names and subdomains from supplied text file. This option can be supplied multiple times. + --set-ttl [value] activate Fake Request Mode and send it with supplied TTL value. + DANGEROUS! May break websites in unexpected ways. Use with care. + --wrong-chksum activate Fake Request Mode and send it with incorrect TCP checksum. + May not work in a VM or with some routers, but is safer than set-ttl. -1 -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode, default) -2 -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible) @@ -56,7 +60,7 @@ Most Passive DPI send HTTP 302 Redirect if you try to access blocked website ove ### Active DPI -Active DPI is more tricky to fool. Currently the software uses 6 methods to circumvent Active DPI: +Active DPI is more tricky to fool. Currently the software uses 7 methods to circumvent Active DPI: * TCP-level fragmentation for first data packet * TCP-level fragmentation for persistent (keep-alive) HTTP sessions @@ -64,6 +68,7 @@ Active DPI is more tricky to fool. Currently the software uses 6 methods to circ * Removing space between header name and value in `Host` header * Adding additional space between HTTP Method (GET, POST etc) and URI * Mixing case of Host header value +* Sending fake HTTP/HTTPS packets with low Time-To-Live value or incorrect checksum to fool DPI and prevent delivering them to the destination These methods should not break any website as they're fully compatible with TCP and HTTP standards, yet it's sufficient to prevent DPI data classification and to circumvent censorship. Additional space may break some websites, although it's acceptable by HTTP/1.1 specification (see 19.3 Tolerant Applications). diff --git a/src/fakepackets.c b/src/fakepackets.c new file mode 100644 index 0000000..5c9dd74 --- /dev/null +++ b/src/fakepackets.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include "windivert.h" +#include "goodbyedpi.h" + +static const char fake_http_request[] = "GET / HTTP/1.1\r\nHost: www.w3.org\r\n" + "User-Agent: curl/7.65.3\r\nAccept: */*\r\n" + "Accept-Encoding: deflate, gzip, br\r\n\r\n"; +static const unsigned char fake_https_request[] = { + 0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x9a, 0x8f, 0xa7, 0x6a, 0x5d, + 0x57, 0xf3, 0x62, 0x19, 0xbe, 0x46, 0x82, 0x45, 0xe2, 0x59, 0x5c, 0xb4, 0x48, 0x31, 0x12, 0x15, + 0x14, 0x79, 0x2c, 0xaa, 0xcd, 0xea, 0xda, 0xf0, 0xe1, 0xfd, 0xbb, 0x20, 0xf4, 0x83, 0x2a, 0x94, + 0xf1, 0x48, 0x3b, 0x9d, 0xb6, 0x74, 0xba, 0x3c, 0x81, 0x63, 0xbc, 0x18, 0xcc, 0x14, 0x45, 0x57, + 0x6c, 0x80, 0xf9, 0x25, 0xcf, 0x9c, 0x86, 0x60, 0x50, 0x31, 0x2e, 0xe9, 0x00, 0x22, 0x13, 0x01, + 0x13, 0x03, 0x13, 0x02, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30, + 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35, + 0x01, 0x00, 0x01, 0x91, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x77, 0x77, 0x77, + 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, + 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, + 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, + 0x20, 0xb0, 0xe4, 0xda, 0x34, 0xb4, 0x29, 0x8d, 0xd3, 0x5c, 0x70, 0xd3, 0xbe, 0xe8, 0xa7, 0x2a, + 0x6b, 0xe4, 0x11, 0x19, 0x8b, 0x18, 0x9d, 0x83, 0x9a, 0x49, 0x7c, 0x83, 0x7f, 0xa9, 0x03, 0x8c, + 0x3c, 0x00, 0x17, 0x00, 0x41, 0x04, 0x4c, 0x04, 0xa4, 0x71, 0x4c, 0x49, 0x75, 0x55, 0xd1, 0x18, + 0x1e, 0x22, 0x62, 0x19, 0x53, 0x00, 0xde, 0x74, 0x2f, 0xb3, 0xde, 0x13, 0x54, 0xe6, 0x78, 0x07, + 0x94, 0x55, 0x0e, 0xb2, 0x6c, 0xb0, 0x03, 0xee, 0x79, 0xa9, 0x96, 0x1e, 0x0e, 0x98, 0x17, 0x78, + 0x24, 0x44, 0x0c, 0x88, 0x80, 0x06, 0x8b, 0xd4, 0x80, 0xbf, 0x67, 0x7c, 0x37, 0x6a, 0x5b, 0x46, + 0x4c, 0xa7, 0x98, 0x6f, 0xb9, 0x22, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, + 0x02, 0x03, 0x01, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, + 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, + 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00, 0x15, 0x00, 0x96, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int send_fake_data(const HANDLE w_filter, + const PWINDIVERT_ADDRESS addr, + const char *pkt, + const UINT packetLen, + const BOOL is_ipv6, + const BOOL is_https, + const BYTE set_ttl, + const BYTE set_checksum + ) { + char packet_fake[MAX_PACKET_SIZE]; + WINDIVERT_ADDRESS addr_new; + PVOID packet_data; + UINT packet_dataLen; + UINT packetLen_new; + PWINDIVERT_IPHDR ppIpHdr; + PWINDIVERT_IPV6HDR ppIpV6Hdr; + PWINDIVERT_TCPHDR ppTcpHdr; + char *fake_request_data = is_https ? fake_https_request : fake_http_request; + UINT fake_request_size = is_https ? sizeof(fake_https_request) : sizeof(fake_http_request) - 1; + + memcpy(&addr_new, addr, sizeof(WINDIVERT_ADDRESS)); + memcpy(packet_fake, pkt, packetLen); + + if (!is_ipv6) { + // IPv4 TCP Data packet + if (!WinDivertHelperParsePacket(packet_fake, packetLen, &ppIpHdr, + NULL, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen)) + return 1; + } + else { + // IPv6 TCP Data packet + if (!WinDivertHelperParsePacket(packet_fake, packetLen, NULL, + &ppIpV6Hdr, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen)) + return 1; + } + + if (packetLen + fake_request_size + 1 > MAX_PACKET_SIZE) + return 2; + + memcpy(packet_data, fake_request_data, fake_request_size); + packetLen_new = packetLen - packet_dataLen + fake_request_size; + + if (!is_ipv6) { + ppIpHdr->Length = htons( + ntohs(ppIpHdr->Length) - + packet_dataLen + fake_request_size + ); + + if (set_ttl) + ppIpHdr->TTL = set_ttl; + } + else { + ppIpV6Hdr->Length = htons( + ntohs(ppIpV6Hdr->Length) - + packet_dataLen + fake_request_size + ); + + if (set_ttl) + ppIpV6Hdr->HopLimit = set_ttl; + } + + // Recalculate the checksum + addr_new.PseudoTCPChecksum = 0; + WinDivertHelperCalcChecksums(packet_fake, packetLen_new, &addr_new, NULL); + + if (set_checksum) { + // ...and damage it + ppTcpHdr->Checksum = htons(ntohs(ppTcpHdr->Checksum) - 1); + } + //printf("Pseudo checksum: %d\n", addr_new.PseudoTCPChecksum); + + WinDivertSend( + w_filter, packet_fake, + packetLen_new, + &addr_new, NULL + ); + debug("Fake packet: OK"); + + return 0; +} + +int send_fake_http_request(const HANDLE w_filter, + const PWINDIVERT_ADDRESS addr, + const char *pkt, + const UINT packetLen, + const BOOL is_ipv6, + const BYTE set_ttl, + const BYTE set_checksum + ) { + return send_fake_data(w_filter, + addr, + pkt, + packetLen, + is_ipv6, + FALSE, + set_ttl, + set_checksum + ); +} + +int send_fake_https_request(const HANDLE w_filter, + const PWINDIVERT_ADDRESS addr, + const char *pkt, + const UINT packetLen, + const BOOL is_ipv6, + const BYTE set_ttl, + const BYTE set_checksum + ) { + return send_fake_data(w_filter, + addr, + pkt, + packetLen, + is_ipv6, + TRUE, + set_ttl, + set_checksum + ); +} diff --git a/src/fakepackets.h b/src/fakepackets.h new file mode 100644 index 0000000..de20c7e --- /dev/null +++ b/src/fakepackets.h @@ -0,0 +1,16 @@ +int send_fake_http_request(const HANDLE w_filter, + const PWINDIVERT_ADDRESS addr, + const char *pkt, + const UINT packetLen, + const BOOL is_ipv6, + const BYTE set_ttl, + const BYTE set_checksum + ); +int send_fake_https_request(const HANDLE w_filter, + const PWINDIVERT_ADDRESS addr, + const char *pkt, + const UINT packetLen, + const BOOL is_ipv6, + const BYTE set_ttl, + const BYTE set_checksum + ); diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c index bacf009..bba5fda 100644 --- a/src/goodbyedpi.c +++ b/src/goodbyedpi.c @@ -17,6 +17,7 @@ #include "service.h" #include "dnsredir.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); @@ -26,7 +27,6 @@ WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pA #define die() do { sleep(20); exit(EXIT_FAILURE); } while (0) #define MAX_FILTERS 4 -#define MAX_PACKET_SIZE 9016 #define DIVERT_NO_LOCALNETSv4_DST "(" \ "(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \ @@ -123,6 +123,8 @@ static struct option long_options[] = { {"dns-verb", no_argument, 0, 'v' }, {"blacklist", required_argument, 0, 'b' }, {"ip-id", required_argument, 0, 'i' }, + {"set-ttl", required_argument, 0, '$' }, + {"wrong-chksum",no_argument, 0, '%' }, {0, 0, 0, 0 } }; @@ -205,6 +207,19 @@ unsigned short int atousi(const char *str, const char *msg) { 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; @@ -367,9 +382,11 @@ int main(int argc, char *argv[]) { do_http_allports = 0, do_host_mixedcase = 0, do_dnsv4_redirect = 0, do_dnsv6_redirect = 0, - do_dns_verb = 0, do_blacklist = 0; + do_dns_verb = 0, do_blacklist = 0, + do_wrong_chksum = 0; unsigned int http_fragment_size = 0; unsigned int https_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}; @@ -566,6 +583,12 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } break; + case '$': + ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!"); + break; + case '%': + do_wrong_chksum = 1; + break; default: puts("Usage: goodbyedpi.exe [OPTION...]\n" " -p block passive DPI\n" @@ -587,6 +610,12 @@ int main(int argc, char *argv[]) { " --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" + " Could be combined with --wrong-chksum.\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." "\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" @@ -604,13 +633,14 @@ int main(int argc, char *argv[]) { printf("Block passive: %d\nFragment HTTP: %d\nFragment persistent HTTP: %d\n" "Fragment HTTPS: %d\nhoSt: %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", + "DNS redirect: %d\nDNSv6 redirect: %d\n" + "Fake requests, TTL: %hu\nFake requests, wrong checksum: %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_host, do_host_removespace, do_additional_space, do_host_mixedcase, do_http_allports, do_fragment_http_persistent_nowait, do_dnsv4_redirect, - do_dnsv6_redirect + do_dnsv6_redirect, ttl_of_fake_packet, do_wrong_chksum ); if (do_fragment_http && http_fragment_size > 2) { @@ -724,6 +754,21 @@ int main(int argc, char *argv[]) { } } } + /* 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) && + (ttl_of_fake_packet || do_wrong_chksum) + ) + { + if (packet_dataLen >=2 && memcmp(packet_data, "\x16\x03", 2) == 0) { + send_fake_https_request(w_filter, &addr, packet, packetLen, packet_v6, + ttl_of_fake_packet, do_wrong_chksum); + } + } /* Handle OUTBOUND packet on port 80, search for Host header */ else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND && packet_dataLen > 16 && @@ -744,6 +789,11 @@ int main(int argc, char *argv[]) { host_addr = hdr_value_addr; host_len = hdr_value_len; + if (ttl_of_fake_packet || do_wrong_chksum) + send_fake_http_request(w_filter, &addr, packet, packetLen, packet_v6, + ttl_of_fake_packet, do_wrong_chksum); + + /* * Handle new HTTP request in new * connection (when Window Size modification disabled) diff --git a/src/goodbyedpi.h b/src/goodbyedpi.h index 626e85e..5dd3d47 100644 --- a/src/goodbyedpi.h +++ b/src/goodbyedpi.h @@ -1,4 +1,5 @@ #define HOST_MAXLEN 253 +#define MAX_PACKET_SIZE 9016 #ifndef DEBUG #define debug(...) do {} while (0)