Enhance bypass strategies
This commit is contained in:
Vadim Vetrov 2024-08-12 00:24:23 +03:00
commit 569cfcf049
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
9 changed files with 889 additions and 497 deletions

View File

@ -45,17 +45,33 @@ If you have troubles with some sites being proxied, you can play with flags. For
Also DNS over HTTPS (DOH) is preferred for additional anonimity. Also DNS over HTTPS (DOH) is preferred for additional anonimity.
## Check it
Here is a command aims to help you deterime whether it works or no:
```
curl -o/dev/null -k --connect-to ::google.com -k -L -H Host:\ mirror.gcr.io https://test.googlevideo.com/v2/cimg/android/blobs/sha256:6fd8bdac3da660bde7bd0b6f2b6a46e1b686afb74b9a4614def32532b73f5eaa
```
It should return bad speed without youtubeUnblock and good with it. With youtubeUnblock the speed should be the same as with next the command:
```
curl -o/dev/null -k --connect-to ::google.com -k -L -H Host:\ mirror.gcr.io https://mirror.gcr.io/v2/cimg/android/blobs/sha256:6fd8bdac3da660bde7bd0b6f2b6a46e1b686afb74b9a4614def32532b73f5eaa
```
## Flags ## Flags
Available flags: Available flags:
- `--queue-num=<number of netfilter queue>` - The number of netfilter queue youtubeUnblock will be linked to. Defaults to 537. - `--queue-num=<number of netfilter queue>` - The number of netfilter queue youtubeUnblock will be linked to. Defaults to 537.
- `--sni-domains=<comma separated domain list>|all` - List of domains you want to be handled by sni. Use this string if you want to change default domains. Defaults to `googlevideo.com,youtube.com,ggpht.com,ytimg.com`. You can pass all if you want for every Client Hello to be handled. - `--sni-domains=<comma separated domain list>|all` - List of domains you want to be handled by sni. Use this string if you want to change default domains. Defaults to `googlevideo.com,youtube.com,ggpht.com,ytimg.com`. You can pass all if you want for every Client Hello to be handled.
- `--fake-sni={0|1}` This flag enables fake-sni which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to Troubleshooting for EPERMS. Defaults to 1.
- `--fake-sni-seq-len=<length>` This flag specifies youtubeUnblock to build a complicated construction of fake client hello packets. length determines how much fakes will be sent. Defaults to 1.
- `--faking-strategy={ack,ttl}` This flag determines the strategy of fake packets invalidation. `ack` specifies that random sequence/acknowledgemend random will be set. This options may be handled by provider which uses conntrack with drop on invalid conntrack state firewall rule enabled. `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured. Defaults to `ack`
- `--faking-ttl=<ttl>` Tunes the time to live of fake sni messages. TTL is specified like that the packet will go through the TSPU and captured by it, but will not reach the destination server. Defaults to 8.
- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by TSPU. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies.
- `--frag-sni-reverse={0|1}` Specifies youtubeUnblock to send Client Hello fragments in the reverse order. Defaults to 1.
- `--frag-sni-faked={0|1}` Specifies youtubeUnblock to send fake packets near Client Hello (fills payload with zeroes). Defaults to 0.
- `--seg2delay=<delay>` - This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet. - `--seg2delay=<delay>` - This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet.
- `--fake-sni={ack,ttl, none}` This flag enables fake-sni which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to FAQ for EPERMS. Note, that this flag is set to `ack` by default. You may disable fake sni by setting it to `none`. Note, that your ISP may have conntrack drop on invalid state enabled, so this flag won't work. Use `ttl` to escape that. - `--silent` - Disables verbose mode.
- `--fake-sni-ttl=<ttl>` Tunes the time to live of fake sni messages. TTL is specified like that the packet will go through the TSPU and captured by it, but will not reach the destination server. Defaults to 8.
- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything. - `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything.
- `--threads=<threads number>` Specifies the amount of threads you want to be running for your program. This defaults to 1 and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance) - `--threads=<threads number>` Specifies the amount of threads you want to be running for your program. This defaults to 1 and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance)
- `--silent` - Disables Google video detected debug logs.
- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by TSPU. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies.
If you are on Chromium you may have to disable kyber (the feature that makes the TLS ClientHello very fat). I've got the problem with it on router, so to escape possibly errors it is better to just disable it: in chrome://flags search for kyber and switch it to disabled state. If you are on Chromium you may have to disable kyber (the feature that makes the TLS ClientHello very fat). I've got the problem with it on router, so to escape possibly errors it is better to just disable it: in chrome://flags search for kyber and switch it to disabled state.

