diff --git a/Kbuild b/Kbuild index 3962a79..1290c84 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o tls.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/args.c b/args.c index 42df82a..142d969 100644 --- a/args.c +++ b/args.c @@ -7,7 +7,9 @@ #include #include #include +#include "types.h" +static char custom_fake_buf[MAX_FAKE_SIZE]; struct config_t config = { .threads = THREADS_NUM, @@ -18,6 +20,8 @@ struct config_t config = { .faking_ttl = FAKE_TTL, .fake_sni = 1, .fake_sni_seq_len = 1, + .fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT, + .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .frag_middle_sni = 1, .frag_sni_pos = 1, .use_ipv6 = 1, @@ -55,6 +59,8 @@ struct config_t config = { .queue_start_num = DEFAULT_QUEUE_NUM, .fake_sni_pkt = fake_sni_old, .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator + .fake_custom_pkt = custom_fake_buf, + .fake_custom_pkt_sz = 0 }; #define OPT_SNI_DOMAINS 1 @@ -63,6 +69,9 @@ struct config_t config = { #define OPT_FAKING_TTL 3 #define OPT_FAKING_STRATEGY 10 #define OPT_FAKE_SNI_SEQ_LEN 11 +#define OPT_FAKE_SNI_SEQ_TYPE 26 +#define OPT_FAKE_SNI_TYPE 27 +#define OPT_FAKE_CUSTOM_PAYLOAD 28 #define OPT_FRAG 4 #define OPT_FRAG_SNI_REVERSE 12 #define OPT_FRAG_SNI_FAKED 13 @@ -83,7 +92,7 @@ struct config_t config = { #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 -#define OPT_MAX OPT_SNI_DOMAINS +#define OPT_MAX OPT_FAKE_CUSTOM_PAYLOAD static struct option long_opt[] = { {"help", 0, 0, 'h'}, @@ -94,6 +103,9 @@ static struct option long_opt[] = { {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, + {"fake-sni-seq-type", 1, 0, OPT_FAKE_SNI_SEQ_TYPE}, + {"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE}, + {"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD}, {"faking-strategy", 1, 0, OPT_FAKING_STRATEGY}, {"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET}, {"faking-ttl", 1, 0, OPT_FAKING_TTL}, @@ -150,6 +162,9 @@ void print_usage(const char *argv0) { printf("\t--exclude-domains=\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); + printf("\t--fake-sni-seq-type={default|random|custom}\n"); + printf("\t--fake-sni-type={default|random|custom}\n"); + printf("\t--fake-custom-payload=\n"); printf("\t--fake-seq-offset=\n"); printf("\t--faking-ttl=\n"); printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n"); @@ -299,7 +314,7 @@ int parse_args(int argc, char *argv[]) { break; case OPT_FAKE_SEQ_OFFSET: num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { + if (errno != 0) { goto invalid_opt; } @@ -323,6 +338,66 @@ int parse_args(int argc, char *argv[]) { config.fake_sni_seq_len = num; break; + case OPT_FAKE_SNI_SEQ_TYPE: + if (strcmp(optarg, "default") == 0) { + config.fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT; + } else if (strcmp(optarg, "random") == 0) { + config.fake_sni_seq_type = FAKE_PAYLOAD_RANDOM; + } else if (strcmp(optarg, "custom") == 0) { + config.fake_sni_seq_type = FAKE_PAYLOAD_CUSTOM; + } else { + goto invalid_opt; + } + + break; + case OPT_FAKE_SNI_TYPE: + if (strcmp(optarg, "default") == 0) { + config.fake_sni_type = FAKE_PAYLOAD_DEFAULT; + } else if (strcmp(optarg, "random") == 0) { + config.fake_sni_type = FAKE_PAYLOAD_RANDOM; + } else if (strcmp(optarg, "custom") == 0) { + config.fake_sni_type = FAKE_PAYLOAD_CUSTOM; + } else { + goto invalid_opt; + } + + break; + case OPT_FAKE_CUSTOM_PAYLOAD: { + uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; + + const char *custom_hex_fake = optarg; + size_t custom_hlen = strlen(custom_hex_fake); + if ((custom_hlen & 1) == 1) { + printf("Custom fake hex should be divisible by two\n"); + goto invalid_opt; + } + + + size_t custom_len = custom_hlen >> 1; + if (custom_len > MAX_FAKE_SIZE) { + printf("Custom fake is too large\n"); + goto invalid_opt; + } + + for (int i = 0; i < custom_len; i++) { + sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); + } + + config.fake_custom_pkt_sz = custom_len; + config.fake_custom_pkt = (char *)custom_buf; + + // if (strcmp(optarg, "default") == 0) { + // config.fake_sni_type = FAKE_PAYLOAD_DEFAULT; + // } else if (strcmp(optarg, "random") == 0) { + // config.fake_sni_type = FAKE_PAYLOAD_RANDOM; + // } else if (strcmp(optarg, "custom") == 0) { + // config.fake_sni_type = FAKE_PAYLOAD_CUSTOM; + // } else { + // goto invalid_opt; + // } + // + } + break; case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { diff --git a/config.h b/config.h index 2b5393f..43fd9f8 100644 --- a/config.h +++ b/config.h @@ -32,6 +32,14 @@ struct config_t { unsigned char faking_ttl; int fake_sni; unsigned int fake_sni_seq_len; + +#define FAKE_PAYLOAD_RANDOM 0 +#define FAKE_PAYLOAD_CUSTOM 1 +// In default mode all other options will be skipped. +#define FAKE_PAYLOAD_DEFAULT 2 + int fake_sni_seq_type; + int fake_sni_type; + #define VERBOSE_INFO 0 #define VERBOSE_DEBUG 1 #define VERBOSE_TRACE 2 @@ -47,10 +55,16 @@ struct config_t { const char *exclude_domains_str; unsigned int exclude_domains_strlen; unsigned int all_domains; + const char *fake_sni_pkt; unsigned int fake_sni_pkt_sz; + + const char *fake_custom_pkt; + unsigned int fake_custom_pkt_sz; + + unsigned int fk_winsize; - unsigned int fakeseq_offset; + int fakeseq_offset; unsigned int mark; int synfake; unsigned int synfake_len; @@ -89,19 +103,30 @@ extern struct config_t config; #define FAKE_TTL 8 // Will invalidate fake packets by out-of-ack_seq out-of-seq request -#define FAKE_STRAT_RAND_SEQ 1 +#define FAKE_STRAT_RAND_SEQ (1 << 0) // Will assume that GGC server is located further than FAKE_TTL // Thus, Fake packet will be eliminated automatically. -#define FAKE_STRAT_TTL 2 -#define FAKE_STRAT_PAST_SEQ 3 -#define FAKE_STRAT_TCP_CHECK 4 -#define FAKE_STRAT_TCP_MD5SUM 5 +#define FAKE_STRAT_TTL (1 << 1) +#define FAKE_STRAT_PAST_SEQ (1 << 2) +#define FAKE_STRAT_TCP_CHECK (1 << 3) +#define FAKE_STRAT_TCP_MD5SUM (1 << 4) +#define FAKE_STRAT_COUNT 5 + +/** + * This macros iterates through all faking strategies and executes code under it. + * destination strategy will be available under name of `strategy` variable. + */ +#define ITER_FAKE_STRAT(fake_bitmask, strategy) \ +for (int strategy = 1; strategy <= (1 << FAKE_STRAT_COUNT); strategy <<= 1) \ +if ((fake_bitmask) & strategy) #ifndef FAKING_STRATEGY #define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ #endif +#define MAX_FAKE_SIZE 1300 + #if !defined(SILENT) && !defined(KERNEL_SPACE) #define DEBUG #endif diff --git a/mangle.c b/mangle.c index 0b6c45d..88380e7 100644 --- a/mangle.c +++ b/mangle.c @@ -5,6 +5,7 @@ #include "utils.h" #include "quic.h" #include "logging.h" +#include "tls.h" #ifndef KERNEL_SPACE #include @@ -527,31 +528,33 @@ send_frag1: send_fake: if (config.frag_sni_faked) { - uint32_t iphfl, tcphfl; - fake_pad_len = f2len; - ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); - if (ret < 0) { - lgerror("Invalid frag2", ret); - goto erret_lc; + ITER_FAKE_STRAT(config.faking_strategy, strategy) { + uint32_t iphfl, tcphfl; + fake_pad_len = f2len; + ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); + if (ret < 0) { + lgerror("Invalid frag2", ret); + goto erret_lc; + } + memcpy(fake_pad, frag2, iphfl + tcphfl); + memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); + struct tcphdr *fakethdr = (void *)(fake_pad + iphfl); + if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { + lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq)); + fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs); + lgtrace_addp("%u, ", ntohl(fakethdr->seq)); + } + ret = fail_packet(strategy, + fake_pad, &fake_pad_len, MAX_PACKET_SIZE); + if (ret < 0) { + lgerror("Failed to fail packet", ret); + goto erret_lc; + } + ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0); + if (ret < 0) { + goto erret_lc; + } } - memcpy(fake_pad, frag2, iphfl + tcphfl); - memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); - struct tcphdr *fakethdr = (void *)(fake_pad + iphfl); - if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { - lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq)); - fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs); - lgtrace_addp("%u, ", ntohl(fakethdr->seq)); - } - ret = fail_packet(fake_pad, &fake_pad_len, MAX_PACKET_SIZE); - if (ret < 0) { - lgerror("Failed to fail packet", ret); - goto erret_lc; - } - ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0); - if (ret < 0) { - goto erret_lc; - } - } if (config.frag_sni_reverse) @@ -596,476 +599,131 @@ int post_fake_sni(const void *iph, unsigned int iph_len, void *fsiph = (void *)rfsiph; struct tcphdr *fstcph = (void *)rfstcph; - for (int i = 0; i < sequence_len; i++) { + ITER_FAKE_STRAT(config.faking_strategy, strategy) { + struct fake_type fake_seq_type = { + .type = FAKE_PAYLOAD_DEFAULT, + .strategy = strategy, + }; + + switch (config.fake_sni_seq_type) { + case FAKE_PAYLOAD_RANDOM: + fake_seq_type.type = FAKE_PAYLOAD_RANDOM; + break; + case FAKE_PAYLOAD_CUSTOM: + fake_seq_type.type = FAKE_PAYLOAD_CUSTOM; + fake_seq_type.fake_data = config.fake_custom_pkt; + fake_seq_type.fake_len = config.fake_custom_pkt_sz; + break; + default: + fake_seq_type.type = FAKE_PAYLOAD_DEFAULT; + } + + for (int i = 0; i < sequence_len; i++) { + NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(fake_sni)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } + uint32_t fsn_len = MAX_PACKET_SIZE; + + ret = gen_fake_sni( + fake_seq_type, + fsiph, iph_len, fstcph, tcph_len, + fake_sni, &fsn_len); + if (ret < 0) { + lgerror("gen_fake_sni", ret); + goto erret_lc; + } + + lgtrace_addp("post fake sni #%d", i + 1); + lgtrace_addp("post with %d bytes", fsn_len); + ret = instance_config.send_raw_packet(fake_sni, fsn_len); + if (ret < 0) { + lgerror("send fake sni", ret); + goto erret_lc; + } + + if (!(config.faking_strategy == FAKE_STRAT_PAST_SEQ || + config.faking_strategy == FAKE_STRAT_RAND_SEQ)) { + + uint32_t iph_len; + uint32_t tcph_len; + uint32_t plen; + ret = tcp_payload_split( + fake_sni, fsn_len, + &fsiph, &iph_len, + &fstcph, &tcph_len, + NULL, &plen); + + if (ret < 0) { + lgtrace_addp("continue fake seq"); + goto erret_lc; + } + + fstcph->seq = htonl(ntohl(fstcph->seq) + plen); + memcpy(rfsiph, fsiph, iph_len); + memcpy(rfstcph, fstcph, tcph_len); + fsiph = (void *)rfsiph; + fstcph = (void *)rfstcph; + } + + NETBUF_FREE(fake_sni); + continue; +erret_lc: + NETBUF_FREE(fake_sni); + return ret; + } + + struct fake_type ftype = { + .type = FAKE_PAYLOAD_DEFAULT, + .strategy = strategy + }; + + switch (config.fake_sni_type) { + case FAKE_PAYLOAD_RANDOM: + ftype.type = FAKE_PAYLOAD_RANDOM; + break; + case FAKE_PAYLOAD_CUSTOM: + ftype.type = FAKE_PAYLOAD_CUSTOM; + ftype.fake_data = config.fake_custom_pkt; + ftype.fake_len = config.fake_custom_pkt_sz; + break; + default: + ftype.type = FAKE_PAYLOAD_DEFAULT; + } + NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_sni)) { lgerror("Allocation error", -ENOMEM); return -ENOMEM; } uint32_t fsn_len = MAX_PACKET_SIZE; - ret = gen_fake_sni(fsiph, iph_len, fstcph, tcph_len, - fake_sni, &fsn_len); + ret = gen_fake_sni( + ftype, + iph, iph_len, tcph, tcph_len, + fake_sni, &fsn_len); if (ret < 0) { lgerror("gen_fake_sni", ret); - goto erret_lc; + goto erret_lc_cst; } - lgtrace_addp("post fake sni #%d", i + 1); - lgtrace_addp("post with %d", fsn_len); + lgtrace_addp("post normal fake sni"); + lgtrace_addp("post with %d bytes", fsn_len); ret = instance_config.send_raw_packet(fake_sni, fsn_len); if (ret < 0) { lgerror("send fake sni", ret); - goto erret_lc; + goto erret_lc_cst; } - uint32_t iph_len; - uint32_t tcph_len; - uint32_t plen; - tcp_payload_split( - fake_sni, fsn_len, - &fsiph, &iph_len, - &fstcph, &tcph_len, - NULL, &plen); + goto after_cus2; - - fstcph->seq = htonl(ntohl(fstcph->seq) + plen); - memcpy(rfsiph, fsiph, iph_len); - memcpy(rfstcph, fstcph, tcph_len); - fsiph = (void *)rfsiph; - fstcph = (void *)rfstcph; - - NETBUF_FREE(fake_sni); - continue; -erret_lc: +erret_lc_cst: NETBUF_FREE(fake_sni); return ret; +after_cus2: + ; } return 0; } -#define TLS_CONTENT_TYPE_HANDSHAKE 0x16 -#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 -#define TLS_EXTENSION_SNI 0x0000 -#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d - -/** - * Processes tls payload of the tcp request. - * - * data Payload data of TCP. - * dlen Length of `data`. - */ -struct tls_verdict analyze_tls_data( - const uint8_t *data, - uint32_t dlen) -{ - struct tls_verdict vrd = {0}; - - size_t i = 0; - const uint8_t *data_end = data + dlen; - - while (i + 4 < dlen) { - const uint8_t *msgData = data + i; - - uint8_t tls_content_type = *msgData; - uint8_t tls_vmajor = *(msgData + 1); - uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); - - if (tls_vmajor != 0x03) goto nextMessage; - - if (i + 5 > dlen) break; - - if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) - goto nextMessage; - - if (config.sni_detection == SNI_DETECTION_BRUTE) { - goto brute; - } - - const uint8_t *handshakeProto = msgData + 5; - - if (handshakeProto + 1 >= data_end) break; - - uint8_t handshakeType = *handshakeProto; - - if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) - goto nextMessage; - - const uint8_t *msgPtr = handshakeProto; - msgPtr += 1; - msgPtr += 3 + 2 + 32; - - if (msgPtr + 1 >= data_end) break; - uint8_t sessionIdLength = *msgPtr; - msgPtr++; - msgPtr += sessionIdLength; - - if (msgPtr + 2 >= data_end) break; - uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); - msgPtr += 2; - msgPtr += ciphersLength; - - if (msgPtr + 1 >= data_end) break; - uint8_t compMethodsLen = *msgPtr; - msgPtr++; - msgPtr += compMethodsLen; - - if (msgPtr + 2 >= data_end) break; - uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr); - msgPtr += 2; - - const uint8_t *extensionsPtr = msgPtr; - const uint8_t *extensions_end = extensionsPtr + extensionsLen; - if (extensions_end > data_end) extensions_end = data_end; - - while (extensionsPtr < extensions_end) { - const uint8_t *extensionPtr = extensionsPtr; - if (extensionPtr + 4 >= extensions_end) break; - - uint16_t extensionType = - ntohs(*(uint16_t *)extensionPtr); - extensionPtr += 2; - - uint16_t extensionLen = - ntohs(*(uint16_t *)extensionPtr); - extensionPtr += 2; - - - if (extensionPtr + extensionLen > extensions_end) - break; - - if (extensionType != TLS_EXTENSION_SNI) - goto nextExtension; - - const uint8_t *sni_ext_ptr = extensionPtr; - - if (sni_ext_ptr + 2 >= extensions_end) break; - uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr); - - sni_ext_ptr += 2; - - const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen; - if (sni_ext_end >= extensions_end) break; - - if (sni_ext_ptr + 3 >= sni_ext_end) break; - sni_ext_ptr++; - uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); - sni_ext_ptr += 2; - - if (sni_ext_ptr + sni_len > sni_ext_end) break; - - char *sni_name = (char *)sni_ext_ptr; - - vrd.sni_offset = (uint8_t *)sni_name - data; - vrd.sni_len = sni_len; - - if (config.all_domains) { - vrd.target_sni = 1; - goto check_domain; - } - - - unsigned int j = 0; - for (unsigned int i = 0; i <= config.domains_strlen; i++) { - if ( i > j && - (i == config.domains_strlen || - config.domains_str[i] == '\0' || - config.domains_str[i] == ',' || - config.domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = config.domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - vrd.target_sni = 1; - goto check_domain; - } - - j = i + 1; - } - } - -check_domain: - if (vrd.target_sni == 1 && config.exclude_domains_strlen != 0) { - unsigned int j = 0; - for (unsigned int i = 0; i <= config.exclude_domains_strlen; i++) { - if ( i > j && - (i == config.exclude_domains_strlen || - config.exclude_domains_str[i] == '\0' || - config.exclude_domains_str[i] == ',' || - config.exclude_domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = config.exclude_domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - - vrd.target_sni = 0; - lgdebugmsg("Excluded SNI: %.*s", - vrd.sni_len, data + vrd.sni_offset); - goto out; - } - - j = i + 1; - } - } - } - - goto out; - -nextExtension: - extensionsPtr += 2 + 2 + extensionLen; - } -nextMessage: - i += 5 + message_length; - } - -out: - return vrd; - - -brute: - if (config.all_domains) { - vrd.target_sni = 1; - vrd.sni_len = 0; - vrd.sni_offset = dlen / 2; - goto out; - } - - unsigned int j = 0; - for (unsigned int i = 0; i <= config.domains_strlen; i++) { - if ( i > j && - (i == config.domains_strlen || - config.domains_str[i] == '\0' || - config.domains_str[i] == ',' || - config.domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *domain_startp = config.domains_str + j; - - if (domain_len + dlen + 1> MAX_PACKET_SIZE) { - continue; - } - - NETBUF_ALLOC(buf, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buf)) { - lgerror("Allocation error", -ENOMEM); - goto out; - } - NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); - if (!NETBUF_CHECK(nzbuf)) { - lgerror("Allocation error", -ENOMEM); - NETBUF_FREE(buf); - goto out; - } - - int *zbuf = (void *)nzbuf; - - memcpy(buf, domain_startp, domain_len); - memcpy(buf + domain_len, "#", 1); - memcpy(buf + domain_len + 1, data, dlen); - - z_function((char *)buf, zbuf, domain_len + 1 + dlen); - - for (unsigned int k = 0; k < dlen; k++) { - if (zbuf[k] == domain_len) { - vrd.target_sni = 1; - vrd.sni_len = domain_len; - vrd.sni_offset = (k - domain_len - 1); - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - goto out; - } - } - - - j = i + 1; - - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - } - } - - goto out; -} - -int gen_fake_sni(const void *ipxh, uint32_t iph_len, - const struct tcphdr *tcph, uint32_t tcph_len, - uint8_t *buf, uint32_t *buflen) { - - if (!ipxh || !tcph || !buf || !buflen) - return -EINVAL; - - int ipxv = netproto_version(ipxh, iph_len); - - if (ipxv == IP4VERSION) { - const struct iphdr *iph = ipxh; - - memcpy(buf, iph, iph_len); - struct iphdr *niph = (struct iphdr *)buf; - - niph->protocol = IPPROTO_TCP; - } else if (ipxv == IP6VERSION) { - const struct ip6_hdr *iph = ipxh; - - iph_len = sizeof(struct ip6_hdr); - memcpy(buf, iph, iph_len); - struct ip6_hdr *niph = (struct ip6_hdr *)buf; - - niph->ip6_nxt = IPPROTO_TCP; - } else { - return -EINVAL; - } - - const char *data = config.fake_sni_pkt; - size_t data_len = config.fake_sni_pkt_sz; - - uint32_t dlen = iph_len + tcph_len + data_len; - - if (*buflen < dlen) - return -ENOMEM; - - memcpy(buf + iph_len, tcph, tcph_len); - memcpy(buf + iph_len + tcph_len, data, data_len); - - - if (ipxv == IP4VERSION) { - struct iphdr *niph = (struct iphdr *)buf; - niph->tot_len = htons(dlen); - } else if (ipxv == IP6VERSION) { - struct ip6_hdr *niph = (struct ip6_hdr *)buf; - niph->ip6_plen = htons(dlen - iph_len); - } - - fail_packet(buf, &dlen, *buflen); - *buflen = dlen; - - return 0; -} - -#define TCP_MD5SIG_LEN 16 -#define TCP_MD5SIG_KIND 19 -struct tcp_md5sig_opt { - uint8_t kind; - uint8_t len; - uint8_t sig[TCP_MD5SIG_LEN]; -}; -#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt)) -// Real length of the option, with NOOP fillers -#define TCP_MD5SIG_OPT_RLEN 20 - -int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { - void *iph; - uint32_t iph_len; - struct tcphdr *tcph; - uint32_t tcph_len; - uint8_t *data; - uint32_t dlen; - int ret; - - ret = tcp_payload_split(payload, *plen, - &iph, &iph_len, &tcph, &tcph_len, - &data, &dlen); - - uint32_t ipxv = netproto_version(payload, *plen); - - if (ret < 0) { - return ret; - } - - - if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) { - lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); - - if (config.fakeseq_offset) { - tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); - } else { -#ifdef KERNEL_SPACE - tcph->seq = 124; -#else - tcph->seq = random(); -#endif - - } - - lgtrace_addp("%u", ntohl(tcph->seq)); - } else if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { - lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); - tcph->seq = htonl(ntohl(tcph->seq) - dlen); - lgtrace_addp("%u", ntohl(tcph->seq)); - - } else if (config.faking_strategy == FAKE_STRAT_TTL) { - lgtrace_addp("set fake ttl to %d", config.faking_ttl); - - if (ipxv == IP4VERSION) { - ((struct iphdr *)iph)->ttl = config.faking_ttl; - } else if (ipxv == IP6VERSION) { - ((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; - } else { - lgerror("fail_packet: IP version is unsupported", -EINVAL); - return -EINVAL; - } - } else if (config.faking_strategy == FAKE_STRAT_TCP_MD5SUM) { - int optp_len = tcph_len - sizeof(struct tcphdr); - int delta = TCP_MD5SIG_OPT_RLEN - optp_len; - lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta); - - if (delta > 0) { - if (avail_buflen - *plen < delta) { - return -1; - } - uint8_t *ndata = data + delta; - uint8_t *ndptr = ndata + dlen; - uint8_t *dptr = data + dlen; - for (size_t i = dlen + 1; i > 0; i--) { - *ndptr = *dptr; - --ndptr, --dptr; - } - data = ndata; - tcph_len = tcph_len + delta; - tcph->doff = tcph_len >> 2; - if (ipxv == IP4VERSION) { - ((struct iphdr *)iph)->tot_len = htons(ntohs(((struct iphdr *)iph)->tot_len) + delta); - } else if (ipxv == IP6VERSION) { - ((struct ip6_hdr *)iph)->ip6_plen = htons(ntohs(((struct ip6_hdr *)iph)->ip6_plen) + delta); - } else { - lgerror("fail_packet: IP version is unsupported", -EINVAL); - return -EINVAL; - } - optp_len += delta; - *plen += delta; - } - - uint8_t *optplace = (uint8_t *)tcph + sizeof(struct tcphdr); - struct tcp_md5sig_opt *mdopt = (void *)optplace; - mdopt->kind = TCP_MD5SIG_KIND; - mdopt->len = TCP_MD5SIG_OPT_LEN; - - optplace += sizeof(struct tcp_md5sig_opt); - optp_len -= sizeof(struct tcp_md5sig_opt); - - while (optp_len-- > 0) { - *optplace++ = 0x01; - } - } - - set_ip_checksum(iph, iph_len); - set_tcp_checksum(tcph, iph, iph_len); - - if (config.faking_strategy == FAKE_STRAT_TCP_CHECK) { - lgtrace_addp("break fake tcp checksum"); - tcph->check += 1; - } - - return 0; -} diff --git a/mangle.h b/mangle.h index f07ab17..4b8afa7 100644 --- a/mangle.h +++ b/mangle.h @@ -3,35 +3,6 @@ #include "types.h" -/** - * Result of analyze_tls_data function - */ -struct tls_verdict { - int target_sni; /* google video hello packet */ - int sni_offset; /* offset from start of tcp _payload_ */ - int sni_len; -}; - -/** - * Processes the packet and finds TLS Client Hello information inside it. - * data pointer points to start of TLS Message (TCP Payload) - */ -struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); - - -/** - * Generates fake client hello message - */ -int gen_fake_sni(const void *iph, uint32_t iph_len, - const struct tcphdr *tcph, uint32_t tcph_len, - uint8_t *buf, uint32_t *buflen); - -/** - * Invalidates the raw packet. The function aims to invalid the packet - * in such way as it will be accepted by DPI, but dropped by target server - */ -int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); - #define PKT_ACCEPT 0 #define PKT_DROP 1 diff --git a/raw_replacements.h b/raw_replacements.h index 4123bd8..8dde7ff 100644 --- a/raw_replacements.h +++ b/raw_replacements.h @@ -3,8 +3,8 @@ #define FAKE_SNI_MAXLEN 1500 -static const char fake_sni[] = "\026\003\001\002\000\001\000\001\374\003\003\323[\345\201f\362\200:B\356Uq\355X\315i\235*\021\367\331\272\a>\233\254\355\307/\342\372\265 \275\2459l&r\222\313\361\3729`\376\256\233\333O\001\373\33050\r\260f,\231\035 \324^\000>\023\002\023\003\023\001\300,\3000\000\237\314\251\314\250\314\252\300+\300/\000\236\300$\300(\000k\300#\300'\000g\300\n\300\024\0009\300\t\300\023\0003\000\235\000\234\000=\000<\0005\000/\000\377\001\000\001u\000\000\000\023\000\021\000\000\016www.google.com\000\v\000\004\003\000\001\002\000\n\000\026\000\024\000\035\000\027\000\036\000\031\000\030\001\000\001\001\001\002\001\003\001\004\000\020\000\016\000\f\002h2\bhttp/1.1\000\026\000\000\000\027\000\000\0001\000\000\000\r\0000\000.\004\003\005\003\006\003\b\a\b\b\b\032\b\033\b\034\b\t\b\n\b\v\b\004\b\005\b\006\004\001\005\001\006\001\003\003\003\001\003\002\004\002\005\002\006\002\000+\000\005\004\003\004\003\003\000-\000\002\001\001\0003\000&\000$\000\035\000 \004\224\206\021\256\f\222\266\3435\216\202\342\2573\341\3503\2107\341\023\016\240r|6\000^K\310s\000\025\000\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; +static const char fake_sni[] = "\026\003\001\002\000\001\000\001\374\003\003\323[\345\201f\362\200:B\356Uq\355X\315i\235*\021\367\331\272\a>\233\254\355\307/\342\372\265 \275\2459l&r\222\313\361\3729`\376\256\233\333O\001\373\33050\r\260f,\231\035 \324^\000>\023\002\023\003\023\001\300,\3000\000\237\314\251\314\250\314\252\300+\300/\000\236\300$\300(\000k\300#\300'\000g\300\n\300\024\0009\300\t\300\023\0003\000\235\000\234\000=\000<\0005\000/\000\377\001\000\001u\000\000\000\023\000\021\000\000\016www.google.com\000\v\000\004\003\000\001\002\000\n\000\026\000\024\000\035\000\027\000\036\000\031\000\030\001\000\001\001\001\002\001\003\001\004\000\020\000\016\000\f\002h2\bhttp/1.1\000\026\000\000\000\027\000\000\0001\000\000\000\r\0000\000.\004\003\005\003\006\003\b\a\b\b\b\032\b\033\b\034\b\t\b\n\b\v\b\004\b\005\b\006\004\001\005\001\006\001\003\003\003\001\003\002\004\002\005\002\006\002\000+\000\005\004\003\004\003\003\000-\000\002\001\001\0003\000&\000$\000\035\000 \004\224\206\021\256\f\222\266\3435\216\202\342\2573\341\3503\2107\341\023\016\240r|6\000^K\310s\000\025\000\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; -static const char fake_sni_old[] = "\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356"; +static const char fake_sni_old[] = "\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317"; #endif /*RAW_REPLACEMENTS_H*/ diff --git a/tls.c b/tls.c new file mode 100644 index 0000000..287f7c8 --- /dev/null +++ b/tls.c @@ -0,0 +1,336 @@ +#include "types.h" +#include "tls.h" +#include "config.h" +#include "logging.h" +#include "utils.h" + +#ifndef KERNEL_SPACE +#include +#include +#endif + +#define TLS_CONTENT_TYPE_HANDSHAKE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 +#define TLS_EXTENSION_SNI 0x0000 +#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d + +/** + * Processes tls payload of the tcp request. + * + * data Payload data of TCP. + * dlen Length of `data`. + */ +struct tls_verdict analyze_tls_data( + const uint8_t *data, + uint32_t dlen) +{ + struct tls_verdict vrd = {0}; + + size_t i = 0; + const uint8_t *data_end = data + dlen; + + while (i + 4 < dlen) { + const uint8_t *msgData = data + i; + + uint8_t tls_content_type = *msgData; + uint8_t tls_vmajor = *(msgData + 1); + uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); + + if (tls_vmajor != 0x03) goto nextMessage; + + if (i + 5 > dlen) break; + + if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) + goto nextMessage; + + if (config.sni_detection == SNI_DETECTION_BRUTE) { + goto brute; + } + + const uint8_t *handshakeProto = msgData + 5; + + if (handshakeProto + 1 >= data_end) break; + + uint8_t handshakeType = *handshakeProto; + + if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) + goto nextMessage; + + const uint8_t *msgPtr = handshakeProto; + msgPtr += 1; + msgPtr += 3 + 2 + 32; + + if (msgPtr + 1 >= data_end) break; + uint8_t sessionIdLength = *msgPtr; + msgPtr++; + msgPtr += sessionIdLength; + + if (msgPtr + 2 >= data_end) break; + uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); + msgPtr += 2; + msgPtr += ciphersLength; + + if (msgPtr + 1 >= data_end) break; + uint8_t compMethodsLen = *msgPtr; + msgPtr++; + msgPtr += compMethodsLen; + + if (msgPtr + 2 >= data_end) break; + uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr); + msgPtr += 2; + + const uint8_t *extensionsPtr = msgPtr; + const uint8_t *extensions_end = extensionsPtr + extensionsLen; + if (extensions_end > data_end) extensions_end = data_end; + + while (extensionsPtr < extensions_end) { + const uint8_t *extensionPtr = extensionsPtr; + if (extensionPtr + 4 >= extensions_end) break; + + uint16_t extensionType = + ntohs(*(uint16_t *)extensionPtr); + extensionPtr += 2; + + uint16_t extensionLen = + ntohs(*(uint16_t *)extensionPtr); + extensionPtr += 2; + + + if (extensionPtr + extensionLen > extensions_end) + break; + + if (extensionType != TLS_EXTENSION_SNI) + goto nextExtension; + + const uint8_t *sni_ext_ptr = extensionPtr; + + if (sni_ext_ptr + 2 >= extensions_end) break; + uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr); + + sni_ext_ptr += 2; + + const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen; + if (sni_ext_end >= extensions_end) break; + + if (sni_ext_ptr + 3 >= sni_ext_end) break; + sni_ext_ptr++; + uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); + sni_ext_ptr += 2; + + if (sni_ext_ptr + sni_len > sni_ext_end) break; + + char *sni_name = (char *)sni_ext_ptr; + + vrd.sni_offset = (uint8_t *)sni_name - data; + vrd.sni_len = sni_len; + + if (config.all_domains) { + vrd.target_sni = 1; + goto check_domain; + } + + + unsigned int j = 0; + for (unsigned int i = 0; i <= config.domains_strlen; i++) { + if ( i > j && + (i == config.domains_strlen || + config.domains_str[i] == '\0' || + config.domains_str[i] == ',' || + config.domains_str[i] == '\n' )) { + + unsigned int domain_len = (i - j); + const char *sni_startp = sni_name + sni_len - domain_len; + const char *domain_startp = config.domains_str + j; + + if (sni_len >= domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + domain_len)) { + vrd.target_sni = 1; + goto check_domain; + } + + j = i + 1; + } + } + +check_domain: + if (vrd.target_sni == 1 && config.exclude_domains_strlen != 0) { + unsigned int j = 0; + for (unsigned int i = 0; i <= config.exclude_domains_strlen; i++) { + if ( i > j && + (i == config.exclude_domains_strlen || + config.exclude_domains_str[i] == '\0' || + config.exclude_domains_str[i] == ',' || + config.exclude_domains_str[i] == '\n' )) { + + unsigned int domain_len = (i - j); + const char *sni_startp = sni_name + sni_len - domain_len; + const char *domain_startp = config.exclude_domains_str + j; + + if (sni_len >= domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + domain_len)) { + + vrd.target_sni = 0; + lgdebugmsg("Excluded SNI: %.*s", + vrd.sni_len, data + vrd.sni_offset); + goto out; + } + + j = i + 1; + } + } + } + + goto out; + +nextExtension: + extensionsPtr += 2 + 2 + extensionLen; + } +nextMessage: + i += 5 + message_length; + } + +out: + return vrd; + + +brute: + if (config.all_domains) { + vrd.target_sni = 1; + vrd.sni_len = 0; + vrd.sni_offset = dlen / 2; + goto out; + } + + unsigned int j = 0; + for (unsigned int i = 0; i <= config.domains_strlen; i++) { + if ( i > j && + (i == config.domains_strlen || + config.domains_str[i] == '\0' || + config.domains_str[i] == ',' || + config.domains_str[i] == '\n' )) { + + unsigned int domain_len = (i - j); + const char *domain_startp = config.domains_str + j; + + if (domain_len + dlen + 1> MAX_PACKET_SIZE) { + continue; + } + + NETBUF_ALLOC(buf, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buf)) { + lgerror("Allocation error", -ENOMEM); + goto out; + } + NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); + if (!NETBUF_CHECK(nzbuf)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(buf); + goto out; + } + + int *zbuf = (void *)nzbuf; + + memcpy(buf, domain_startp, domain_len); + memcpy(buf + domain_len, "#", 1); + memcpy(buf + domain_len + 1, data, dlen); + + z_function((char *)buf, zbuf, domain_len + 1 + dlen); + + for (unsigned int k = 0; k < dlen; k++) { + if (zbuf[k] == domain_len) { + vrd.target_sni = 1; + vrd.sni_len = domain_len; + vrd.sni_offset = (k - domain_len - 1); + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + goto out; + } + } + + + j = i + 1; + + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + } + } + + goto out; +} + +int gen_fake_sni(struct fake_type type, + const void *ipxh, uint32_t iph_len, + const struct tcphdr *tcph, uint32_t tcph_len, + uint8_t *buf, uint32_t *buflen) { + + uint32_t data_len = type.fake_len; + if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { + data_len = random() % 1200; + } else if (type.type == FAKE_PAYLOAD_DEFAULT) { + data_len = config.fake_sni_pkt_sz; + } + + if (!ipxh || !tcph || !buf || !buflen) + return -EINVAL; + + int ipxv = netproto_version(ipxh, iph_len); + + if (ipxv == IP4VERSION) { + const struct iphdr *iph = ipxh; + + memcpy(buf, iph, iph_len); + struct iphdr *niph = (struct iphdr *)buf; + + niph->protocol = IPPROTO_TCP; + } else if (ipxv == IP6VERSION) { + const struct ip6_hdr *iph = ipxh; + + iph_len = sizeof(struct ip6_hdr); + memcpy(buf, iph, iph_len); + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + + niph->ip6_nxt = IPPROTO_TCP; + } else { + return -EINVAL; + } + + uint32_t dlen = iph_len + tcph_len + data_len; + + if (*buflen < dlen) + return -ENOMEM; + + memcpy(buf + iph_len, tcph, tcph_len); + uint8_t *bfdptr = buf + iph_len + tcph_len; + + switch (type.type) { + case FAKE_PAYLOAD_DEFAULT: + memcpy(bfdptr, config.fake_sni_pkt, data_len); + break; + case FAKE_PAYLOAD_DATA: + memcpy(bfdptr, type.fake_data, data_len); + break; + default: // FAKE_PAYLOAD_RANDOM + getrandom(bfdptr, data_len, 0); + } + + if (ipxv == IP4VERSION) { + struct iphdr *niph = (struct iphdr *)buf; + niph->tot_len = htons(dlen); + } else if (ipxv == IP6VERSION) { + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + niph->ip6_plen = htons(dlen - iph_len); + } + + fail_packet(type.strategy, buf, &dlen, *buflen); + + *buflen = dlen; + + return 0; +} + diff --git a/tls.h b/tls.h new file mode 100644 index 0000000..ba71d19 --- /dev/null +++ b/tls.h @@ -0,0 +1,53 @@ +#ifndef TLS_H +#define TLS_H + +#include "types.h" + + +/** + * Result of analyze_tls_data function + */ +struct tls_verdict { + int target_sni; /* google video hello packet */ + int sni_offset; /* offset from start of tcp _payload_ */ + int sni_len; +}; + +/** + * Processes the packet and finds TLS Client Hello information inside it. + * data pointer points to start of TLS Message (TCP Payload) + */ +struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); + + +struct fake_type { + +#define FAKE_PAYLOAD_RANDOM 0 +#define FAKE_PAYLOAD_DATA 1 +// In default mode all other options will be skipped. +#define FAKE_PAYLOAD_DEFAULT 2 + int type; + + // Length of the final fake message. + // Pass 0 in RANDOM mode to make it random + uint16_t fake_len; + + // Payload of the fake message of fake_len length. + // Will be omitted in RANDOM mode. + const char *fake_data; + + // faking strategy of the fake packet. + // Does not support bitmask, pass standalone strategy. + // Pass 0 if you don't want any faking procedures. + unsigned int strategy; +}; + +/** + * Generates the fake client hello message + */ +int gen_fake_sni(struct fake_type type, + const void *iph, uint32_t iph_len, + const struct tcphdr *tcph, uint32_t tcph_len, + uint8_t *buf, uint32_t *buflen); + +#endif /* TLS_H */ diff --git a/uspace.mk b/uspace.mk index db43b15..d119c84 100644 --- a/uspace.mk +++ b/uspace.mk @@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la diff --git a/utils.c b/utils.c index 19f14fa..e93b4c1 100644 --- a/utils.c +++ b/utils.c @@ -523,3 +523,119 @@ void z_function(const char *str, int *zbuf, size_t len) { } } +#define TCP_MD5SIG_LEN 16 +#define TCP_MD5SIG_KIND 19 +struct tcp_md5sig_opt { + uint8_t kind; + uint8_t len; + uint8_t sig[TCP_MD5SIG_LEN]; +}; +#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt)) +// Real length of the option, with NOOP fillers +#define TCP_MD5SIG_OPT_RLEN 20 + +int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { + void *iph; + uint32_t iph_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint8_t *data; + uint32_t dlen; + int ret; + + ret = tcp_payload_split(payload, *plen, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + + uint32_t ipxv = netproto_version(payload, *plen); + + if (ret < 0) { + return ret; + } + + + if (strategy == FAKE_STRAT_RAND_SEQ) { + lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); + + if (config.fakeseq_offset) { + tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); + } else { +#ifdef KERNEL_SPACE + tcph->seq = 124; +#else + tcph->seq = random(); +#endif + + } + + lgtrace_addp("%u", ntohl(tcph->seq)); + } else if (strategy == FAKE_STRAT_PAST_SEQ) { + lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); + tcph->seq = htonl(ntohl(tcph->seq) - dlen); + lgtrace_addp("%u", ntohl(tcph->seq)); + + } else if (strategy == FAKE_STRAT_TTL) { + lgtrace_addp("set fake ttl to %d", config.faking_ttl); + + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->ttl = config.faking_ttl; + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; + } else { + lgerror("fail_packet: IP version is unsupported", -EINVAL); + return -EINVAL; + } + } else if (strategy == FAKE_STRAT_TCP_MD5SUM) { + int optp_len = tcph_len - sizeof(struct tcphdr); + int delta = TCP_MD5SIG_OPT_RLEN - optp_len; + lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta); + + if (delta > 0) { + if (avail_buflen - *plen < delta) { + return -1; + } + uint8_t *ndata = data + delta; + uint8_t *ndptr = ndata + dlen; + uint8_t *dptr = data + dlen; + for (size_t i = dlen + 1; i > 0; i--) { + *ndptr = *dptr; + --ndptr, --dptr; + } + data = ndata; + tcph_len = tcph_len + delta; + tcph->doff = tcph_len >> 2; + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->tot_len = htons(ntohs(((struct iphdr *)iph)->tot_len) + delta); + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_plen = htons(ntohs(((struct ip6_hdr *)iph)->ip6_plen) + delta); + } else { + lgerror("fail_packet: IP version is unsupported", -EINVAL); + return -EINVAL; + } + optp_len += delta; + *plen += delta; + } + + uint8_t *optplace = (uint8_t *)tcph + sizeof(struct tcphdr); + struct tcp_md5sig_opt *mdopt = (void *)optplace; + mdopt->kind = TCP_MD5SIG_KIND; + mdopt->len = TCP_MD5SIG_OPT_LEN; + + optplace += sizeof(struct tcp_md5sig_opt); + optp_len -= sizeof(struct tcp_md5sig_opt); + + while (optp_len-- > 0) { + *optplace++ = 0x01; + } + } + + set_ip_checksum(iph, iph_len); + set_tcp_checksum(tcph, iph, iph_len); + + if (strategy == FAKE_STRAT_TCP_CHECK) { + lgtrace_addp("break fake tcp checksum"); + tcph->check += 1; + } + + return 0; +} diff --git a/utils.h b/utils.h index b9ad085..77106b0 100644 --- a/utils.h +++ b/utils.h @@ -101,4 +101,14 @@ int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len); void z_function(const char *str, int *zbuf, size_t len); + +/** + * Invalidates the raw packet. The function aims to invalid the packet + * in such way as it will be accepted by DPI, but dropped by target server + * + * Does not support bitmask, pass standalone strategy. + */ +int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); + + #endif /* UTILS_H */