Add multiple fooling options

This commit is contained in:
Vadim Vetrov 2024-08-11 02:10:18 +03:00
parent d29177d783
commit 2e96aa150e
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
6 changed files with 548 additions and 273 deletions

127
args.c
View File

@ -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) {

View File

@ -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
View File

@ -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;
}

View File

@ -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 */

View File

@ -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

View File

@ -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");
}