332
args.c Normal file
View File

@ -0,0 +1,332 @@
#include "config.h"
#include "raw_replacements.h"
#include <stdbool.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
struct config_t config = {
.threads = THREADS_NUM,
.frag_sni_reverse = 1,
.frag_sni_faked = 0,
.fragmentation_strategy = FRAGMENTATION_STRATEGY,
.faking_strategy = FAKING_STRATEGY,
.faking_ttl = FAKE_TTL,
.fake_sni = 1,
.fake_sni_seq_len = 1,
#ifdef SEG2_DELAY
.seg2_delay = SEG2_DELAY,
#else
.seg2_delay = 0,
#endif
#ifdef USE_GSO
.use_gso = true,
#else
.use_gso = false,
#endif
#ifdef DEBUG
.verbose = true,
#else
.verbose = false,
#endif
.domains_str = defaul_snistr,
.domains_strlen = sizeof(defaul_snistr),
.queue_start_num = DEFAULT_QUEUE_NUM,
.fake_sni_pkt = fake_sni,
.fake_sni_pkt_sz = sizeof(fake_sni) - 1, // - 1 for null-terminator
};
#define OPT_SNI_DOMAINS 1
#define OPT_FAKE_SNI 2
#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-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}
};
static long parse_numeric_option(const char* value) {
errno = 0;
if (*value == '\0') {
errno = EINVAL;
return 0;
}
char* end;
long result = strtol(value, &end, 10);
if (*end != '\0') {
errno = EINVAL;
return 0;
}
return result;
}
void print_version() {
printf("youtubeUnblock\n");
printf("Bypasses deep packet inspection systems that relies on SNI\n");
printf("\n");
}
void print_usage(const char *argv0) {
print_version();
printf("Usage: %s [ OPTIONS ] \n", argv0);
printf("Options:\n");
printf("\t--queue-num=<number of netfilter queue>\n");
printf("\t--sni-domains=<comma separated domain list>|all\n");
printf("\t--fake-sni={1|0}\n");
printf("\t--fake-sni-seq-len=<length>\n");
printf("\t--faking-ttl=<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=<delay>\n");
printf("\t--threads=<threads number>\n");
printf("\t--silent\n");
printf("\t--no-gso\n");
printf("\n");
}
int parse_args(int argc, char *argv[]) {
int opt;
int optIdx;
long num;
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
switch (opt) {
case 'h':
print_usage(argv[0]);
goto out;
case 'v':
print_version();
goto out;
case OPT_SILENT:
config.verbose = 0;
break;
case OPT_NO_GSO:
config.use_gso = 0;
break;
case OPT_SNI_DOMAINS:
if (!strcmp(optarg, "all")) {
config.all_domains = 1;
}
config.domains_str = optarg;
config.domains_strlen = strlen(config.domains_str);
break;
case OPT_FRAG:
if (strcmp(optarg, "tcp") == 0) {
config.fragmentation_strategy = FRAG_STRAT_TCP;
} else if (strcmp(optarg, "ip") == 0) {
config.fragmentation_strategy = FRAG_STRAT_IP;
} else if (strcmp(optarg, "none") == 0) {
config.fragmentation_strategy = FRAG_STRAT_NONE;
} else {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
break;
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);
if (errno != 0 || num < 0) {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
config.seg2_delay = num;
break;
case OPT_THREADS:
num = parse_numeric_option(optarg);
if (errno != 0 || num < 0 || num > MAX_THREADS) {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
config.threads = num;
break;
case OPT_QUEUE_NUM:
num = parse_numeric_option(optarg);
if (errno != 0 || num < 0) {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
config.queue_start_num = num;
break;
default:
goto error;
}
}
errno = 0;
return 0;
out:
errno = 0;
return 1;
error:
print_usage(argv[0]);
errno = EINVAL;
return -1;
}
void print_welcome() {
switch (config.fragmentation_strategy) {
case FRAG_STRAT_TCP:
printf("Using TCP segmentation\n");
break;
case FRAG_STRAT_IP:
printf("Using IP fragmentation\n");
break;
default:
printf("SNI fragmentation is disabled\n");
break;
}
if (config.seg2_delay) {
printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay);
}
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 FAKE_STRAT_ACK_SEQ:
printf("Ack-Seq faking strategy will be used\n");
break;
}
if (config.use_gso) {
printf("GSO is enabled\n");
}
if (config.all_domains) {
printf("All Client Hello will be targetted by youtubeUnblock!\n");
}
}

