mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2024-12-22 06:15:31 +00:00
Add multiple fooling options
This commit is contained in:
parent
d29177d783
commit
2e96aa150e
127
args.c
127
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=<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--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");
|
||||
@ -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) {
|
||||
|
52
config.h
52
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 */
|
||||
|
333
mangle.c
333
mangle.c
@ -1,5 +1,5 @@
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
}
|
||||
|
11
mangle.h
11
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 */
|
||||
|
@ -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
|
||||
|
296
youtubeUnblock.c
296
youtubeUnblock.c
@ -7,10 +7,10 @@
|
||||
#error "The build aims to the kernel, not userspace"
|
||||
#endif
|
||||
|
||||
#include <libnetfilter_queue/linux_nfnetlink_queue.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <linux/netfilter/nfnetlink_queue.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <libnetfilter_queue/libnetfilter_queue.h>
|
||||
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
||||
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user