diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index bcc5d60..2b088e0 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -1,9 +1,9 @@ #define _GNU_SOURCE +#include "darkmagic.h" #include #include #include #include -#include "darkmagic.h" uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment) { diff --git a/nfq/desync.c b/nfq/desync.c new file mode 100644 index 0000000..c95bc47 --- /dev/null +++ b/nfq/desync.c @@ -0,0 +1,278 @@ +#define _GNU_SOURCE + +#include "desync.h" +#include "protocol.h" +#include "params.h" +#include "helpers.h" +#include "hostlist.h" + +#include + + +static const char fake_http_request[] = "GET / HTTP/1.1\r\nHost: www.w3.org\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n\r\n"; +static const uint8_t fake_https_request[] = { + 0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x9a, 0x8f, 0xa7, 0x6a, 0x5d, + 0x57, 0xf3, 0x62, 0x19, 0xbe, 0x46, 0x82, 0x45, 0xe2, 0x59, 0x5c, 0xb4, 0x48, 0x31, 0x12, 0x15, + 0x14, 0x79, 0x2c, 0xaa, 0xcd, 0xea, 0xda, 0xf0, 0xe1, 0xfd, 0xbb, 0x20, 0xf4, 0x83, 0x2a, 0x94, + 0xf1, 0x48, 0x3b, 0x9d, 0xb6, 0x74, 0xba, 0x3c, 0x81, 0x63, 0xbc, 0x18, 0xcc, 0x14, 0x45, 0x57, + 0x6c, 0x80, 0xf9, 0x25, 0xcf, 0x9c, 0x86, 0x60, 0x50, 0x31, 0x2e, 0xe9, 0x00, 0x22, 0x13, 0x01, + 0x13, 0x03, 0x13, 0x02, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30, + 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35, + 0x01, 0x00, 0x01, 0x91, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x77, 0x77, 0x77, + 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, + 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, + 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, + 0x20, 0xb0, 0xe4, 0xda, 0x34, 0xb4, 0x29, 0x8d, 0xd3, 0x5c, 0x70, 0xd3, 0xbe, 0xe8, 0xa7, 0x2a, + 0x6b, 0xe4, 0x11, 0x19, 0x8b, 0x18, 0x9d, 0x83, 0x9a, 0x49, 0x7c, 0x83, 0x7f, 0xa9, 0x03, 0x8c, + 0x3c, 0x00, 0x17, 0x00, 0x41, 0x04, 0x4c, 0x04, 0xa4, 0x71, 0x4c, 0x49, 0x75, 0x55, 0xd1, 0x18, + 0x1e, 0x22, 0x62, 0x19, 0x53, 0x00, 0xde, 0x74, 0x2f, 0xb3, 0xde, 0x13, 0x54, 0xe6, 0x78, 0x07, + 0x94, 0x55, 0x0e, 0xb2, 0x6c, 0xb0, 0x03, 0xee, 0x79, 0xa9, 0x96, 0x1e, 0x0e, 0x98, 0x17, 0x78, + 0x24, 0x44, 0x0c, 0x88, 0x80, 0x06, 0x8b, 0xd4, 0x80, 0xbf, 0x67, 0x7c, 0x37, 0x6a, 0x5b, 0x46, + 0x4c, 0xa7, 0x98, 0x6f, 0xb9, 0x22, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, + 0x02, 0x03, 0x01, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, + 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, + 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00, 0x15, 0x00, 0x96, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t zeropkt[DPI_DESYNC_MAX_FAKE_LEN]; + +void desync_init() +{ + memset(zeropkt, 0, sizeof(zeropkt)); +} + + +// result : true - drop original packet, false = dont drop +bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *iphdr, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, uint8_t *data_payload, size_t len_payload) +{ + if (!!iphdr == !!ip6hdr) return false; // one and only one must be present + + if (!tcphdr->syn && len_payload) + { + struct sockaddr_storage src, dst; + const uint8_t *fake; + size_t fake_size; + char host[256]; + bool bHaveHost=false; + + if (IsHttp(data_payload,len_payload)) + { + DLOG("packet contains HTTP request\n") + fake = (uint8_t*)fake_http_request; + fake_size = sizeof(fake_http_request); + if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host)); + } + else if (IsTLSClientHello(data_payload,len_payload)) + { + DLOG("packet contains TLS ClientHello\n") + fake = (uint8_t*)fake_https_request; + fake_size = sizeof(fake_https_request); + if (params.hostlist || params.desync_skip_nosni || params.debug) + { + bHaveHost=TLSHelloExtractHost(data_payload,len_payload,host,sizeof(host)); + if (params.desync_skip_nosni && !bHaveHost) + { + DLOG("not applying dpi-desync to TLS ClientHello without hostname in the SNI\n") + return false; + } + } + + } + else + { + if (!params.desync_any_proto) return false; + DLOG("applying dpi-desync to unknown protocol\n") + fake = zeropkt; + fake_size = 256; + } + + if (bHaveHost) + { + DLOG("hostname: %s\n",host) + if (params.hostlist && !SearchHostList(params.hostlist,host,params.debug)) + { + DLOG("not applying dpi-desync to this request\n") + return false; + } + } + + extract_endpoints(iphdr, ip6hdr, tcphdr, &src, &dst); + if (params.debug) + { + printf("dpi desync src="); + print_sockaddr((struct sockaddr *)&src); + printf(" dst="); + print_sockaddr((struct sockaddr *)&dst); + printf("\n"); + } + + uint8_t newdata[DPI_DESYNC_MAX_FAKE_LEN+100]; + size_t newlen = sizeof(newdata); + uint8_t ttl_orig = iphdr ? iphdr->ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; + uint8_t ttl_fake = params.desync_ttl ? params.desync_ttl : ttl_orig; + uint8_t flags_orig = *((uint8_t*)tcphdr+13); + uint32_t *timestamps = tcp_find_timestamps(tcphdr); + + switch(params.desync_mode) + { + case DESYNC_DISORDER: + case DESYNC_DISORDER2: + { + size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1; + uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100]; + size_t fakeseg_len; + + if (split_posseq,split_pos), tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_orig,TCP_FOOL_NONE, + data_payload+split_pos, len_payload-split_pos, newdata, &newlen) || + !rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + { + return false; + } + } + + + if (params.desync_mode==DESYNC_DISORDER) + { + DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) + fakeseg_len = sizeof(fakeseg); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_fake,params.desync_tcp_fooling_mode, + zeropkt, split_pos, fakeseg, &fakeseg_len) || + !rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) + { + return false; + } + } + + + DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) + newlen = sizeof(newdata); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_orig,TCP_FOOL_NONE, + data_payload, split_pos, newdata, &newlen) || + !rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + { + return false; + } + + if (params.desync_mode==DESYNC_DISORDER) + { + DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) + return false; + } + + return true; + } + break; + case DESYNC_SPLIT: + case DESYNC_SPLIT2: + { + size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1; + uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100]; + size_t fakeseg_len; + + if (params.desync_mode==DESYNC_SPLIT) + { + DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) + fakeseg_len = sizeof(fakeseg); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_fake,params.desync_tcp_fooling_mode, + zeropkt, split_pos, fakeseg, &fakeseg_len) || + !rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) + { + return false; + } + } + + DLOG("sending 1st tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) + newlen = sizeof(newdata); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_orig,TCP_FOOL_NONE, + data_payload, split_pos, newdata, &newlen) || + !rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + { + return false; + } + + if (params.desync_mode==DESYNC_SPLIT) + { + DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) + return false; + } + + if (split_posseq,split_pos), tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_orig,TCP_FOOL_NONE, + data_payload+split_pos, len_payload-split_pos, newdata, &newlen) || + !rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + { + return false; + } + } + + return true; + } + break; + + case DESYNC_FAKE: + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_fake,params.desync_tcp_fooling_mode, + fake, fake_size, newdata, &newlen)) + { + return false; + } + break; + case DESYNC_RST: + case DESYNC_RSTACK: + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (params.desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, + ttl_fake,params.desync_tcp_fooling_mode, + NULL, 0, newdata, &newlen)) + { + return false; + } + break; + + default: + return false; + } + + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + return false; + + if (params.desync_retrans) + DLOG("dropping packet to force retransmission. len=%zu len_payload=%zu\n", len_pkt, len_payload) + else + { + DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload) + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt)) + return false; + } + return true; + } + + return false; +} diff --git a/nfq/desync.h b/nfq/desync.h new file mode 100644 index 0000000..e000259 --- /dev/null +++ b/nfq/desync.h @@ -0,0 +1,27 @@ +#pragma once + +#include "darkmagic.h" + +#include +#include +#include +#include +#include +#include + +#define DPI_DESYNC_FWMARK_DEFAULT 0x40000000 +#define DPI_DESYNC_MAX_FAKE_LEN 1500 + +enum dpi_desync_mode { + DESYNC_NONE=0, + DESYNC_FAKE, + DESYNC_RST, + DESYNC_RSTACK, + DESYNC_DISORDER, + DESYNC_DISORDER2, + DESYNC_SPLIT, + DESYNC_SPLIT2 +}; + +void desync_init(); +bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *iphdr, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, uint8_t *data_payload, size_t len_payload); diff --git a/nfq/gzip.c b/nfq/gzip.c index d536b23..7857728 100644 --- a/nfq/gzip.c +++ b/nfq/gzip.c @@ -1,82 +1,82 @@ +#include "gzip.h" #include #include #include -#include "gzip.h" #define ZCHUNK 16384 #define BUFMIN 128 #define BUFCHUNK (1024*128) -int z_readfile(FILE *F,char **buf,size_t *size) +int z_readfile(FILE *F, char **buf, size_t *size) { - z_stream zs; - int r; - unsigned char in[ZCHUNK]; - size_t bufsize; - void *newbuf; + z_stream zs; + int r; + unsigned char in[ZCHUNK]; + size_t bufsize; + void *newbuf; - memset(&zs,0,sizeof(zs)); + memset(&zs, 0, sizeof(zs)); - *buf = NULL; - bufsize=*size=0; + *buf = NULL; + bufsize = *size = 0; - r=inflateInit2(&zs,47); - if (r != Z_OK) return r; + r = inflateInit2(&zs, 47); + if (r != Z_OK) return r; - do - { - zs.avail_in = fread(in, 1, sizeof(in), F); - if (ferror(F)) - { - r = Z_ERRNO; - goto zerr; - } - if (!zs.avail_in) break; - zs.next_in = in; - do - { - if ((bufsize-*size) +#include + +const uint8_t *find_bin_const(const uint8_t *data, size_t len, const void *blk, size_t blk_len) +{ + while (len >= blk_len) + { + if (!memcmp(data, blk, blk_len)) + return data; + data++; + len--; + } + return NULL; +} +uint8_t *find_bin(uint8_t *data, size_t len, const void *blk, size_t blk_len) +{ + while (len >= blk_len) + { + if (!memcmp(data, blk, blk_len)) + return data; + data++; + len--; + } + return NULL; +} + +void print_sockaddr(const struct sockaddr *sa) +{ + char str[64]; + switch (sa->sa_family) + { + case AF_INET: + if (inet_ntop(sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, str, sizeof(str))) + printf("%s:%d", str, ntohs(((struct sockaddr_in*)sa)->sin_port)); + break; + case AF_INET6: + if (inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, sizeof(str))) + printf("%s:%d", str, ntohs(((struct sockaddr_in6*)sa)->sin6_port)); + break; + default: + printf("UNKNOWN_FAMILY_%d", sa->sa_family); + } +} diff --git a/nfq/helpers.h b/nfq/helpers.h new file mode 100644 index 0000000..0dd62e3 --- /dev/null +++ b/nfq/helpers.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include + +const uint8_t *find_bin_const(const uint8_t *data, size_t len, const void *blk, size_t blk_len); +uint8_t *find_bin(uint8_t *data, size_t len, const void *blk, size_t blk_len); +void print_sockaddr(const struct sockaddr *sa); diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 737b625..953f17d 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -1,4 +1,12 @@ #define _GNU_SOURCE + +#include "sec.h" +#include "desync.h" +#include "helpers.h" +#include "checksum.h" +#include "params.h" +#include "hostlist.h" + #include #include #include @@ -13,93 +21,12 @@ #include #include #include -#include "darkmagic.h" -#include "hostlist.h" -#include "sec.h" #define NF_DROP 0 #define NF_ACCEPT 1 -#define Q_RCVBUF (128*1024) // in bytes -#define Q_MAXLEN 1024 // in packets -#define DPI_DESYNC_FWMARK_DEFAULT 0x40000000 - - -static const char fake_http_request[] = "GET / HTTP/1.1\r\nHost: www.w3.org\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\r\n" - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" - "Accept-Encoding: gzip, deflate\r\n\r\n"; -static const uint8_t fake_https_request[] = { - 0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x9a, 0x8f, 0xa7, 0x6a, 0x5d, - 0x57, 0xf3, 0x62, 0x19, 0xbe, 0x46, 0x82, 0x45, 0xe2, 0x59, 0x5c, 0xb4, 0x48, 0x31, 0x12, 0x15, - 0x14, 0x79, 0x2c, 0xaa, 0xcd, 0xea, 0xda, 0xf0, 0xe1, 0xfd, 0xbb, 0x20, 0xf4, 0x83, 0x2a, 0x94, - 0xf1, 0x48, 0x3b, 0x9d, 0xb6, 0x74, 0xba, 0x3c, 0x81, 0x63, 0xbc, 0x18, 0xcc, 0x14, 0x45, 0x57, - 0x6c, 0x80, 0xf9, 0x25, 0xcf, 0x9c, 0x86, 0x60, 0x50, 0x31, 0x2e, 0xe9, 0x00, 0x22, 0x13, 0x01, - 0x13, 0x03, 0x13, 0x02, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30, - 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35, - 0x01, 0x00, 0x01, 0x91, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x77, 0x77, 0x77, - 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, - 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, - 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, - 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, - 0x20, 0xb0, 0xe4, 0xda, 0x34, 0xb4, 0x29, 0x8d, 0xd3, 0x5c, 0x70, 0xd3, 0xbe, 0xe8, 0xa7, 0x2a, - 0x6b, 0xe4, 0x11, 0x19, 0x8b, 0x18, 0x9d, 0x83, 0x9a, 0x49, 0x7c, 0x83, 0x7f, 0xa9, 0x03, 0x8c, - 0x3c, 0x00, 0x17, 0x00, 0x41, 0x04, 0x4c, 0x04, 0xa4, 0x71, 0x4c, 0x49, 0x75, 0x55, 0xd1, 0x18, - 0x1e, 0x22, 0x62, 0x19, 0x53, 0x00, 0xde, 0x74, 0x2f, 0xb3, 0xde, 0x13, 0x54, 0xe6, 0x78, 0x07, - 0x94, 0x55, 0x0e, 0xb2, 0x6c, 0xb0, 0x03, 0xee, 0x79, 0xa9, 0x96, 0x1e, 0x0e, 0x98, 0x17, 0x78, - 0x24, 0x44, 0x0c, 0x88, 0x80, 0x06, 0x8b, 0xd4, 0x80, 0xbf, 0x67, 0x7c, 0x37, 0x6a, 0x5b, 0x46, - 0x4c, 0xa7, 0x98, 0x6f, 0xb9, 0x22, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, - 0x02, 0x03, 0x01, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, - 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, - 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00, 0x15, 0x00, 0x96, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00 -}; - - -static uint8_t zeropkt[1500]; - - -enum dpi_desync_mode { - DESYNC_NONE=0, - DESYNC_FAKE, - DESYNC_RST, - DESYNC_RSTACK, - DESYNC_DISORDER, - DESYNC_DISORDER2 -}; - - -struct params_s -{ - bool debug; - int wsize; - int qnum; - bool hostcase, hostnospace; - char hostspell[4]; - enum dpi_desync_mode desync_mode; - bool desync_retrans,desync_skip_nosni,desync_any_proto; - int desync_split_pos; - uint8_t desync_ttl; - uint8_t desync_tcp_fooling_mode; - uint32_t desync_fwmark; - char hostfile[256]; - strpool *hostlist; -}; - -static struct params_s params; - -#define DLOG(format, ...) {if (params.debug) printf(format, ##__VA_ARGS__);} +struct params_s params; static bool bHup = false; @@ -128,48 +55,6 @@ static void dohup() } -static const uint8_t *find_bin_const(const uint8_t *data, size_t len, const void *blk, size_t blk_len) -{ - while (len >= blk_len) - { - if (!memcmp(data, blk, blk_len)) - return data; - data++; - len--; - } - return NULL; -} -static uint8_t *find_bin(uint8_t *data, size_t len, const void *blk, size_t blk_len) -{ - while (len >= blk_len) - { - if (!memcmp(data, blk, blk_len)) - return data; - data++; - len--; - } - return NULL; -} - - -static void print_sockaddr(const struct sockaddr *sa) -{ - char str[64]; - switch (sa->sa_family) - { - case AF_INET: - if (inet_ntop(sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, str, sizeof(str))) - printf("%s:%d", str, ntohs(((struct sockaddr_in*)sa)->sin_port)); - break; - case AF_INET6: - if (inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, sizeof(str))) - printf("%s:%d", str, ntohs(((struct sockaddr_in6*)sa)->sin6_port)); - break; - default: - printf("UNKNOWN_FAMILY_%d", sa->sa_family); - } -} - static bool proto_check_ipv4(uint8_t *data, size_t len) { @@ -266,163 +151,12 @@ static inline bool tcp_ack_segment(const struct tcphdr *tcphdr) static void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize) { uint16_t winsize_old; - /* - uint8_t scale_factor=1; - int optlen = (tcp->doff << 2); - uint8_t *opt = (uint8_t*)(tcp+1); - - optlen = optlen>sizeof(struct tcphdr) ? optlen-sizeof(struct tcphdr) : 0; - printf("optslen=%d\n",optlen); - while (optlen) - { - switch(*opt) - { - case 0: break; // end of option list; - case 1: opt++; optlen--; break; // noop - default: - if (optlen<2 || optlen=3) - { - scale_factor=opt[2]; - printf("Found scale factor %u\n",opt[2]); - //opt[2]=0; - } - optlen-=opt[1]; - opt+=opt[1]; - } - } - */ winsize_old = htons(tcp->window); // << scale_factor; tcp->window = htons(winsize); DLOG("Window size change %u => %u\n", winsize_old, winsize) } - -static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; -static bool IsHttp(const char *data, size_t len) -{ - const char **method; - size_t method_len; - for (method = http_methods; *method; method++) - { - method_len = strlen(*method); - if (method_len <= len && !memcmp(data, *method, method_len)) - return true; - } - return false; -} -static bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host) -{ - const uint8_t *p, *s, *e=data+len; - - p = find_bin_const(data, len, "\nHost:", 6); - if (!p) return false; - p+=6; - while(pp) - { - size_t slen = s-p; - if (host && len_host) - { - if (slen>=len_host) slen=len_host-1; - for(size_t i=0;i=6 && data[0]==0x16 && data[1]==0x03 && data[2]==0x01 && data[5]==0x01 && (ntohs(*(uint16_t*)(data+3))+5)<=len; -} -static bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext) -{ - // +0 - // u8 ContentType: Handshake - // u16 Version: TLS1.0 - // u16 Length - // +5 - // u8 HandshakeType: ClientHello - // u24 Length - // u16 Version - // c[32] random - // u8 SessionIDLength - // - // u16 CipherSuitesLength - // - // u8 CompressionMethodsLength - // - // u16 ExtensionsLength - - size_t l,ll; - - l = 1+2+2+1+3+2+32; - // SessionIDLength - if (len<(l+1)) return false; - ll = data[6]<<16 | data[7]<<8 | data[8]; // HandshakeProtocol length - if (len<(ll+9)) return false; - l += data[l]+1; - // CipherSuitesLength - if (len<(l+2)) return false; - l += ntohs(*(uint16_t*)(data+l))+2; - // CompressionMethodsLength - if (len<(l+1)) return false; - l += data[l]+1; - // ExtensionsLength - if (len<(l+2)) return false; - - data+=l; len-=l; - l=ntohs(*(uint16_t*)data); - data+=2; len-=2; - if (l=4) - { - uint16_t etype=*(uint16_t*)data; - size_t elen=ntohs(*(uint16_t*)(data+2)); - data+=4; l-=4; - if (l=len_host) slen=len_host-1; - for(size_t i=0;isyn && len_payload) - { - struct sockaddr_storage src, dst; - const uint8_t *fake; - size_t fake_size; - char host[256]; - bool bHaveHost=false; - - if (IsHttp(data_payload,len_payload)) - { - DLOG("packet contains HTTP request\n") - fake = (uint8_t*)fake_http_request; - fake_size = sizeof(fake_http_request); - if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host)); - } - else if (IsTLSClientHello(data_payload,len_payload)) - { - DLOG("packet contains TLS ClientHello\n") - fake = (uint8_t*)fake_https_request; - fake_size = sizeof(fake_https_request); - if (params.hostlist || params.desync_skip_nosni || params.debug) - { - bHaveHost=TLSHelloExtractHost(data_payload,len_payload,host,sizeof(host)); - if (params.desync_skip_nosni && !bHaveHost) - { - DLOG("not applying dpi-desync to TLS ClientHello without hostname in the SNI\n") - return false; - } - } - - } - else - { - if (!params.desync_any_proto) return false; - DLOG("applying dpi-desync to unknown protocol\n") - fake = zeropkt; - fake_size = 256; - } - - if (bHaveHost) - { - DLOG("hostname: %s\n",host) - if (params.hostlist && !SearchHostList(params.hostlist,host,params.debug)) - { - DLOG("not applying dpi-desync to this request\n") - return false; - } - } - - extract_endpoints(iphdr, ip6hdr, tcphdr, &src, &dst); - if (params.debug) - { - printf("dpi desync src="); - print_sockaddr((struct sockaddr *)&src); - printf(" dst="); - print_sockaddr((struct sockaddr *)&dst); - printf("\n"); - } - - uint8_t newdata[1600]; - size_t newlen = sizeof(newdata); - uint8_t ttl_orig = iphdr ? iphdr->ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; - uint8_t ttl_fake = params.desync_ttl ? params.desync_ttl : ttl_orig; - uint8_t flags_orig = *((uint8_t*)tcphdr+13); - uint32_t *timestamps = tcp_find_timestamps(tcphdr); - - switch(params.desync_mode) - { - case DESYNC_FAKE: - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, - ttl_fake,params.desync_tcp_fooling_mode, - fake, fake_size, newdata, &newlen)) - { - return false; - } - break; - case DESYNC_RST: - case DESYNC_RSTACK: - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (params.desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, - ttl_fake,params.desync_tcp_fooling_mode, - NULL, 0, newdata, &newlen)) - { - return false; - } - break; - case DESYNC_DISORDER: - case DESYNC_DISORDER2: - { - size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1; - uint8_t fakeseg[1600]; - size_t fakeseg_len; - - if (split_posseq,split_pos), tcphdr->ack_seq, tcphdr->window, timestamps, - ttl_orig,TCP_FOOL_NONE, - data_payload+split_pos, len_payload-split_pos, newdata, &newlen) || - !rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) - { - return false; - } - } - - - if (params.desync_mode==DESYNC_DISORDER) - { - DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) - fakeseg_len = sizeof(fakeseg); - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, - ttl_fake,params.desync_tcp_fooling_mode, - zeropkt, split_pos, fakeseg, &fakeseg_len) || - !rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) - { - return false; - } - } - - - DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) - newlen = sizeof(newdata); - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->seq, tcphdr->ack_seq, tcphdr->window, timestamps, - ttl_orig,TCP_FOOL_NONE, - data_payload, split_pos, newdata, &newlen) || - !rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) - { - return false; - } - - if (params.desync_mode==DESYNC_DISORDER) - { - DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos) - if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) - return false; - } - - return true; - } - break; - - default: - return false; - } - - if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) - return false; - - if (params.desync_retrans) - DLOG("dropping packet to force retransmission. len=%zu len_payload=%zu\n", len_pkt, len_payload) - else - { - DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload) - if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt)) - return false; - } - return true; - } - - return false; -} - - typedef enum { pass = 0, modify, drop @@ -664,27 +231,26 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt return res; } - if (proto == IPPROTO_TCP && proto_check_tcp(data, len)) + if (!(*mark & params.desync_fwmark) && proto==IPPROTO_TCP && proto_check_tcp(data, len)) { - tcphdr = (struct tcphdr *) data; len_tcp = len; proto_skip_tcp(&data, &len); //DLOG("got TCP packet. payload_len=%d\n",len) - if (params.desync_mode!=DESYNC_NONE && !(*mark & params.desync_fwmark)) + if (modify_tcp_packet(data, len, tcphdr)) + res = modify; + if (params.desync_mode!=DESYNC_NONE) { if (dpi_desync_packet(data_pkt, len_pkt, iphdr, ip6hdr, tcphdr, data, len)) res = drop; } - - if (res!=drop && modify_tcp_packet(data, len, tcphdr)) + if (res==modify) { if (iphdr) tcp_fix_checksum(tcphdr, len_tcp, iphdr->saddr, iphdr->daddr); else tcp6_fix_checksum(tcphdr, len_tcp, &ip6hdr->ip6_src, &ip6hdr->ip6_dst); - res = modify; } } return res; @@ -732,21 +298,21 @@ static void exithelp() " --hostcase\t\t\t\t; change Host: => host:\n" " --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" " --hostnospace\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" - " --dpi-desync[=]\t\t\t; try to desync dpi state. modes : fake rst rstack disorder disorder2\n" + " --dpi-desync[=]\t\t\t; try to desync dpi state. modes : fake rst rstack disorder disorder2 split split2\n" " --dpi-desync-fwmark=\t; override fwmark for desync packet. default = 0x%08X\n" " --dpi-desync-ttl=\t\t\t; set ttl for desync packet\n" - " --dpi-desync-fooling=none|md5sig|ts|badseq|badsum\t; can use multiple comma separated values\n" + " --dpi-desync-fooling=[,]\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum\n" " --dpi-desync-retrans=0|1\t\t; 0(default)=reinject original data packet after fake 1=drop original data packet to force its retransmission\n" " --dpi-desync-skip-nosni=0|1\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n" - " --dpi-desync-split-pos=<1..%zu>\t; (for disorder only) split TCP packet at specified position\n" + " --dpi-desync-split-pos=<1..%u>\t; (for disorder only) split TCP packet at specified position\n" " --dpi-desync-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n" " --hostlist=\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)\n", - DPI_DESYNC_FWMARK_DEFAULT,sizeof(zeropkt) + DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_MAX_FAKE_LEN ); exit(1); } -void cleanup_params() +static void cleanup_params() { if (params.hostlist) { @@ -754,12 +320,12 @@ void cleanup_params() params.hostlist = NULL; } } -void exithelp_clean() +static void exithelp_clean() { cleanup_params(); exithelp(); } -void exit_clean(int code) +static void exit_clean(int code) { cleanup_params(); exit(code); @@ -783,8 +349,6 @@ int main(int argc, char **argv) srandom(time(NULL)); - memset(zeropkt, 0, sizeof(zeropkt)); - memset(¶ms, 0, sizeof(params)); memcpy(params.hostspell, "host", 4); // default hostspell *pidfile = 0; @@ -893,6 +457,10 @@ int main(int argc, char **argv) params.desync_mode = DESYNC_DISORDER; else if (!strcmp(optarg,"disorder2")) params.desync_mode = DESYNC_DISORDER2; + else if (!strcmp(optarg,"split")) + params.desync_mode = DESYNC_SPLIT; + else if (!strcmp(optarg,"split2")) + params.desync_mode = DESYNC_SPLIT2; else { fprintf(stderr, "invalid dpi-desync mode\n"); @@ -943,9 +511,9 @@ int main(int argc, char **argv) break; case 16: /* dpi-desync-split-pos */ params.desync_split_pos = atoi(optarg); - if (params.desync_split_pos<1 || params.desync_split_pos>sizeof(zeropkt)) + if (params.desync_split_pos<1 || params.desync_split_pos>DPI_DESYNC_MAX_FAKE_LEN) { - fprintf(stderr, "dpi-desync-split-pos must be within 1..%zu range\n",sizeof(zeropkt)); + fprintf(stderr, "dpi-desync-split-pos must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN); exit_clean(1); } break; @@ -1023,7 +591,9 @@ int main(int argc, char **argv) if (!droproot(uid, gid)) goto exiterr; printf("Running as UID=%u GID=%u\n", getuid(), getgid()); - signal(SIGHUP, onhup); + signal(SIGHUP, onhup); + + desync_init(); fd = nfq_fd(h); diff --git a/nfq/params.h b/nfq/params.h new file mode 100644 index 0000000..63be26a --- /dev/null +++ b/nfq/params.h @@ -0,0 +1,32 @@ +#pragma once + +#include "params.h" +#include "strpool.h" + +#include +#include +#include + +#define Q_RCVBUF (128*1024) // in bytes +#define Q_MAXLEN 1024 // in packets + +struct params_s +{ + bool debug; + int wsize; + int qnum; + bool hostcase, hostnospace; + char hostspell[4]; + enum dpi_desync_mode desync_mode; + bool desync_retrans,desync_skip_nosni,desync_any_proto; + int desync_split_pos; + uint8_t desync_ttl; + uint8_t desync_tcp_fooling_mode; + uint32_t desync_fwmark; + char hostfile[256]; + strpool *hostlist; +}; + +extern struct params_s params; + +#define DLOG(format, ...) {if (params.debug) printf(format, ##__VA_ARGS__);} diff --git a/nfq/protocol.c b/nfq/protocol.c new file mode 100644 index 0000000..b0625c2 --- /dev/null +++ b/nfq/protocol.c @@ -0,0 +1,129 @@ +#include "protocol.h" +#include "helpers.h" +#include +#include +#include + +const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; +bool IsHttp(const char *data, size_t len) +{ + const char **method; + size_t method_len; + for (method = http_methods; *method; method++) + { + method_len = strlen(*method); + if (method_len <= len && !memcmp(data, *method, method_len)) + return true; + } + return false; +} +bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host) +{ + const uint8_t *p, *s, *e=data+len; + + p = find_bin_const(data, len, "\nHost:", 6); + if (!p) return false; + p+=6; + while(pp) + { + size_t slen = s-p; + if (host && len_host) + { + if (slen>=len_host) slen=len_host-1; + for(size_t i=0;i=6 && data[0]==0x16 && data[1]==0x03 && data[2]==0x01 && data[5]==0x01 && (ntohs(*(uint16_t*)(data+3))+5)<=len; +} +bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext) +{ + // +0 + // u8 ContentType: Handshake + // u16 Version: TLS1.0 + // u16 Length + // +5 + // u8 HandshakeType: ClientHello + // u24 Length + // u16 Version + // c[32] random + // u8 SessionIDLength + // + // u16 CipherSuitesLength + // + // u8 CompressionMethodsLength + // + // u16 ExtensionsLength + + size_t l,ll; + + l = 1+2+2+1+3+2+32; + // SessionIDLength + if (len<(l+1)) return false; + ll = data[6]<<16 | data[7]<<8 | data[8]; // HandshakeProtocol length + if (len<(ll+9)) return false; + l += data[l]+1; + // CipherSuitesLength + if (len<(l+2)) return false; + l += ntohs(*(uint16_t*)(data+l))+2; + // CompressionMethodsLength + if (len<(l+1)) return false; + l += data[l]+1; + // ExtensionsLength + if (len<(l+2)) return false; + + data+=l; len-=l; + l=ntohs(*(uint16_t*)data); + data+=2; len-=2; + if (l=4) + { + uint16_t etype=*(uint16_t*)data; + size_t elen=ntohs(*(uint16_t*)(data+2)); + data+=4; l-=4; + if (l=len_host) slen=len_host-1; + for(size_t i=0;i +#include +#include + +bool IsHttp(const char *data, size_t len); +bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host); +bool IsTLSClientHello(const uint8_t *data, size_t len); +bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext); +bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host); diff --git a/nfq/sec.c b/nfq/sec.c index d17720d..88a91bd 100644 --- a/nfq/sec.c +++ b/nfq/sec.c @@ -1,6 +1,6 @@ +#include "sec.h" #include #include -#include "sec.h" #include #include #include diff --git a/nfq/sec.h b/nfq/sec.h index cfa5752..f854628 100644 --- a/nfq/sec.h +++ b/nfq/sec.h @@ -1,10 +1,12 @@ #pragma once -#include -#include #include +#include +#include +#include bool setpcap(uint64_t caps); +bool checkpcap(uint64_t caps); int getmaxcap(); bool dropcaps(); bool droproot(uid_t uid, gid_t gid);