11
args.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef ARGS_H
#define ARGS_H
void print_version();
void print_usage(const char *argv0);
int parse_args(int argc, char *argv[]);
/* Prints starting messages */
void print_welcome();
#endif /* ARGS_H */

View File

@ -1,18 +1,40 @@
#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 { struct config_t {
unsigned int queue_start_num; unsigned int queue_start_num;
int rawsocket;
int threads; int threads;
int use_gso; int use_gso;
int fragmentation_strategy; int fragmentation_strategy;
unsigned char fake_sni_ttl; int frag_sni_reverse;
int fake_sni_strategy; int frag_sni_faked;
int faking_strategy;
unsigned char faking_ttl;
int fake_sni;
unsigned int fake_sni_seq_len;
int verbose; int verbose;
/* In milliseconds */
unsigned int seg2_delay; unsigned int seg2_delay;
const char *domains_str; const char *domains_str;
unsigned int domains_strlen; unsigned int domains_strlen;
unsigned int all_domains; unsigned int all_domains;
const char *fake_sni_pkt;
unsigned int fake_sni_pkt_sz;
}; };
extern struct config_t config; extern struct config_t config;
#define MAX_THREADS 16 #define MAX_THREADS 16
@ -43,23 +65,17 @@ extern struct config_t config;
#define SEG2_DELAY 100 #define SEG2_DELAY 100
#endif #endif
#define FAKE_SNI_TTL 8 #define FAKE_TTL 8
// No fake SNI // Will invalidate fake packets by out-of-ack_seq out-of-seq request
#define FKSN_STRAT_NONE 0 #define FAKE_STRAT_ACK_SEQ 1
// Will invalidate fake client hello by out-of-ack_seq out-of-seq request // Will assume that GGC server is located further than FAKE_TTL
#define FKSN_STRAT_ACK_SEQ 1 // Thus, Fake packet will be eliminated automatically.
// Will assume that GGC server is located further than FAKE_SNI_TTL #define FAKE_STRAT_TTL 2
// Thus, Fake Client Hello will be eliminated automatically.
#define FKSN_STRAT_TTL 2
#ifdef NO_FAKE_SNI #ifndef FAKING_STRATEGY
#define FAKE_SNI_STRATEGY FKSN_STRAT_NONE #define FAKING_STRATEGY FAKE_STRAT_ACK_SEQ
#endif
#ifndef FAKE_SNI_STRATEGY
#define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ
#endif #endif
#if !defined(SILENT) && !defined(KERNEL_SPACE) #if !defined(SILENT) && !defined(KERNEL_SPACE)
@ -72,4 +88,8 @@ extern struct config_t config;
#define DEFAULT_QUEUE_NUM 537 #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"; 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 */

389
mangle.c
View File

