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