From 2e96aa150e8af92f593f6311623a7daaebc4ebc7 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 11 Aug 2024 02:10:18 +0300 Subject: [PATCH] Add multiple fooling options --- args.c | 127 +++++++++++++----- config.h | 52 +++++--- mangle.c | 333 ++++++++++++++++++++++++++++++++++++++++++++++- mangle.h | 11 ++ uspace.mk | 2 +- youtubeUnblock.c | 296 +++++++++++------------------------------ 6 files changed, 548 insertions(+), 273 deletions(-) diff --git a/args.c b/args.c index d833507..1de5654 100644 --- a/args.c +++ b/args.c @@ -9,11 +9,14 @@ struct config_t config = { - .rawsocket = -2, .threads = THREADS_NUM, + .frag_sni_reverse = 0, + .frag_sni_faked = 0, .fragmentation_strategy = FRAGMENTATION_STRATEGY, - .fake_sni_strategy = FAKE_SNI_STRATEGY, - .fake_sni_ttl = FAKE_SNI_TTL, + .faking_strategy = FAKING_STRATEGY, + .faking_ttl = FAKE_TTL, + .fake_sni = 1, + .fake_sni_seq_len = 1, #ifdef SEG2_DELAY .seg2_delay = SEG2_DELAY, @@ -40,26 +43,36 @@ struct config_t config = { #define OPT_SNI_DOMAINS 1 #define OPT_FAKE_SNI 2 -#define OPT_FAKE_SNI_TTL 3 +#define OPT_FAKING_TTL 3 +#define OPT_FAKING_STRATEGY 10 +#define OPT_FAKE_SNI_SEQ_LEN 11 #define OPT_FRAG 4 +#define OPT_FRAG_SNI_REVERSE 12 +#define OPT_FRAG_SNI_FAKED 13 #define OPT_SEG2DELAY 5 #define OPT_THREADS 6 #define OPT_SILENT 7 #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 +#define OPT_MAX OPT_FRAG_SNI_FAKED + static struct option long_opt[] = { - {"help", 0, 0, 'h'}, - {"version", 0, 0, 'v'}, - {"sni-domains", 1, 0, OPT_SNI_DOMAINS}, - {"fake-sni", 1, 0, OPT_FAKE_SNI}, - {"fake-sni-ttl", 1, 0, OPT_FAKE_SNI_TTL}, - {"frag", 1, 0, OPT_FRAG}, - {"seg2delay", 1, 0, OPT_SEG2DELAY}, - {"threads", 1, 0, OPT_THREADS}, - {"silent", 0, 0, OPT_SILENT}, - {"no-gso", 0, 0, OPT_NO_GSO}, - {"queue-num", 1, 0, OPT_QUEUE_NUM}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {"sni-domains", 1, 0, OPT_SNI_DOMAINS}, + {"fake-sni", 1, 0, OPT_FAKE_SNI}, + {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, + {"faking-strategy", 1, 0, OPT_FAKING_STRATEGY}, + {"faking-ttl", 1, 0, OPT_FAKING_TTL}, + {"frag", 1, 0, OPT_FRAG}, + {"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE}, + {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, + {"seg2delay", 1, 0, OPT_SEG2DELAY}, + {"threads", 1, 0, OPT_THREADS}, + {"silent", 0, 0, OPT_SILENT}, + {"no-gso", 0, 0, OPT_NO_GSO}, + {"queue-num", 1, 0, OPT_QUEUE_NUM}, {0,0,0,0} }; @@ -93,9 +106,13 @@ void print_usage(const char *argv0) { printf("Options:\n"); printf("\t--queue-num=\n"); printf("\t--sni-domains=|all\n"); - printf("\t--fake-sni={ack,ttl,none}\n"); - printf("\t--fake-sni-ttl=\n"); + printf("\t--fake-sni={1|0}\n"); + printf("\t--fake-sni-seq-len=\n"); + printf("\t--faking-ttl=\n"); + printf("\t--faking-strategy={ack,ttl}\n"); printf("\t--frag={tcp,ip,none}\n"); + printf("\t--frag-sni-reverse={0|1}\n"); + printf("\t--frag-sni-faked={0|1}\n"); printf("\t--seg2delay=\n"); printf("\t--threads=\n"); printf("\t--silent\n"); @@ -143,19 +160,72 @@ int parse_args(int argc, char *argv[]) { } break; - case OPT_FAKE_SNI: - if (strcmp(optarg, "ack") == 0) { - config.fake_sni_strategy = FKSN_STRAT_ACK_SEQ; - } else if (strcmp(optarg, "ttl") == 0) { - config.fake_sni_strategy = FKSN_STRAT_TTL; - } else if (strcmp(optarg, "none") == 0) { - config.fake_sni_strategy = FKSN_STRAT_NONE; + case OPT_FRAG_SNI_FAKED: + if (strcmp(optarg, "1") == 0) { + config.frag_sni_faked = 1; + } else if (strcmp(optarg, "0") == 0) { + config.frag_sni_faked = 0; } else { errno = EINVAL; printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } + break; + case OPT_FRAG_SNI_REVERSE: + if (strcmp(optarg, "1") == 0) { + config.frag_sni_reverse = 1; + } else if (strcmp(optarg, "0") == 0) { + config.frag_sni_reverse = 0; + } else { + errno = EINVAL; + printf("Invalid option %s\n", long_opt[optIdx].name); + goto error; + } + + break; + case OPT_FAKING_STRATEGY: + if (strcmp(optarg, "ack") == 0) { + config.faking_strategy = FAKE_STRAT_ACK_SEQ; + } else if (strcmp(optarg, "ttl") == 0) { + config.faking_strategy = FAKE_STRAT_TTL; + } else { + errno = EINVAL; + printf("Invalid option %s\n", long_opt[optIdx].name); + goto error; + } + + break; + case OPT_FAKING_TTL: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > 255) { + printf("Invalid option %s\n", long_opt[optIdx].name); + goto error; + } + + config.faking_ttl = num; + break; + + case OPT_FAKE_SNI: + if (strcmp(optarg, "1") == 0) { + config.fake_sni = 1; + } else if (strcmp(optarg, "0") == 0) { + config.fake_sni = 0; + } else { + errno = EINVAL; + printf("Invalid option %s\n", long_opt[optIdx].name); + goto error; + } + + break; + case OPT_FAKE_SNI_SEQ_LEN: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > 255) { + printf("Invalid option %s\n", long_opt[optIdx].name); + goto error; + } + + config.fake_sni_seq_len = num; break; case OPT_SEG2DELAY: num = parse_numeric_option(optarg); @@ -175,15 +245,6 @@ int parse_args(int argc, char *argv[]) { config.threads = num; break; - case OPT_FAKE_SNI_TTL: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0 || num > 255) { - printf("Invalid option %s\n", long_opt[optIdx].name); - goto error; - } - - config.fake_sni_ttl = num; - break; case OPT_QUEUE_NUM: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { diff --git a/config.h b/config.h index 28b59c6..4cbe00d 100644 --- a/config.h +++ b/config.h @@ -1,18 +1,38 @@ +#ifndef YTB_CONFIG_H +#define YTB_CONFIG_H + +typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); +/** + * Sends the packet after delay_ms. The function should schedule send and return immediately + * (for example, open daemon thread) + */ +typedef void (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms); + +struct instance_config_t { + raw_send_t send_raw_packet; + delayed_send_t send_delayed_packet; +}; +extern struct instance_config_t instance_config; struct config_t { unsigned int queue_start_num; - int rawsocket; int threads; int use_gso; int fragmentation_strategy; - unsigned char fake_sni_ttl; - int fake_sni_strategy; + int frag_sni_reverse; + int frag_sni_faked; + int faking_strategy; + unsigned char faking_ttl; + int fake_sni; + unsigned int fake_sni_seq_len; int verbose; + /* In milliseconds */ unsigned int seg2_delay; const char *domains_str; unsigned int domains_strlen; unsigned int all_domains; }; + extern struct config_t config; #define MAX_THREADS 16 @@ -43,23 +63,17 @@ extern struct config_t config; #define SEG2_DELAY 100 #endif -#define FAKE_SNI_TTL 8 +#define FAKE_TTL 8 -// No fake SNI -#define FKSN_STRAT_NONE 0 -// Will invalidate fake client hello by out-of-ack_seq out-of-seq request -#define FKSN_STRAT_ACK_SEQ 1 -// Will assume that GGC server is located further than FAKE_SNI_TTL -// Thus, Fake Client Hello will be eliminated automatically. -#define FKSN_STRAT_TTL 2 +// Will invalidate fake packets by out-of-ack_seq out-of-seq request +#define FAKE_STRAT_ACK_SEQ 1 +// Will assume that GGC server is located further than FAKE_TTL +// Thus, Fake packet will be eliminated automatically. +#define FAKE_STRAT_TTL 2 -#ifdef NO_FAKE_SNI -#define FAKE_SNI_STRATEGY FKSN_STRAT_NONE -#endif - -#ifndef FAKE_SNI_STRATEGY -#define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ +#ifndef FAKING_STRATEGY +#define FAKING_STRATEGY FAKE_STRAT_ACK_SEQ #endif #if !defined(SILENT) && !defined(KERNEL_SPACE) @@ -72,4 +86,8 @@ extern struct config_t config; #define DEFAULT_QUEUE_NUM 537 +#define MAX_PACKET_SIZE 8192 + static const char defaul_snistr[] = "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com"; + +#endif /* YTB_CONFIG_H */ diff --git a/mangle.c b/mangle.c index 462b9cf..c3e973b 100644 --- a/mangle.c +++ b/mangle.c @@ -1,5 +1,5 @@ +#include #define _GNU_SOURCE - #include "mangle.h" #include "raw_replacements.h" #include "config.h" @@ -23,6 +23,292 @@ typedef uint16_t __u16; #define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) #endif +static int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) { + if (poses_sz == 0) { + return instance_config.send_raw_packet(packet, pktlen); + } + + { + uint8_t frag1[MAX_PACKET_SIZE]; + uint8_t frag2[MAX_PACKET_SIZE]; + uint32_t f1len = MAX_PACKET_SIZE; + uint32_t f2len = MAX_PACKET_SIZE; + + int ret; + + if (dvs > poses[0]) { + printf("send_frags: Recursive dvs(%d) is more than poses0(%d)\n", dvs, poses[0]); + return -EINVAL; + } + + ret = ip4_frag(packet, pktlen, poses[0] - dvs, + frag1, &f1len, frag2, &f2len); + + if (ret < 0) { + lgerror("send_frags: frag", ret); + printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs); + return ret; + } + + dvs += poses[0]; + ret = send_ip4_frags(frag1, f1len, NULL, 0, 0); + if (ret < 0) { + return ret; + } + + ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) { + if (poses_sz == 0) { + if (config.seg2_delay && dvs > 0) { + if (!instance_config.send_delayed_packet) { + return -EINVAL; + } + + instance_config.send_delayed_packet( + packet, pktlen, config.seg2_delay); + + return 0; + } else { + return instance_config.send_raw_packet( + packet, pktlen); + } + } else { + uint8_t frag1[MAX_PACKET_SIZE]; + uint8_t frag2[MAX_PACKET_SIZE]; + uint8_t fake_pad[MAX_PACKET_SIZE]; + uint32_t f1len = MAX_PACKET_SIZE; + uint32_t f2len = MAX_PACKET_SIZE; + + int ret; + + if (dvs > poses[0]) { + printf("send_frags: Recursive dvs(%d) is more than poses0(%d)\n", dvs, poses[0]); + return -EINVAL; + } + + ret = tcp4_frag(packet, pktlen, poses[0] - dvs, + frag1, &f1len, frag2, &f2len); + + if (ret < 0) { + lgerror("send_frags: frag", ret); + printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs); + return ret; + } + + int reverse = config.frag_sni_reverse; + + if (reverse) + goto send_frag2; + +send_frag1: + { + ret = send_tcp4_frags(frag1, f1len, NULL, 0, 0); + if (ret < 0) { + return ret; + } + + if (reverse) + goto out; + } + +send_fake: + if (config.frag_sni_faked) { + uint32_t iphfl, tcphfl; + ret = tcp4_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); + if (ret < 0) { + lgerror("Invalid frag2", ret); + return ret; + } + memcpy(fake_pad, frag2, iphfl + tcphfl); + memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); + ret = fail4_packet(fake_pad, f2len); + if (ret < 0) { + lgerror("Failed to fail packet", ret); + return ret; + } + ret = send_tcp4_frags(fake_pad, f2len, NULL, 0, 0); + if (ret < 0) { + return ret; + } + + if (reverse) + goto send_frag1; + } + + +send_frag2: + { + dvs += poses[0]; + ret = send_tcp4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); + if (ret < 0) { + return ret; + } + + if (reverse) + goto send_fake; + } + } +out: + + return 0; +} + + + +int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { + if (raw_payload_len > MAX_PACKET_SIZE) { + return PKT_ACCEPT; + } + + const struct iphdr *iph; + uint32_t iph_len; + const struct tcphdr *tcph; + uint32_t tcph_len; + const uint8_t *data; + uint32_t dlen; + + int ret = tcp4_payload_split((uint8_t *)raw_payload, raw_payload_len, + (struct iphdr **)&iph, &iph_len, (struct tcphdr **)&tcph, &tcph_len, + (uint8_t **)&data, &dlen); + + if (ret < 0) { + goto accept; + } + + struct verdict vrd = analyze_tls_data(data, dlen); + + if (vrd.target_sni) { + if (config.verbose) + printf("SNI target detected\n"); + + uint8_t payload[MAX_PACKET_SIZE]; + uint32_t payload_len = raw_payload_len; + memcpy(payload, raw_payload, raw_payload_len); + + struct iphdr *iph; + uint32_t iph_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint8_t *data; + uint32_t dlen; + + int ret = tcp4_payload_split(payload, payload_len, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + ip4_set_checksum(iph); + tcp4_set_checksum(tcph, iph); + + + if (dlen > 1480 && config.verbose) { + printf("WARNING! Client Hello packet is too big and may cause issues!\n"); + } + + if (config.fake_sni) { + uint8_t rfsiph[60]; + uint8_t rfstcph[60]; + + memcpy(rfsiph, iph, iph_len); + memcpy(rfstcph, tcph, tcph_len); + + struct iphdr *fsiph = (void *)rfsiph; + struct tcphdr *fstcph = (void *)rfstcph; + + for (int i = 0; i < config.fake_sni_seq_len; i++) { + uint8_t fake_sni[MAX_PACKET_SIZE]; + uint32_t fsn_len = MAX_PACKET_SIZE; + ret = gen_fake_sni(fsiph, fstcph, fake_sni, &fsn_len); + if (ret < 0) { + lgerror("gen_fake_sni", ret); + goto accept; + } + + ret = instance_config.send_raw_packet(fake_sni, fsn_len); + if (ret < 0) { + lgerror("send fake sni", ret); + goto accept; + } + + uint32_t iph_len; + uint32_t tcph_len; + uint32_t plen; + tcp4_payload_split(fake_sni, fsn_len, &fsiph, &iph_len, &fstcph, &tcph_len, NULL, &plen); + + + fstcph->seq = htonl(ntohl(fstcph->seq) + plen); + memcpy(rfsiph, fsiph, iph_len); + memcpy(rfstcph, fstcph, tcph_len); + fsiph = (void *)rfsiph; + fstcph = (void *)rfstcph; + + } + } + + size_t ipd_offset; + size_t mid_offset; + + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: { + ipd_offset = vrd.sni_offset; + mid_offset = ipd_offset + vrd.sni_len / 2; + + uint32_t poses[] = { 2, mid_offset }; + + ret = send_tcp4_frags(payload, payload_len, poses, 2, 0); + if (ret < 0) { + lgerror("tcp4 send frags", ret); + goto accept; + } + + goto drop; + } + break; + case FRAG_STRAT_IP: { + ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; + mid_offset = ipd_offset + vrd.sni_len / 2; + mid_offset += 8 - mid_offset % 8; + + uint32_t poses[] = { mid_offset }; + ret = send_tcp4_frags(payload, payload_len, poses, 1, 0); + if (ret < 0) { + lgerror("ip4 send frags", ret); + goto accept; + } + + goto drop; + } + break; + default: + ret = instance_config.send_raw_packet(payload, payload_len); + if (ret < 0) { + lgerror("raw pack send", ret); + goto accept; + } + + goto drop; + } + + + + goto drop; + } + +accept: + return PKT_ACCEPT; +drop: + return PKT_DROP; +} + + + + void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph) { #ifdef KERNEL_SPACE @@ -52,15 +338,22 @@ int ip4_payload_split(__u8 *pkt, __u32 buflen, struct iphdr **iph, __u32 *iph_len, __u8 **payload, __u32 *plen) { if (pkt == NULL || buflen < sizeof(struct iphdr)) { + lgerror("ip4_payload_split: pkt|buflen", -EINVAL); return -EINVAL; } struct iphdr *hdr = (struct iphdr *)pkt; - if (hdr->version != IPVERSION) return -EINVAL; + if (hdr->version != IPVERSION) { + lgerror("ip4_payload_split: ipversion", -EINVAL); + return -EINVAL; + } __u32 hdr_len = hdr->ihl * 4; __u32 pktlen = ntohs(hdr->tot_len); - if (buflen < pktlen || hdr_len > pktlen) return -EINVAL; + if (buflen < pktlen || hdr_len > pktlen) { + lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL); + return -EINVAL; + } if (iph) *iph = hdr; @@ -474,10 +767,10 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, ntcph->th_dport = tcph->th_dport; ntcph->th_sport = tcph->th_sport; - if (config.fake_sni_strategy == FKSN_STRAT_TTL) { + if (config.faking_strategy == FAKE_STRAT_TTL) { ntcph->seq = tcph->seq; ntcph->ack_seq = tcph->ack_seq; - niph->ttl = config.fake_sni_ttl; + niph->ttl = config.faking_ttl; } #endif @@ -487,3 +780,33 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, return 0; } + +int fail4_packet(uint8_t *payload, uint32_t plen) { + struct iphdr *iph; + uint32_t iph_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint8_t *data; + uint32_t dlen; + int ret; + + ret = tcp4_payload_split(payload, plen, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + + if (ret < 0) { + return ret; + } + + if (config.faking_strategy == FAKE_STRAT_ACK_SEQ) { + tcph->seq = random(); + tcph->ack_seq = random(); + } else if (config.faking_strategy == FAKE_STRAT_TTL) { + iph->ttl = config.faking_ttl; + } + + ip4_set_checksum(iph); + tcp4_set_checksum(tcph, iph); + + return 0; +} diff --git a/mangle.h b/mangle.h index 1e3a173..dd0b882 100644 --- a/mangle.h +++ b/mangle.h @@ -63,4 +63,15 @@ void ip4_set_checksum(struct iphdr *iph); int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, uint8_t *buf, uint32_t *buflen); + +int fail4_packet(uint8_t *payload, uint32_t plen); + +#define PKT_ACCEPT 0 +#define PKT_DROP 1 + +/** + * Processes the packet and returns verdict. + * This is the primary function that traverses the packet. + */ +int process_packet(const uint8_t *packet, uint32_t packet_len); #endif /* YU_MANGLE_H */ diff --git a/uspace.mk b/uspace.mk index 5aaa254..5c8d062 100644 --- a/uspace.mk +++ b/uspace.mk @@ -22,7 +22,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c +SRCS := youtubeUnblock.c mangle.c args.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 6221c3c..2025176 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -7,10 +7,10 @@ #error "The build aims to the kernel, not userspace" #endif -#include #include #include +#include #include #include #include @@ -31,6 +31,9 @@ #include "args.h" pthread_mutex_t rawsocket_lock; +int rawsocket = -2; + + static int open_socket(struct mnl_socket **_nl) { struct mnl_socket *nl = NULL; @@ -67,20 +70,20 @@ static int close_socket(struct mnl_socket **_nl) { } static int open_raw_socket(void) { - if (config.rawsocket != -2) { + if (rawsocket != -2) { errno = EALREADY; perror("Raw socket is already opened"); return -1; } - config.rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - if (config.rawsocket == -1) { + rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (rawsocket == -1) { perror("Unable to create raw socket"); return -1; } int mark = RAWSOCKET_MARK; - if (setsockopt(config.rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) + if (setsockopt(rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark); return -1; @@ -89,24 +92,24 @@ static int open_raw_socket(void) { int mst = pthread_mutex_init(&rawsocket_lock, NULL); if (mst) { fprintf(stderr, "Mutex err: %d\n", mst); - close(config.rawsocket); + close(rawsocket); errno = mst; return -1; } - return config.rawsocket; + return rawsocket; } static int close_raw_socket(void) { - if (config.rawsocket < 0) { + if (rawsocket < 0) { errno = EALREADY; perror("Raw socket is not set"); return -1; } - if (close(config.rawsocket)) { + if (close(rawsocket)) { perror("Unable to close raw socket"); pthread_mutex_destroy(&rawsocket_lock); return -1; @@ -114,7 +117,7 @@ static int close_raw_socket(void) { pthread_mutex_destroy(&rawsocket_lock); - config.rawsocket = -2; + rawsocket = -2; return 0; } @@ -191,7 +194,7 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { pthread_mutex_lock(&rawsocket_lock); - int sent = sendto(config.rawsocket, + int sent = sendto(rawsocket, pkt, pktlen, 0, (struct sockaddr *)&daddr, sizeof(daddr)); @@ -203,6 +206,8 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { return sent; } + + struct packet_data { uint32_t id; uint16_t hw_proto; @@ -243,7 +248,7 @@ struct dps_t { uint32_t timer; }; // Note that the thread will automatically release dps_t and pkt_buff -void *delay_packet_send(void *data) { +void *delay_packet_send_fn(void *data) { struct dps_t *dpdt = data; uint8_t *pkt = dpdt->pkt; @@ -260,206 +265,23 @@ void *delay_packet_send(void *data) { free(dpdt); return NULL; } - -static int process_packet(const struct packet_data packet, struct queue_data qdata) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *verdnlh; -#ifdef DEBUG_LOGGING - printf("packet received (id=%u hw=0x%04x hook=%u, payload len %u)\n", - packet.id, packet.hw_proto, packet.hook, packet.payload_len); -#endif - - if (packet.hw_proto != ETH_P_IP) { - return fallback_accept_packet(packet.id, qdata); - } - - const int family = AF_INET; - - const uint8_t *raw_payload = packet.payload; - size_t raw_payload_len = packet.payload_len; - - const struct iphdr *iph; - uint32_t iph_len; - const struct tcphdr *tcph; - uint32_t tcph_len; - const uint8_t *data; - uint32_t dlen; - - int ret = tcp4_payload_split((uint8_t *)raw_payload, raw_payload_len, - (struct iphdr **)&iph, &iph_len, (struct tcphdr **)&tcph, &tcph_len, - (uint8_t **)&data, &dlen); - - if (ret < 0) { - goto fallback; - } - - struct verdict vrd = analyze_tls_data(data, dlen); - - verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata.queue_num); - nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT); - - if (vrd.target_sni) { - if (config.verbose) - printf("SNI target detected\n"); - - if (dlen > 1480) { - if (config.verbose) - fprintf(stderr, "WARNING! Client Hello packet is too big and may cause issues!\n"); - } - - uint8_t frag1[MNL_SOCKET_BUFFER_SIZE]; - uint8_t frag2[MNL_SOCKET_BUFFER_SIZE]; - uint32_t f1len = MNL_SOCKET_BUFFER_SIZE; - uint32_t f2len = MNL_SOCKET_BUFFER_SIZE; - nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP); - int ret = 0; - - nfq_ip_set_checksum((struct iphdr *)iph); - nfq_tcp_compute_checksum_ipv4( - (struct tcphdr *)tcph, (struct iphdr *)iph); - - if (config.fake_sni_strategy != FKSN_STRAT_NONE) { - uint8_t rfsiph[60]; - uint8_t rfstcph[60]; - - memcpy(rfsiph, iph, iph_len); - memcpy(rfstcph, tcph, tcph_len); - - struct iphdr *fsiph = (void *)rfsiph; - struct tcphdr *fstcph = (void *)rfstcph; - - for (int i = 0; i < 10; i++) { - uint8_t fake_sni[MNL_SOCKET_BUFFER_SIZE]; - uint32_t fsn_len = MNL_SOCKET_BUFFER_SIZE; - ret = gen_fake_sni(fsiph, fstcph, fake_sni, &fsn_len); - if (ret < 0) { - errno = -ret; - perror("gen_fake_sni"); - goto fallback; - } - - printf("%d\n", i); - ret = send_raw_socket(fake_sni, fsn_len); - if (ret < 0) { - errno = -ret; - perror("send fake sni"); - goto fallback; - } - - uint32_t iph_len; - uint32_t tcph_len; - uint32_t plen; - tcp4_payload_split(fake_sni, fsn_len, &fsiph, &iph_len, &fstcph, &tcph_len, NULL, &plen); - - - fstcph->seq = htonl(ntohl(tcph->seq) + plen * (i + 1)); - memcpy(rfsiph, fsiph, iph_len); - memcpy(rfstcph, fstcph, tcph_len); - fsiph = (void *)rfsiph; - fstcph = (void *)rfstcph; - - } - } - - size_t ipd_offset; - size_t mid_offset; - - switch (config.fragmentation_strategy) { - case FRAG_STRAT_TCP: - ipd_offset = vrd.sni_offset; - mid_offset = ipd_offset + vrd.sni_len / 2; - - if ((ret = tcp4_frag(raw_payload, raw_payload_len, - mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - - errno = -ret; - perror("tcp4_frag"); - goto fallback; - } - - break; - case FRAG_STRAT_IP: - ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; - mid_offset = ipd_offset + vrd.sni_len / 2; - mid_offset += 8 - mid_offset % 8; - - if ((ret = ip4_frag(raw_payload, raw_payload_len, - mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - - errno = -ret; - perror("ip4_frag"); - goto fallback; - } - - break; - default: - ret = send_raw_socket(raw_payload, raw_payload_len); - if (ret < 0) { - errno = -ret; - perror("raw pack send"); - goto fallback; - } - - goto send_verd; - } - - ret = send_raw_socket(frag2, f2len); - if (ret < 0) { - errno = -ret; - perror("raw frags send: frag2"); - - goto fallback; - } - - if (config.seg2_delay) { - struct dps_t *dpdt = malloc(sizeof(struct dps_t)); - dpdt->pkt = malloc(f1len); - memcpy(dpdt->pkt, frag1, f1len); - dpdt->pktlen = f1len; - dpdt->timer = config.seg2_delay; - pthread_t thr; - pthread_create(&thr, NULL, delay_packet_send, dpdt); - pthread_detach(thr); - } else { - ret = send_raw_socket(frag1, f1len); - - if (ret < 0) { - errno = -ret; - perror("raw frags send: frag1"); - - goto fallback; - } - } - } - - -/* - if (pktb_mangled(pktb)) { - if (config.versose) - printf("Mangled!\n"); - - nfq_nlmsg_verdict_put_pkt( - verdnlh, pktb_data(pktb), pktb_len(pktb)); - } -*/ - -send_verd: - if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); - - goto error; - } - - return MNL_CB_OK; - -fallback: - return fallback_accept_packet(packet.id, qdata); -error: - return MNL_CB_ERROR; +void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { + struct dps_t *dpdt = malloc(sizeof(struct dps_t)); + dpdt->pkt = malloc(data_len); + memcpy(dpdt->pkt, data, data_len); + dpdt->pktlen = data_len; + dpdt->timer = delay_ms; + pthread_t thr; + pthread_create(&thr, NULL, delay_packet_send_fn, dpdt); + pthread_detach(thr); } + + + static int queue_cb(const struct nlmsghdr *nlh, void *data) { + char buf[MNL_SOCKET_BUFFER_SIZE]; struct queue_data *qdata = data; @@ -500,7 +322,26 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { } - return process_packet(packet, *qdata); + struct nlmsghdr *verdnlh; + verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata->queue_num); + + int ret = process_packet(packet.payload, packet.payload_len); + + switch (ret) { + case PKT_DROP: + nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP); + break; + default: + nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT); + break; + } + + if (mnl_socket_sendto(*qdata->_nl, verdnlh, verdnlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + return MNL_CB_ERROR; + } + + return MNL_CB_OK; } #define BUF_SIZE (0xffff + (MNL_SOCKET_BUFFER_SIZE / 2)) @@ -600,6 +441,11 @@ void *init_queue_wrapper(void *qdconf) { return thres; } +struct instance_config_t instance_config = { + .send_raw_packet = send_raw_socket, + .send_delayed_packet = delay_packet_send, +}; + int main(int argc, char *argv[]) { int ret; if ((ret = parse_args(argc, argv)) != 0) { @@ -626,18 +472,34 @@ int main(int argc, char *argv[]) { printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay); } - switch (config.fake_sni_strategy) { - case FKSN_STRAT_TTL: - printf("Fake SNI will be sent before each request, TTL strategy will be used with TTL %d\n", config.fake_sni_ttl); + if (config.fake_sni) { + printf("Fake SNI will be sent before each target client hello\n"); + } else { + printf("Fake SNI is disabled\n"); + } + + if (config.frag_sni_reverse) { + printf("Fragmentation Client Hello will be reversed\n"); + } + + if (config.frag_sni_faked) { + printf("Fooling packets will be sent near the original Client Hello\n"); + } + + if (config.fake_sni_seq_len > 1) { + printf("Faking sequence of length %d will be built as fake sni\n", config.fake_sni_seq_len); + } + + switch (config.faking_strategy) { + case FAKE_STRAT_TTL: + printf("TTL faking strategy will be used with TTL %d\n", config.faking_ttl); break; - case FRAG_STRAT_IP: - printf("Fake SNI will be sent before each request, Ack-Seq strategy will be used\n"); - break; - default: - printf("SNI fragmentation is disabled\n"); + case FAKE_STRAT_ACK_SEQ: + printf("Ack-Seq faking strategy will be used\n"); break; } + if (config.use_gso) { printf("GSO is enabled\n"); }