@ -1,7 +1,6 @@
#include <stdlib.h>
#define _GNU_SOURCE #define _GNU_SOURCE
#include "mangle.h" #include "mangle.h"
#include "raw_replacements.h"
#include "config.h" #include "config.h"
#ifdef KERNEL_SPACE #ifdef KERNEL_SPACE
@ -23,6 +22,319 @@ typedef uint16_t __u16;
#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) #define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);})
#endif #endif
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 tls_verdict vrd = analyze_tls_data(data, dlen);
if (vrd.target_sni) {
if (config.verbose)
printf("Target SNI detected: %.*s\n", vrd.sni_len, data + vrd.sni_offset);
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) {
post_fake_sni(iph, iph_len, tcph, tcph_len,
config.fake_sni_seq_len);
}
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_ip4_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;
}
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) {
if (config.seg2_delay && ((dvs > 0) ^ config.frag_sni_reverse)) {
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];
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;
}
if (config.frag_sni_reverse)
goto send_frag2;
send_frag1:
ret = send_ip4_frags(frag1, f1len, NULL, 0, 0);
if (ret < 0) {
return ret;
}
if (config.frag_sni_reverse)
goto out;
send_frag2:
dvs += poses[0];
ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs);
if (ret < 0) {
return ret;
}
if (config.frag_sni_reverse)
goto send_frag1;
}
out:
return 0;
}
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) ^ config.frag_sni_reverse)) {
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;
}
if (config.frag_sni_reverse)
goto send_frag2;
send_frag1:
{
ret = send_tcp4_frags(frag1, f1len, NULL, 0, 0);
if (ret < 0) {
return ret;
}
if (config.frag_sni_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 (config.frag_sni_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 (config.frag_sni_reverse)
goto send_fake;
}
}
out:
return 0;
}
int post_fake_sni(const struct iphdr *iph, unsigned int iph_len,
const struct tcphdr *tcph, unsigned int tcph_len,
unsigned char sequence_len) {
uint8_t rfsiph[60];
uint8_t rfstcph[60];
int ret;
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 < sequence_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);
return ret;
}
ret = instance_config.send_raw_packet(fake_sni, fsn_len);
if (ret < 0) {
lgerror("send fake sni", ret);
return ret;
}
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;
}
return 0;
}
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph) void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph)
{ {
#ifdef KERNEL_SPACE #ifdef KERNEL_SPACE
@ -52,15 +364,22 @@ int ip4_payload_split(__u8 *pkt, __u32 buflen,
struct iphdr **iph, __u32 *iph_len, struct iphdr **iph, __u32 *iph_len,
__u8 **payload, __u32 *plen) { __u8 **payload, __u32 *plen) {
if (pkt == NULL || buflen < sizeof(struct iphdr)) { if (pkt == NULL || buflen < sizeof(struct iphdr)) {
lgerror("ip4_payload_split: pkt|buflen", -EINVAL);
return -EINVAL; return -EINVAL;
} }
struct iphdr *hdr = (struct iphdr *)pkt; 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 hdr_len = hdr->ihl * 4;
__u32 pktlen = ntohs(hdr->tot_len); __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) if (iph)
*iph = hdr; *iph = hdr;
@ -270,6 +589,9 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset,
s2_hdr->tot_len = htons(s2_dlen); s2_hdr->tot_len = htons(s2_dlen);
s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset);
s1_tcph->window = htons(1);
s2_tcph->window = htons(1);
if (config.verbose) if (config.verbose)
printf("Packet split in portion %u %u\n", s1_plen, s2_plen); printf("Packet split in portion %u %u\n", s1_plen, s2_plen);
@ -295,11 +617,11 @@ typedef __u16 uint16_t;
* data Payload data of TCP. * data Payload data of TCP.
* dlen Length of `data`. * dlen Length of `data`.
*/ */
struct verdict analyze_tls_data( struct tls_verdict analyze_tls_data(
const uint8_t *data, const uint8_t *data,
uint32_t dlen) uint32_t dlen)
{ {
struct verdict vrd = {0}; struct tls_verdict vrd = {0};
size_t i = 0; size_t i = 0;
const uint8_t *data_end = data + dlen; const uint8_t *data_end = data + dlen;
@ -449,41 +771,58 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
return -EINVAL; return -EINVAL;
int ip_len = iph->ihl * 4; int ip_len = iph->ihl * 4;
size_t data_len = sizeof(fake_sni); int tcph_len = tcph->doff * 4;
size_t dlen = data_len + ip_len; const char *data = config.fake_sni_pkt;
size_t data_len = config.fake_sni_pkt_sz;
size_t dlen = ip_len + tcph_len + data_len;
if (*buflen < dlen) if (*buflen < dlen)
return -ENOMEM; return -ENOMEM;
*buflen = dlen;
memcpy(buf, iph, ip_len); memcpy(buf, iph, ip_len);
memcpy(buf + ip_len, fake_sni, data_len); memcpy(buf + ip_len, tcph, tcph_len);
memcpy(buf + ip_len + tcph_len, data, data_len);
struct iphdr *niph = (struct iphdr *)buf; struct iphdr *niph = (struct iphdr *)buf;
struct tcphdr *ntcph = (struct tcphdr *)(buf + ip_len);
niph->protocol = IPPROTO_TCP; niph->protocol = IPPROTO_TCP;
niph->tot_len = htons(dlen); niph->tot_len = htons(dlen);
int ret = 0; fail4_packet(buf, *buflen);
struct tcphdr *ntcph = (struct tcphdr *)(buf + ip_len);
#ifdef KERNEL_SPACE *buflen = dlen;
ntcph->dest = tcph->dest; return 0;
ntcph->source = tcph->source; }
#else
ntcph->th_dport = tcph->th_dport;
ntcph->th_sport = tcph->th_sport;
if (config.fake_sni_strategy == FKSN_STRAT_TTL) { int fail4_packet(uint8_t *payload, uint32_t plen) {
ntcph->seq = tcph->seq; struct iphdr *iph;
ntcph->ack_seq = tcph->ack_seq; uint32_t iph_len;
niph->ttl = config.fake_sni_ttl; 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;
} }
#endif 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(niph); ip4_set_checksum(iph);
tcp4_set_checksum(ntcph, niph); tcp4_set_checksum(tcph, iph);
return 0; return 0;
} }

