From 5b79d8e8ba569193dae758bd01595d725552cab1 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 25 Dec 2021 01:50:34 +0300 Subject: [PATCH 1/6] Update Makefile, make fortify work on modern mingw compilers --- src/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index 655af95..edc18b9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,21 +9,22 @@ MINGWLIB = /usr/x86_64-w64-mingw32/lib/ TARGET = goodbyedpi.exe # Linking SSP does not work for some reason, the executable doesn't start. #LIBS = -L$(WINDIVERTLIBS) -Wl,-Bstatic -lssp -Wl,-Bdynamic -lWinDivert -lws2_32 -LIBS = -L$(WINDIVERTLIBS) -lWinDivert -lws2_32 +LIBS = -L$(WINDIVERTLIBS) -lWinDivert -lws2_32 -l:libssp.a CC = $(CPREFIX)gcc CCWINDRES = $(CPREFIX)windres CFLAGS = -std=c99 -pie -fPIE -pipe -I$(WINDIVERTHEADERS) -L$(WINDIVERTLIBS) \ - -O2 -D_FORTIFY_SOURCE=2 \ + -O2 -D_FORTIFY_SOURCE=2 -fstack-protector \ -Wall -Wextra -Wpedantic -Wformat=2 -Wshadow -Wstrict-aliasing=1 -Werror=format-security \ -Wfloat-equal -Wcast-align -Wsign-conversion \ #-fstack-protector-strong -LDFLAGS = -Wl,-O1,-pie,--dynamicbase,--nxcompat,--sort-common,--as-needed \ +LDFLAGS = -fstack-protector -Wl,-O1,-pie,--dynamicbase,--nxcompat,--sort-common,--as-needed \ -Wl,--image-base,0x140000000 -Wl,--disable-auto-image-base ifdef BIT64 LDFLAGS += -Wl,--high-entropy-va -Wl,--pic-executable,-e,mainCRTStartup else - LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup + CFLAGS += -m32 + LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup -m32 endif .PHONY: default all clean From b57a204d9694315e6f64a3fa88abd5a9293ea964 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 25 Dec 2021 10:49:26 +0300 Subject: [PATCH 2/6] Apply each Fake Packet method to each packet separately Before: --set-ttl and --wrong-chksum generated single Fake Packet with both low TTL and incorrect checksum. Now: --set-ttl and --wrong-chksum generate two packets: one with low TTL, another with incorrect checksum. --- src/fakepackets.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/fakepackets.c b/src/fakepackets.c index 5c9dd74..84dce74 100644 --- a/src/fakepackets.c +++ b/src/fakepackets.c @@ -127,6 +127,28 @@ static int send_fake_data(const HANDLE w_filter, return 0; } +static int send_fake_request(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 + ) { + if (set_ttl) { + send_fake_data(w_filter, addr, pkt, packetLen, + is_ipv6, is_https, + set_ttl, FALSE); + } + if (set_checksum) { + send_fake_data(w_filter, addr, pkt, packetLen, + is_ipv6, is_https, + FALSE, set_checksum); + } + return 0; +} + int send_fake_http_request(const HANDLE w_filter, const PWINDIVERT_ADDRESS addr, const char *pkt, @@ -135,15 +157,9 @@ int send_fake_http_request(const HANDLE w_filter, const BYTE set_ttl, const BYTE set_checksum ) { - return send_fake_data(w_filter, - addr, - pkt, - packetLen, - is_ipv6, - FALSE, - set_ttl, - set_checksum - ); + return send_fake_request(w_filter, addr, pkt, packetLen, + is_ipv6, FALSE, + set_ttl, set_checksum); } int send_fake_https_request(const HANDLE w_filter, @@ -154,13 +170,7 @@ int send_fake_https_request(const HANDLE w_filter, const BYTE set_ttl, const BYTE set_checksum ) { - return send_fake_data(w_filter, - addr, - pkt, - packetLen, - is_ipv6, - TRUE, - set_ttl, - set_checksum - ); + return send_fake_request(w_filter, addr, pkt, packetLen, + is_ipv6, TRUE, + set_ttl, set_checksum); } From bbb6af89fea92459231a287a8458d1cc48f8bcf0 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 25 Dec 2021 10:58:42 +0300 Subject: [PATCH 3/6] New Fake Packet circumvention method: wrong SEQ/ACK numbers This method sends Fake Packet with the TCP SEQ/ACK numbers "in the past": -66000 is used for ACK (right out of the ACK permissive window in Linux stack), -10000 is used for SEQ (without any reasoning). This method is pretty effective in Russia. It also could be handy in a networks which prohibit changing TTL values (mobile networks with tethering block/premium feature). --- src/fakepackets.c | 37 ++++++++++++++++++++++++++++--------- src/fakepackets.h | 6 ++++-- src/goodbyedpi.c | 16 +++++++++++++--- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/fakepackets.c b/src/fakepackets.c index 84dce74..7fa7750 100644 --- a/src/fakepackets.c +++ b/src/fakepackets.c @@ -53,7 +53,8 @@ static int send_fake_data(const HANDLE w_filter, const BOOL is_ipv6, const BOOL is_https, const BYTE set_ttl, - const BYTE set_checksum + const BYTE set_checksum, + const BYTE set_seq ) { char packet_fake[MAX_PACKET_SIZE]; WINDIVERT_ADDRESS addr_new; @@ -69,6 +70,9 @@ static int send_fake_data(const HANDLE w_filter, memcpy(&addr_new, addr, sizeof(WINDIVERT_ADDRESS)); memcpy(packet_fake, pkt, packetLen); + addr_new.PseudoTCPChecksum = 0; + addr_new.PseudoIPChecksum = 0; + if (!is_ipv6) { // IPv4 TCP Data packet if (!WinDivertHelperParsePacket(packet_fake, packetLen, &ppIpHdr, @@ -107,8 +111,15 @@ static int send_fake_data(const HANDLE w_filter, ppIpV6Hdr->HopLimit = set_ttl; } + if (set_seq) { + // This is the smallest ACK drift Linux can't handle already, since at least v2.6.18. + // https://github.com/torvalds/linux/blob/v2.6.18/net/netfilter/nf_conntrack_proto_tcp.c#L395 + ppTcpHdr->AckNum = htonl(ntohl(ppTcpHdr->AckNum) - 66000); + // This is just random, no specifics about this value. + ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) - 10000); + } + // Recalculate the checksum - addr_new.PseudoTCPChecksum = 0; WinDivertHelperCalcChecksums(packet_fake, packetLen_new, &addr_new, NULL); if (set_checksum) { @@ -134,17 +145,23 @@ static int send_fake_request(const HANDLE w_filter, const BOOL is_ipv6, const BOOL is_https, const BYTE set_ttl, - const BYTE set_checksum + const BYTE set_checksum, + const BYTE set_seq ) { if (set_ttl) { send_fake_data(w_filter, addr, pkt, packetLen, is_ipv6, is_https, - set_ttl, FALSE); + set_ttl, FALSE, FALSE); } if (set_checksum) { send_fake_data(w_filter, addr, pkt, packetLen, is_ipv6, is_https, - FALSE, set_checksum); + FALSE, set_checksum, FALSE); + } + if (set_seq) { + send_fake_data(w_filter, addr, pkt, packetLen, + is_ipv6, is_https, + FALSE, FALSE, set_seq); } return 0; } @@ -155,11 +172,12 @@ int send_fake_http_request(const HANDLE w_filter, const UINT packetLen, const BOOL is_ipv6, const BYTE set_ttl, - const BYTE set_checksum + const BYTE set_checksum, + const BYTE set_seq ) { return send_fake_request(w_filter, addr, pkt, packetLen, is_ipv6, FALSE, - set_ttl, set_checksum); + set_ttl, set_checksum, set_seq); } int send_fake_https_request(const HANDLE w_filter, @@ -168,9 +186,10 @@ int send_fake_https_request(const HANDLE w_filter, const UINT packetLen, const BOOL is_ipv6, const BYTE set_ttl, - const BYTE set_checksum + const BYTE set_checksum, + const BYTE set_seq ) { return send_fake_request(w_filter, addr, pkt, packetLen, is_ipv6, TRUE, - set_ttl, set_checksum); + set_ttl, set_checksum, set_seq); } diff --git a/src/fakepackets.h b/src/fakepackets.h index de20c7e..9bb44d4 100644 --- a/src/fakepackets.h +++ b/src/fakepackets.h @@ -4,7 +4,8 @@ int send_fake_http_request(const HANDLE w_filter, const UINT packetLen, const BOOL is_ipv6, const BYTE set_ttl, - const BYTE set_checksum + const BYTE set_checksum, + const BYTE set_seq ); int send_fake_https_request(const HANDLE w_filter, const PWINDIVERT_ADDRESS addr, @@ -12,5 +13,6 @@ int send_fake_https_request(const HANDLE w_filter, const UINT packetLen, const BOOL is_ipv6, const BYTE set_ttl, - const BYTE set_checksum + const BYTE set_checksum, + const BYTE set_seq ); diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c index a80b6e5..0b87ffe 100644 --- a/src/goodbyedpi.c +++ b/src/goodbyedpi.c @@ -125,6 +125,7 @@ static struct option long_options[] = { {"ip-id", required_argument, 0, 'i' }, {"set-ttl", required_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 } @@ -453,6 +454,7 @@ int main(int argc, char *argv[]) { do_dns_verb = 0, do_blacklist = 0, do_fake_packet = 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; @@ -665,6 +667,10 @@ int main(int argc, char *argv[]) { 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; @@ -703,6 +709,7 @@ int main(int argc, char *argv[]) { " --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" @@ -730,13 +737,15 @@ int main(int argc, char *argv[]) { "Mix Host: %d\nHTTP AllPorts: %d\nHTTP Persistent Nowait: %d\n" "DNS redirect: %d\nDNSv6 redirect: %d\n" "Fake requests, TTL: %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_wrong_chksum + do_dnsv6_redirect, ttl_of_fake_packet, do_wrong_chksum, + do_wrong_seq ); if (do_fragment_http && http_fragment_size > 2) { @@ -863,7 +872,7 @@ int main(int argc, char *argv[]) { if (packet_dataLen >=2 && memcmp(packet_data, "\x16\x03", 2) == 0) { if (do_fake_packet) { send_fake_https_request(w_filter, &addr, packet, packetLen, packet_v6, - ttl_of_fake_packet, do_wrong_chksum); + ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); } if (do_native_frag) { // Signal for native fragmentation code handler @@ -899,7 +908,8 @@ int main(int argc, char *argv[]) { if (do_fake_packet) send_fake_http_request(w_filter, &addr, packet, packetLen, packet_v6, - ttl_of_fake_packet, do_wrong_chksum); + ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); + } if (do_host_mixedcase) { mix_case(host_addr, host_len); From 21ff80b43c6150ef0de2cad44e141eea11d1c660 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 25 Dec 2021 11:13:50 +0300 Subject: [PATCH 4/6] Automatic TTL value picker for --set-ttl Fake Packet mode This is a per-connection (per-destination) automatic TTL adjusting feature. Basically a --set-ttl mode where you don't need to set specific TTL value. It works as follows: 1. All incoming SYN/ACKs (the response to client's SYN) are intercepted 2. TTL value is extracted from SYN/ACK 3. New TTL is calculated with the simple formula: 128 > extracted_ttl > 64: // Server is running Windows fakepacket_ttl = 128 - extracted_ttl - decrement 64 > extracted_ttl > 34: // Server is running Linux/FreeBSD/other fakepacket_ttl = 64 - extracted_ttl - decrement 4. Fake packet is sent To comply with the multi-path multi-hop server connections where 1 hop dispersion is not rare, decrement should be at least of value "2", which is the default. The patch does not process "too strange" TTL values (bigger than 128, less than 34). --- src/dnsredir.h | 3 + src/goodbyedpi.c | 81 +++++++++++++--- src/ttltrack.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++ src/ttltrack.h | 25 +++++ 4 files changed, 330 insertions(+), 14 deletions(-) create mode 100644 src/ttltrack.c create mode 100644 src/ttltrack.h diff --git a/src/dnsredir.h b/src/dnsredir.h index 106c6fb..d5fed65 100644 --- a/src/dnsredir.h +++ b/src/dnsredir.h @@ -1,3 +1,5 @@ +#ifndef _DNSREDIR_H +#define _DNSREDIR_H #include typedef struct conntrack_info { @@ -34,3 +36,4 @@ int dns_handle_outgoing(const uint32_t srcip[4], const uint16_t srcport, void flush_dns_cache(); int dns_is_dns_packet(const char *packet_data, const UINT packet_dataLen, const int outgoing); +#endif diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c index 0b87ffe..e468182 100644 --- a/src/goodbyedpi.c +++ b/src/goodbyedpi.c @@ -16,6 +16,7 @@ #include "utils/repl_str.h" #include "service.h" #include "dnsredir.h" +#include "ttltrack.h" #include "blackwhitelist.h" #include "fakepackets.h" @@ -94,6 +95,22 @@ WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pA } \ } 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; @@ -124,6 +141,7 @@ static struct option long_options[] = { {"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, '*' }, @@ -442,6 +460,7 @@ int main(int argc, char *argv[]) { 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, @@ -451,8 +470,9 @@ 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_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; @@ -651,6 +671,7 @@ int main(int argc, char *argv[]) { break; case 'v': do_dns_verb = 1; + do_tcp_verb = 1; break; case 'b': do_blacklist = 1; @@ -663,6 +684,15 @@ int main(int argc, char *argv[]) { 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; @@ -705,7 +735,8 @@ int main(int argc, char *argv[]) { " 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" + " --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" @@ -736,7 +767,7 @@ int main(int argc, char *argv[]) { "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\nFake requests, wrong checksum: %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), @@ -744,8 +775,8 @@ int main(int argc, char *argv[]) { 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_wrong_chksum, - do_wrong_seq + do_dnsv6_redirect, ttl_of_fake_packet, do_auto_ttl, + do_wrong_chksum, do_wrong_seq ); if (do_fragment_http && http_fragment_size > 2) { @@ -871,6 +902,9 @@ int main(int argc, char *argv[]) { { 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); } @@ -906,7 +940,10 @@ int main(int argc, char *argv[]) { should_recalc_checksum = 1; } - if (do_fake_packet) + 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); } @@ -1022,20 +1059,36 @@ int main(int argc, char *argv[]) { 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 && - !do_native_frag) { + 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_fragment_http && ppTcpHdr->SrcPort == htons(80)) { - change_window_size(ppTcpHdr, http_fragment_size); - should_recalc_checksum = 1; + + 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."); + } } - else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) { - change_window_size(ppTcpHdr, https_fragment_size); - should_recalc_checksum = 1; + + 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; + } } } } diff --git a/src/ttltrack.c b/src/ttltrack.c new file mode 100644 index 0000000..97aeb82 --- /dev/null +++ b/src/ttltrack.c @@ -0,0 +1,235 @@ +/** + * TCP (TTL) Connection Tracker for GoodbyeDPI + * + * Monitors SYN/ACK only, to extract the TTL value of the remote server. + * + */ + +#include +#include +#include +#include "goodbyedpi.h" +#include "ttltrack.h" +#include "utils/uthash.h" + + +/* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */ +#define TCP_CONNRECORD_KEY_LEN 37 + +#define TCP_CLEANUP_INTERVAL_SEC 30 + +/* HACK! + * uthash uses strlen() for HASH_FIND_STR. + * We have null bytes in our key, so we can't use strlen() + * And since it's always TCP_CONNRECORD_KEY_LEN bytes long, + * we don't need to use any string function to determine length. + */ +#undef uthash_strlen +#define uthash_strlen(s) TCP_CONNRECORD_KEY_LEN + +typedef struct tcp_connrecord { + /* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */ + char key[TCP_CONNRECORD_KEY_LEN]; + time_t time; /* time when this record was added */ + uint16_t ttl; + UT_hash_handle hh; /* makes this structure hashable */ +} tcp_connrecord_t; + +static time_t last_cleanup = 0; +static tcp_connrecord_t *conntrack = NULL; + +inline static void fill_key_data(char *key, const uint8_t is_ipv6, const uint32_t srcip[4], + const uint32_t dstip[4], const uint16_t srcport, const uint16_t dstport) +{ + int offset = 0; + + if (is_ipv6) { + *(uint8_t*)(key) = '6'; + offset += sizeof(uint8_t); + ipv6_copy_addr((uint32_t*)(key + offset), srcip); + offset += sizeof(uint32_t) * 4; + ipv6_copy_addr((uint32_t*)(key + offset), dstip); + offset += sizeof(uint32_t) * 4; + } + else { + *(uint8_t*)(key) = '4'; + offset += sizeof(uint8_t); + ipv4_copy_addr((uint32_t*)(key + offset), srcip); + offset += sizeof(uint32_t) * 4; + ipv4_copy_addr((uint32_t*)(key + offset), dstip); + offset += sizeof(uint32_t) * 4; + } + + *(uint16_t*)(key + offset) = srcport; + offset += sizeof(srcport); + *(uint16_t*)(key + offset) = dstport; + offset += sizeof(dstport); +} + +inline static void fill_data_from_key(uint8_t *is_ipv6, uint32_t srcip[4], uint32_t dstip[4], + uint16_t *srcport, uint16_t *dstport, const char *key) +{ + int offset = 0; + + if (key[0] == '6') { + *is_ipv6 = 1; + offset += sizeof(uint8_t); + ipv6_copy_addr(srcip, (uint32_t*)(key + offset)); + offset += sizeof(uint32_t) * 4; + ipv6_copy_addr(dstip, (uint32_t*)(key + offset)); + offset += sizeof(uint32_t) * 4; + } + else { + *is_ipv6 = 0; + offset += sizeof(uint8_t); + ipv4_copy_addr(srcip, (uint32_t*)(key + offset)); + offset += sizeof(uint32_t) * 4; + ipv4_copy_addr(dstip, (uint32_t*)(key + offset)); + offset += sizeof(uint32_t) * 4; + } + *srcport = *(uint16_t*)(key + offset); + offset += sizeof(*srcport); + *dstport = *(uint16_t*)(key + offset); + offset += sizeof(*dstport); +} + +inline static void construct_key(const uint32_t srcip[4], const uint32_t dstip[4], + const uint16_t srcport, const uint16_t dstport, + char *key, const uint8_t is_ipv6) +{ + debug("Construct key enter\n"); + if (key) { + debug("Constructing key\n"); + fill_key_data(key, is_ipv6, srcip, dstip, srcport, dstport); + } + debug("Construct key end\n"); +} + +inline static void deconstruct_key(const char *key, const tcp_connrecord_t *connrecord, + tcp_conntrack_info_t *conn_info) +{ + debug("Deconstruct key enter\n"); + if (key && conn_info) { + debug("Deconstructing key\n"); + fill_data_from_key(&conn_info->is_ipv6, + conn_info->srcip, conn_info->dstip, + &conn_info->srcport, &conn_info->dstport, + key); + + conn_info->ttl = connrecord->ttl; + } + debug("Deconstruct key end\n"); +} + +static int check_get_tcp_conntrack_key(const char *key, tcp_connrecord_t **connrecord) { + tcp_connrecord_t *tmp_connrecord = NULL; + if (!conntrack) return FALSE; + + HASH_FIND_STR(conntrack, key, tmp_connrecord); + if (tmp_connrecord) { + if (connrecord) + *connrecord = tmp_connrecord; + debug("check_get_tcp_conntrack_key found key\n"); + return TRUE; + } + debug("check_get_tcp_conntrack_key key not found\n"); + return FALSE; +} + +static int add_tcp_conntrack(const uint32_t srcip[4], const uint32_t dstip[4], + const uint16_t srcport, const uint16_t dstport, + const uint8_t is_ipv6, const uint8_t ttl + ) +{ + if (!(srcip && srcport && dstip && dstport)) + return FALSE; + + tcp_connrecord_t *tmp_connrecord = malloc(sizeof(tcp_connrecord_t)); + construct_key(srcip, dstip, srcport, dstport, tmp_connrecord->key, is_ipv6); + + if (!check_get_tcp_conntrack_key(tmp_connrecord->key, NULL)) { + tmp_connrecord->time = time(NULL); + tmp_connrecord->ttl = ttl; + HASH_ADD_STR(conntrack, key, tmp_connrecord); + debug("Added TCP conntrack %u:%hu - %u:%hu\n", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport)); + return TRUE; + } + debug("Not added TCP conntrack %u:%hu - %u:%hu\n", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport)); + free(tmp_connrecord); + return FALSE; +} + +static void tcp_cleanup() { + tcp_connrecord_t *tmp_connrecord, *tmp_connrecord2 = NULL; + + if (last_cleanup == 0) { + last_cleanup = time(NULL); + return; + } + + if (difftime(time(NULL), last_cleanup) >= TCP_CLEANUP_INTERVAL_SEC) { + last_cleanup = time(NULL); + + HASH_ITER(hh, conntrack, tmp_connrecord, tmp_connrecord2) { + if (difftime(last_cleanup, tmp_connrecord->time) >= TCP_CLEANUP_INTERVAL_SEC) { + HASH_DEL(conntrack, tmp_connrecord); + free(tmp_connrecord); + } + } + } +} + +int tcp_handle_incoming(const uint32_t srcip[4], const uint32_t dstip[4], + const uint16_t srcport, const uint16_t dstport, + const uint8_t is_ipv6, const uint8_t ttl) +{ + tcp_cleanup(); + + debug("trying to add TCP srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport)); + return add_tcp_conntrack(srcip, dstip, srcport, dstport, is_ipv6, ttl); + + debug("____tcp_handle_incoming FALSE: srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport)); + return FALSE; +} + +int tcp_handle_outgoing(const uint32_t srcip[4], const uint32_t dstip[4], + const uint16_t srcport, const uint16_t dstport, + tcp_conntrack_info_t *conn_info, + const uint8_t is_ipv6) +{ + char key[TCP_CONNRECORD_KEY_LEN]; + tcp_connrecord_t *tmp_connrecord = NULL; + + if (!conn_info) + return FALSE; + + tcp_cleanup(); + construct_key(dstip, srcip, dstport, srcport, key, is_ipv6); + if (check_get_tcp_conntrack_key(key, &tmp_connrecord) && tmp_connrecord) { + /* Connection exists in conntrack, moving on */ + deconstruct_key(key, tmp_connrecord, conn_info); + HASH_DEL(conntrack, tmp_connrecord); + free(tmp_connrecord); + debug("____tcp_handle_outgoing TRUE: srcport = %hu\n", ntohs(srcport)); + return TRUE; + } + + debug("____tcp_handle_outgoing FALSE: srcport = %hu\n", ntohs(srcport)); + return FALSE; +} + +int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t decrease_for) { + uint8_t ttl_of_fake_packet = 0; + + if (ttl > 64 && ttl < 128) { + ttl_of_fake_packet = 128 - ttl - decrease_for; + } + else if (ttl > 34 && ttl < 64) { + ttl_of_fake_packet = 64 - ttl - decrease_for; + } + else { + ttl_of_fake_packet = 0; + } + + return ttl_of_fake_packet; +} \ No newline at end of file diff --git a/src/ttltrack.h b/src/ttltrack.h new file mode 100644 index 0000000..2447588 --- /dev/null +++ b/src/ttltrack.h @@ -0,0 +1,25 @@ +#ifndef _TTLTRACK_H +#define _TTLTRACK_H +#include +#include "dnsredir.h" + +typedef struct tcp_conntrack_info { + uint8_t is_ipv6; + uint8_t ttl; + uint32_t srcip[4]; + uint16_t srcport; + uint32_t dstip[4]; + uint16_t dstport; +} tcp_conntrack_info_t; + +int tcp_handle_incoming(const uint32_t srcip[4], const uint32_t dstip[4], + const uint16_t srcport, const uint16_t dstport, + const uint8_t is_ipv6, const uint8_t ttl); + +int tcp_handle_outgoing(const uint32_t srcip[4], const uint32_t dstip[4], + const uint16_t srcport, const uint16_t dstport, + tcp_conntrack_info_t *conn_info, + const uint8_t is_ipv6); + +int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t decrease_for); +#endif From 80fcd9c5cf22c511dc3643d496d80700b3cf2129 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 25 Dec 2021 11:16:52 +0300 Subject: [PATCH 5/6] Replace required/optional brackets in the readme --- README.md | 25 ++++++++++++++----------- src/goodbyedpi.c | 22 +++++++++++----------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 1cbe511..ed545ef 100644 --- a/README.md +++ b/README.md @@ -17,26 +17,29 @@ Usage: goodbyedpi.exe [OPTION...] -r replace Host with hoSt -s remove space between host header and its value -m mix Host header case (test.com -> tEsT.cOm) - -f [value] set HTTP fragmentation to value - -k [value] enable HTTP persistent (keep-alive) fragmentation and set it to value + -f set HTTP fragmentation to value + -k enable HTTP persistent (keep-alive) fragmentation and set it to value -n do not wait for first segment ACK when -k is enabled - -e [value] set HTTPS fragmentation to value + -e set HTTPS fragmentation to value -a additional space between Method and Request-URI (enables -s, may break sites) -w try to find and parse HTTP traffic on all processed ports (not only on port 80) - --port [value] additional TCP port to perform fragmentation on (and HTTP tricks with -w) - --ip-id [value] handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID). + --port additional TCP port to perform fragmentation on (and HTTP tricks with -w) + --ip-id handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID). This option can be supplied multiple times. - --dns-addr [value] redirect UDP DNS requests to the supplied IP address (experimental) - --dns-port [value] redirect UDP DNS requests to the supplied port (53 by default) - --dnsv6-addr [value] redirect UDPv6 DNS requests to the supplied IPv6 address (experimental) - --dnsv6-port [value] redirect UDPv6 DNS requests to the supplied port (53 by default) + --dns-addr redirect UDP DNS requests to the supplied IP address (experimental) + --dns-port redirect UDP DNS requests to the supplied port (53 by default) + --dnsv6-addr redirect UDPv6 DNS requests to the supplied IPv6 address (experimental) + --dnsv6-port redirect UDPv6 DNS requests to the supplied port (53 by default) --dns-verb print verbose DNS redirection messages - --blacklist [txtfile] perform HTTP tricks only to host names and subdomains from + --blacklist 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. + --set-ttl activate Fake Request Mode and send it with supplied TTL value. DANGEROUS! May break websites in unexpected ways. Use with care. + --auto-ttl [decttl] activate Fake Request Mode, automatically detect TTL and decrease + it from standard 64 or 128 by decttl (128/64 - TTL - 2 by default). --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. + --wrong-seq activate Fake Request Mode and send it with TCP SEQ/ACK in the past. --native-frag fragment (split) the packets by sending them in smaller packets, without shrinking the Window Size. Works faster (does not slow down the connection) and better. diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c index e468182..c895f56 100644 --- a/src/goodbyedpi.c +++ b/src/goodbyedpi.c @@ -719,21 +719,21 @@ int main(int argc, char *argv[]) { " -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" + " -f set HTTP fragmentation to value\n" + " -k 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" + " -e 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" + " --port additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n" + " --ip-id handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n" + " --dns-addr redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n" + " --dns-port redirect UDPv4 DNS requests to the supplied port (53 by default)\n" + " --dnsv6-addr redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n" + " --dnsv6-port 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" + " --blacklist 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" + " --set-ttl 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" From b45b463d51dc18f4b10a2a1547402d7d11eddddd Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Sat, 25 Dec 2021 11:41:24 +0300 Subject: [PATCH 6/6] Update readme --- README.md | 5 +++-- src/goodbyedpi.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ed545ef..7f9a757 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,9 @@ Usage: goodbyedpi.exe [OPTION...] --dnsv6-addr redirect UDPv6 DNS requests to the supplied IPv6 address (experimental) --dnsv6-port redirect UDPv6 DNS requests to the supplied port (53 by default) --dns-verb print verbose DNS redirection messages - --blacklist perform HTTP tricks only to host names and subdomains from - supplied text file. This option can be supplied multiple times. + --blacklist perform circumvention tricks only to host names and subdomains from + supplied text file (HTTP Host/TLS SNI). + This option can be supplied multiple times. --set-ttl activate Fake Request Mode and send it with supplied TTL value. DANGEROUS! May break websites in unexpected ways. Use with care. --auto-ttl [decttl] activate Fake Request Mode, automatically detect TTL and decrease diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c index c895f56..12ac231 100644 --- a/src/goodbyedpi.c +++ b/src/goodbyedpi.c @@ -731,8 +731,9 @@ int main(int argc, char *argv[]) { " --dnsv6-addr redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n" " --dnsv6-port redirect UDPv6 DNS requests to the supplied port (53 by default)\n" " --dns-verb print verbose DNS redirection messages\n" - " --blacklist perform HTTP tricks only to host names and subdomains from\n" - " supplied text file. This option can be supplied multiple times.\n" + " --blacklist perform circumvention tricks only to host names and subdomains from\n" + " supplied text file (HTTP Host/TLS SNI).\n" + " This option can be supplied multiple times.\n" " --set-ttl 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"