View File

@ -31,28 +31,50 @@ typedef __u32 uint32_t;
#include <netinet/tcp.h> #include <netinet/tcp.h>
#endif #endif
struct verdict { /**
* Result of analyze_tls_data function
*/
struct tls_verdict {
int target_sni; /* google video hello packet */ int target_sni; /* google video hello packet */
int sni_offset; /* offset from start of tcp _payload_ */ int sni_offset; /* offset from start of tcp _payload_ */
int sni_len; int sni_len;
}; };
struct verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); /**
* 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);
/**
* Splits the packet to two IP fragments on position payload_offset.
* payload_offset indicates the position relatively to start of IP payload
* (start of transport header)
*/
int ip4_frag(const uint8_t *pkt, uint32_t pktlen, int ip4_frag(const uint8_t *pkt, uint32_t pktlen,
uint32_t payload_offset, uint32_t payload_offset,
uint8_t *frag1, uint32_t *f1len, uint8_t *frag1, uint32_t *f1len,
uint8_t *frag2, uint32_t *f2len); uint8_t *frag2, uint32_t *f2len);
/**
* Splits the packet to two TCP segments on position payload_offset
* payload_offset indicates the position relatively to start of TCP payload.
*/
int tcp4_frag(const uint8_t *pkt, uint32_t pktlen, int tcp4_frag(const uint8_t *pkt, uint32_t pktlen,
uint32_t payload_offset, uint32_t payload_offset,
uint8_t *seg1, uint32_t *s1len, uint8_t *seg1, uint32_t *s1len,
uint8_t *seg2, uint32_t *s2len); uint8_t *seg2, uint32_t *s2len);
/**
* Splits the raw packet payload to ip header and ip payload.
*/
int ip4_payload_split(uint8_t *pkt, uint32_t buflen, int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
struct iphdr **iph, uint32_t *iph_len, struct iphdr **iph, uint32_t *iph_len,
uint8_t **payload, uint32_t *plen); uint8_t **payload, uint32_t *plen);
/**
* Splits the raw packet payload to ip header, tcp header and tcp payload.
*/
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
struct iphdr **iph, uint32_t *iph_len, struct iphdr **iph, uint32_t *iph_len,
struct tcphdr **tcph, uint32_t *tcph_len, struct tcphdr **tcph, uint32_t *tcph_len,
@ -61,6 +83,49 @@ int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph);
void ip4_set_checksum(struct iphdr *iph); void ip4_set_checksum(struct iphdr *iph);
/**
* Generates fake client hello message
*/
int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
uint8_t *buf, uint32_t *buflen); 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 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);
/**
* Sends fake client hello.
*/
int post_fake_sni(const struct iphdr *iph, unsigned int iph_len,
const struct tcphdr *tcph, unsigned int tcph_len,
unsigned char sequence_len);
/**
* Splits packet by poses and posts.
* Poses are relative to start of TCP payload.
* dvs used internally and should be zero.
*/
int send_tcp4_frags(
const uint8_t *packet, uint32_t pktlen,
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
/**
* Splits packet by poses and posts.
* Poses are relative to start of TCP payload.
* dvs used internally and should be zero.
*/
int send_ip4_frags(
const uint8_t *packet, uint32_t pktlen,
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
#endif /* YU_MANGLE_H */ #endif /* YU_MANGLE_H */

View File

@ -3,7 +3,6 @@
#define FAKE_SNI_MAXLEN 1500 #define FAKE_SNI_MAXLEN 1500
static const char fake_sni[] = "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\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[] = "\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";
#endif /*RAW_REPLACEMENTS_H*/ #endif /*RAW_REPLACEMENTS_H*/

View File

@ -22,7 +22,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
APP:=$(BUILD_DIR)/youtubeUnblock APP:=$(BUILD_DIR)/youtubeUnblock
SRCS := youtubeUnblock.c mangle.c SRCS := youtubeUnblock.c mangle.c args.c
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a

View File

@ -7,10 +7,10 @@
#error "The build aims to the kernel, not userspace" #error "The build aims to the kernel, not userspace"
#endif #endif
#include <libnetfilter_queue/linux_nfnetlink_queue.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <libmnl/libmnl.h> #include <libmnl/libmnl.h>
#include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
@ -26,216 +26,12 @@
#include <pthread.h> #include <pthread.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h>
#include <getopt.h>
#include "config.h" #include "config.h"
#include "mangle.h" #include "mangle.h"
#include "args.h"
pthread_mutex_t rawsocket_lock; pthread_mutex_t rawsocket_lock;
int rawsocket = -2;
struct config_t config = {
.rawsocket = -2,
.threads = THREADS_NUM,
.fragmentation_strategy = FRAGMENTATION_STRATEGY,
.fake_sni_strategy = FAKE_SNI_STRATEGY,
.fake_sni_ttl = FAKE_SNI_TTL,
#ifdef SEG2_DELAY
.seg2_delay = SEG2_DELAY,
#else
.seg2_delay = 0,
#endif
#ifdef USE_GSO
.use_gso = true,
#else
.use_gso = false,
#endif
#ifdef DEBUG
.verbose = true,
#else
.verbose = false,
#endif
.domains_str = defaul_snistr,
.domains_strlen = sizeof(defaul_snistr),
.queue_start_num = DEFAULT_QUEUE_NUM,
};
static long parse_numeric_option(const char* value) {
errno = 0;
if (*value == '\0') {
errno = EINVAL;
return 0;
}
char* end;
long result = strtol(value, &end, 10);
if (*end != '\0') {
errno = EINVAL;
return 0;
}
return result;
}
static void print_version() {
printf("youtubeUnblock\n");
printf("Bypasses youtube detection systems that relies on SNI\n");
}
static void print_usage(const char *argv0) {
print_version();
printf("Usage: %s [ OPTIONS ] \n", argv0);
printf("Options:\n");
printf("\t--queue-num=<number of netfilter queue>\n");
printf("\t--sni-domains=<comma separated domain list>|all\n");
printf("\t--fake-sni={ack,ttl,none}\n");
printf("\t--fake-sni-ttl=<ttl>\n");
printf("\t--frag={tcp,ip,none}\n");
printf("\t--seg2delay=<delay>\n");
printf("\t--threads=<threads number>\n");
printf("\t--silent\n");
printf("\t--no-gso\n");
printf("\n");
}
#define OPT_SNI_DOMAINS 1
#define OPT_FAKE_SNI 2
#define OPT_FAKE_SNI_TTL 3
#define OPT_FRAG 4
#define OPT_SEG2DELAY 5
#define OPT_THREADS 6
#define OPT_SILENT 7
#define OPT_NO_GSO 8
#define OPT_QUEUE_NUM 9
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},
{0,0,0,0}
};
static int parse_args(int argc, char *argv[]) {
int opt;
int optIdx;
long num;
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
switch (opt) {
case 'h':
print_usage(argv[0]);
goto out;
case 'v':
print_version();
goto out;
case OPT_SILENT:
config.verbose = 0;
break;
case OPT_NO_GSO:
config.use_gso = 0;
break;
case OPT_SNI_DOMAINS:
if (strcmp(optarg, "all")) {
config.all_domains = 1;
}
config.domains_str = optarg;
config.domains_strlen = strlen(config.domains_str);
break;
case OPT_FRAG:
if (strcmp(optarg, "tcp") == 0) {
config.fragmentation_strategy = FRAG_STRAT_TCP;
} else if (strcmp(optarg, "ip") == 0) {
config.fragmentation_strategy = FRAG_STRAT_IP;
} else if (strcmp(optarg, "none") == 0) {
config.fragmentation_strategy = FRAG_STRAT_NONE;
} else {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
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;
} else {
errno = EINVAL;
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
break;
case OPT_SEG2DELAY:
num = parse_numeric_option(optarg);
if (errno != 0 || num < 0) {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
config.seg2_delay = num;
break;
case OPT_THREADS:
num = parse_numeric_option(optarg);
if (errno != 0 || num < 0 || num > MAX_THREADS) {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
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) {
printf("Invalid option %s\n", long_opt[optIdx].name);
goto error;
}
config.queue_start_num = num;
break;
default:
goto error;
}
}
errno = 0;
return 0;
out:
errno = 0;
return 1;
error:
print_usage(argv[0]);
errno = EINVAL;
return -1;
}
static int open_socket(struct mnl_socket **_nl) { static int open_socket(struct mnl_socket **_nl) {
struct mnl_socket *nl = NULL; struct mnl_socket *nl = NULL;
@ -272,20 +68,20 @@ static int close_socket(struct mnl_socket **_nl) {
} }
static int open_raw_socket(void) { static int open_raw_socket(void) {
if (config.rawsocket != -2) { if (rawsocket != -2) {
errno = EALREADY; errno = EALREADY;
perror("Raw socket is already opened"); perror("Raw socket is already opened");
return -1; return -1;
} }
config.rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (config.rawsocket == -1) { if (rawsocket == -1) {
perror("Unable to create raw socket"); perror("Unable to create raw socket");
return -1; return -1;
} }
int mark = RAWSOCKET_MARK; 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); fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark);
return -1; return -1;
@ -294,24 +90,24 @@ static int open_raw_socket(void) {
int mst = pthread_mutex_init(&rawsocket_lock, NULL); int mst = pthread_mutex_init(&rawsocket_lock, NULL);
if (mst) { if (mst) {
fprintf(stderr, "Mutex err: %d\n", mst); fprintf(stderr, "Mutex err: %d\n", mst);
close(config.rawsocket); close(rawsocket);
errno = mst; errno = mst;
return -1; return -1;
} }
return config.rawsocket; return rawsocket;
} }
static int close_raw_socket(void) { static int close_raw_socket(void) {
if (config.rawsocket < 0) { if (rawsocket < 0) {
errno = EALREADY; errno = EALREADY;
perror("Raw socket is not set"); perror("Raw socket is not set");
return -1; return -1;
} }
if (close(config.rawsocket)) { if (close(rawsocket)) {
perror("Unable to close raw socket"); perror("Unable to close raw socket");
pthread_mutex_destroy(&rawsocket_lock); pthread_mutex_destroy(&rawsocket_lock);
return -1; return -1;
@ -319,7 +115,7 @@ static int close_raw_socket(void) {
pthread_mutex_destroy(&rawsocket_lock); pthread_mutex_destroy(&rawsocket_lock);
config.rawsocket = -2; rawsocket = -2;
return 0; return 0;
} }
@ -396,7 +192,7 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
pthread_mutex_lock(&rawsocket_lock); pthread_mutex_lock(&rawsocket_lock);
int sent = sendto(config.rawsocket, int sent = sendto(rawsocket,
pkt, pktlen, 0, pkt, pktlen, 0,
(struct sockaddr *)&daddr, sizeof(daddr)); (struct sockaddr *)&daddr, sizeof(daddr));
@ -408,6 +204,8 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
return sent; return sent;
} }
struct packet_data { struct packet_data {
uint32_t id; uint32_t id;
uint16_t hw_proto; uint16_t hw_proto;
@ -448,7 +246,7 @@ struct dps_t {
uint32_t timer; uint32_t timer;
}; };
// Note that the thread will automatically release dps_t and pkt_buff // 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; struct dps_t *dpdt = data;
uint8_t *pkt = dpdt->pkt; uint8_t *pkt = dpdt->pkt;
@ -465,206 +263,23 @@ void *delay_packet_send(void *data) {
free(dpdt); free(dpdt);
return NULL; 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 void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
printf("packet received (id=%u hw=0x%04x hook=%u, payload len %u)\n", struct dps_t *dpdt = malloc(sizeof(struct dps_t));
packet.id, packet.hw_proto, packet.hook, packet.payload_len); dpdt->pkt = malloc(data_len);
#endif memcpy(dpdt->pkt, data, data_len);
dpdt->pktlen = data_len;
if (packet.hw_proto != ETH_P_IP) { dpdt->timer = delay_ms;
return fallback_accept_packet(packet.id, qdata); pthread_t thr;
} pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
pthread_detach(thr);
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;
} }
static int queue_cb(const struct nlmsghdr *nlh, void *data) { static int queue_cb(const struct nlmsghdr *nlh, void *data) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct queue_data *qdata = data; struct queue_data *qdata = data;
@ -705,7 +320,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)) #define BUF_SIZE (0xffff + (MNL_SOCKET_BUFFER_SIZE / 2))
@ -805,6 +439,11 @@ void *init_queue_wrapper(void *qdconf) {
return thres; 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 main(int argc, char *argv[]) {
int ret; int ret;
if ((ret = parse_args(argc, argv)) != 0) { if ((ret = parse_args(argc, argv)) != 0) {
@ -815,37 +454,8 @@ int main(int argc, char *argv[]) {
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
switch (config.fragmentation_strategy) { print_version();
case FRAG_STRAT_TCP: print_welcome();
printf("Using TCP segmentation\n");
break;
case FRAG_STRAT_IP:
printf("Using IP fragmentation\n");
break;
default:
printf("SNI fragmentation is disabled\n");
break;
}
if (config.seg2_delay) {
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);
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");
break;
}
if (config.use_gso) {
printf("GSO is enabled\n");
}
if (open_raw_socket() < 0) { if (open_raw_socket() < 0) {
perror("Unable to open raw socket"); perror("Unable to open raw socket");