diff --git a/docs/quick_start.md b/docs/quick_start.md index d4699d2..d7679d3 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -213,7 +213,7 @@ > Если используются методы нулевой фазы десинхронизации (`--mss`, > `--wssize`, `--dpi-desync=syndata`) и режим фильтрации `hostlist`, то все > параметры, относящиеся к этим методам, следует помещать в отдельные - > профили мульистратегии, которые получат управление до определения имени + > профили мультистратегии, которые получат управление до определения имени > хоста. Необходимо понимать алгоритм работы мультистратегий. Самым надежным > вариантом будет дублирование этих параметров на 2 профиля. Какой-нибудь > сработает в зависимости от параметра `MODE_FILTER`. @@ -237,6 +237,11 @@ 9. Удалите директорию из /tmp, откуда производилась установка. +## Полное удаление + +1. Прогоните `/opt/zapret/uninstall_easy.sh`. +2. Cогласитесь на удаление зависимостей в openwrt. +3. Удалите каталог `/opt/zapret`. ## Итог Это минимальная инструкция, чтобы быстро сориентироваться с чего начать. diff --git a/docs/quick_start_windows.md b/docs/quick_start_windows.md index ef42a04..6443f2d 100644 --- a/docs/quick_start_windows.md +++ b/docs/quick_start_windows.md @@ -47,6 +47,14 @@ _"Совсем ничего не могу, все очень сложно, да Не помогла _"таблетка"_ ? Это вовсе не значит, что ничего не получится. Но придется делать по нормальному. +## НЕ ПОМОГЛО, КАК ТЕПЕРЬ ЭТО УДАЛИТЬ + +Если вы не устанавливали zapret как службу или запланированную задачу (а это требует редактирования cmd файлов), +достаточно закрыть окно с winws и запустить windivert_delete.cmd. +Альтернатива - перезагрузить компьютер. +После чего можно удалить папку с zapret. На этом деинсталляция закончена. +Если же вы устанавливали zapret как службу, то вы наверняка знаете как ее удалить. + ## РЕШЕНИЕ "КАК ПОЛОЖЕНО" 1) Скачайте и распакуйте архив https://github.com/bol-van/zapret-win-bundle/archive/refs/heads/master.zip. diff --git a/mdig/mdig.c b/mdig/mdig.c index 6521f5f..4b9bfc7 100644 --- a/mdig/mdig.c +++ b/mdig/mdig.c @@ -424,7 +424,7 @@ bool dns_parse_print(const uint8_t *a, size_t len) } int dns_parse_query() { - uint8_t a[1500]; + uint8_t a[8192]; size_t l; #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); diff --git a/nfq/conntrack.c b/nfq/conntrack.c index 1f1fd7c..22b82dd 100644 --- a/nfq/conntrack.c +++ b/nfq/conntrack.c @@ -12,29 +12,6 @@ static void ut_oom_recover(void *elem) oom = true; } -const char *l7proto_str(t_l7proto l7) -{ - switch(l7) - { - case HTTP: return "http"; - case TLS: return "tls"; - case QUIC: return "quic"; - case WIREGUARD: return "wireguard"; - case DHT: return "dht"; - default: return "unknown"; - } -} -bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7) -{ - return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) || - (l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) || - (l7proto==TLS && (filter_l7 & L7_PROTO_TLS)) || - (l7proto==QUIC && (filter_l7 & L7_PROTO_QUIC)) || - (l7proto==WIREGUARD && (filter_l7 & L7_PROTO_WIREGUARD)) || - (l7proto==DHT && (filter_l7 & L7_PROTO_DHT)); -} - - static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"}; static void connswap(const t_conn *c, t_conn *c2) diff --git a/nfq/conntrack.h b/nfq/conntrack.h index 415c533..ffe5270 100644 --- a/nfq/conntrack.h +++ b/nfq/conntrack.h @@ -4,8 +4,6 @@ // this conntrack is not bullet-proof // its designed to satisfy dpi desync needs only -#include "packet_queue.h" - #include #include #include @@ -19,6 +17,8 @@ #include #include +#include "packet_queue.h" +#include "protocol.h" //#define HASH_BLOOM 20 #define HASH_NONFATAL_OOM 1 @@ -53,16 +53,6 @@ typedef struct { // FIN - FIN or RST received typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate; -typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT} t_l7proto; -#define L7_PROTO_HTTP 0x00000001 -#define L7_PROTO_TLS 0x00000002 -#define L7_PROTO_QUIC 0x00000004 -#define L7_PROTO_WIREGUARD 0x00000008 -#define L7_PROTO_DHT 0x00000010 -#define L7_PROTO_UNKNOWN 0x80000000 -const char *l7proto_str(t_l7proto l7); -bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7); - typedef struct { bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache diff --git a/nfq/desync.c b/nfq/desync.c index cf942e1..f54d6eb 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -97,11 +97,11 @@ bool desync_only_first_stage(enum dpi_desync_mode mode) } bool desync_valid_second_stage(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER; + return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER; } bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2; + return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2; } bool desync_valid_second_stage_udp(enum dpi_desync_mode mode) { @@ -131,6 +131,10 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) return DESYNC_SPLIT; else if (!strcmp(s,"split2")) return DESYNC_SPLIT2; + else if (!strcmp(s,"multisplit")) + return DESYNC_MULTISPLIT; + else if (!strcmp(s,"multidisorder")) + return DESYNC_MULTIDISORDER; else if (!strcmp(s,"ipfrag2")) return DESYNC_IPFRAG2; else if (!strcmp(s,"hopbyhop")) @@ -582,22 +586,7 @@ static bool replay_queue(struct rawpacket_tailhead *q); static size_t pos_normalize(size_t split_pos, size_t reasm_offset, size_t len_payload) { - size_t rsplit_pos = split_pos; - // normalize split pos to current packet - split_pos=(split_pos>reasm_offset && (split_pos-reasm_offset) %zu\n",rsplit_pos,split_pos); - else - DLOG("split pos %zu is outside of this packet %zu-%zu\n",rsplit_pos,reasm_offset,reasm_offset+len_payload); - } - } - return split_pos; + return (split_pos>reasm_offset && (split_pos-reasm_offset)tcp->th_flags & TH_SYN) && dis->len_payload) { const uint8_t *fake; @@ -856,6 +845,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint const uint8_t *rdata_payload = dis->data_payload; size_t rlen_payload = dis->len_payload; size_t split_pos; + size_t multisplit_pos[MAX_SPLITS]; + int multisplit_count; + int i; t_l7proto l7proto = UNKNOWN; if (replay) @@ -1065,35 +1057,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } - // desync profile may have changed after hostname was revealed - switch(l7proto) - { - case HTTP: - fake = dp->fake_http; - fake_size = dp->fake_http_size; - split_pos = HttpPos(dp->desync_split_http_req, dp->desync_split_pos, rdata_payload, rlen_payload); - break; - case TLS: - fake = dp->fake_tls; - fake_size = dp->fake_tls_size; - split_pos = TLSPos(dp->desync_split_tls, dp->desync_split_pos, rdata_payload, rlen_payload, 0); - break; - default: - fake = dp->fake_unknown; - fake_size = dp->fake_unknown_size; - split_pos=dp->desync_split_pos; - break; - } - - // we do not need reasm buffer anymore - reasm_orig_cancel(ctrack); - rdata_payload=NULL; - if (l7proto==UNKNOWN) { if (!dp->desync_any_proto) { DLOG("not applying tampering to unknown protocol\n"); + reasm_orig_cancel(ctrack); return verdict; } DLOG("applying tampering to unknown protocol\n"); @@ -1135,7 +1104,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } - if (dp->desync_mode==DESYNC_NONE) return verdict; + if (dp->desync_mode==DESYNC_NONE) + { + reasm_orig_cancel(ctrack); + return verdict; + } if (params.debug) { @@ -1144,27 +1117,96 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2)); DLOG("dpi desync src=%s dst=%s\n",s1,s2); } - - if (!split_pos || split_pos>rlen_payload) split_pos=1; - split_pos=pos_normalize(split_pos,reasm_offset,dis->len_payload); - enum dpi_desync_mode desync_mode = dp->desync_mode; + const struct proto_pos *spos; + switch(l7proto) + { + case HTTP: + fake = dp->fake_http; + fake_size = dp->fake_http_size; + spos = &dp->split_http; + break; + case TLS: + fake = dp->fake_tls; + fake_size = dp->fake_tls_size; + spos = &dp->split_tls; + break; + default: + fake = dp->fake_unknown; + fake_size = dp->fake_unknown_size; + spos = &dp->split_unknown; + break; + } + if (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER) + { + split_pos=0; + ResolveMultiPos(rdata_payload, rlen_payload, l7proto, dp->splits, dp->split_count, multisplit_pos, &multisplit_count); + if (params.debug) + { + if (multisplit_count) + { + DLOG("multisplit pos: "); + for (i=0;ilen_payload); + if (multisplit_pos[j]) j++; + } + multisplit_count=j; + if (params.debug) + { + if (multisplit_count) + { + DLOG("normalized multisplit pos: "); + for (i=0;idesync_mode==DESYNC_SPLIT || dp->desync_mode==DESYNC_SPLIT2 || dp->desync_mode==DESYNC_DISORDER || dp->desync_mode==DESYNC_DISORDER2 || + dp->desync_mode2==DESYNC_SPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_DISORDER || dp->desync_mode2==DESYNC_DISORDER2) + { + multisplit_count=0; + split_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, spos); + DLOG("regular split pos: %zu\n",split_pos); + if (!split_pos || split_pos>rlen_payload) split_pos=1; + split_pos=pos_normalize(split_pos,reasm_offset,dis->len_payload); + if (split_pos) + DLOG("normalized regular split pos : %zu\n",split_pos); + else + DLOG("regular split pos is outside of this packet\n"); + } + else + { + multisplit_count=0; + split_pos = 0; + } + + // we do not need reasm buffer anymore + reasm_orig_cancel(ctrack); + rdata_payload=NULL; + uint32_t fooling_orig = FOOL_NONE; - bool b; + bool bFake = false; pkt1_len = sizeof(pkt1); - b = false; - switch(desync_mode) + switch(dp->desync_mode) { case DESYNC_FAKE_KNOWN: - if (reasm_offset) - { - desync_mode = dp->desync_mode2; - break; - } + if (reasm_offset) break; if (l7proto==UNKNOWN) { DLOG("not applying fake because of unknown protocol\n"); - desync_mode = dp->desync_mode2; break; } case DESYNC_FAKE: @@ -1176,14 +1218,14 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint { return verdict; } - DLOG("sending fake request : "); + DLOG("sending fake : "); hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n"); - b = true; + bFake = true; break; case DESYNC_RST: case DESYNC_RSTACK: if (reasm_offset) break; - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (dp->desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, NULL, 0, pkt1, &pkt1_len)) @@ -1191,15 +1233,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint return verdict; } DLOG("sending fake RST/RSTACK\n"); - b = true; + bFake = true; break; case DESYNC_HOPBYHOP: case DESYNC_DESTOPT: case DESYNC_IPFRAG1: - fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; - desync_mode = dp->desync_mode2; - if (dis->ip6 && (desync_mode==DESYNC_NONE || !desync_valid_second_stage_tcp(desync_mode) || - (!split_pos && (desync_mode==DESYNC_SPLIT || desync_mode==DESYNC_SPLIT2 || desync_mode==DESYNC_DISORDER || desync_mode==DESYNC_DISORDER2)))) + fooling_orig = (dp->desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (dp->desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; + if (dis->ip6 && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2) || + (!split_pos && (dp->desync_mode2==DESYNC_SPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_DISORDER || dp->desync_mode2==DESYNC_DISORDER2)) || + (!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)))) { if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), @@ -1219,24 +1261,65 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint break; } - if (b) + if (bFake) { if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; - if (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2)) - { - DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload); - verdict_tcp_csum_fix(verdict, dis->tcp, dis->transport_len, dis->ip, dis->ip6); - if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt)) - return verdict; - return VERDICT_DROP; - } - desync_mode = dp->desync_mode2; } - pkt1_len = sizeof(pkt1); + enum dpi_desync_mode desync_mode = dp->desync_mode2==DESYNC_NONE ? dp->desync_mode : dp->desync_mode2; switch(desync_mode) { + case DESYNC_MULTISPLIT: + if (multisplit_count) + { + size_t from,to; + for (i=0,from=0 ; i<=multisplit_count ; i++) + { + to = i==multisplit_count ? dis->len_payload : multisplit_pos[i]; + + pkt1_len = sizeof(pkt1); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, + net32_add(dis->tcp->th_seq,from), dis->tcp->th_ack, + dis->tcp->th_win, scale_factor, timestamps,ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), + fooling_orig,0,0, + dis->data_payload+from, to-from, pkt1, &pkt1_len)) + return verdict; + DLOG("sending multisplit part %d %zu-%zu len=%zu : ",i+1,from,to-1,to-from); + hexdump_limited_dlog(dis->data_payload+from,to-from,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + return verdict; + + from = to; + } + return VERDICT_DROP; + } + break; + case DESYNC_MULTIDISORDER: + if (multisplit_count) + { + size_t from,to; + for (i=multisplit_count-1,to=dis->len_payload ; i>=-1 ; i--) + { + from = i>=0 ? multisplit_pos[i] : 0; + + pkt1_len = sizeof(pkt1); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, + net32_add(dis->tcp->th_seq,from), dis->tcp->th_ack, + dis->tcp->th_win, scale_factor, timestamps,ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), + fooling_orig,0,0, + dis->data_payload+from, to-from, pkt1, &pkt1_len)) + return verdict; + DLOG("sending multisplit part %d %zu-%zu len=%zu : ",i+2,from,to-1,to-from); + hexdump_limited_dlog(dis->data_payload+from,to-from,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + return verdict; + + to = from; + } + return VERDICT_DROP; + } + break; case DESYNC_DISORDER: case DESYNC_DISORDER2: if (split_pos) @@ -1270,6 +1353,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg_len = dis->len_payload-split_pos; } + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq , split_pos - dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, @@ -1357,6 +1441,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg_len = split_pos; } + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq,-dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, @@ -1436,7 +1521,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint default: break; } - + + if (bFake) + { + DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload); + verdict_tcp_csum_fix(verdict, dis->tcp, dis->transport_len, dis->ip, dis->ip6); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt)) + return verdict; + return VERDICT_DROP; + } } return verdict; @@ -1833,7 +1926,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_FAKE: if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len)) return verdict; - DLOG("sending fake request : "); + DLOG("sending fake : "); hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n"); if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; @@ -1996,7 +2089,7 @@ static void packet_debug(bool replay, const struct dissect *dis) char s[80]; str_tcphdr(s,sizeof(s),dis->tcp); DLOG(" %s\n",s); - if (dis->len_payload) { DLOG("TCP: "); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } + if (dis->len_payload) { DLOG("TCP: len=%zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } } else if (dis->udp) @@ -2004,7 +2097,7 @@ static void packet_debug(bool replay, const struct dissect *dis) char s[30]; str_udphdr(s,sizeof(s),dis->udp); DLOG(" %s\n",s); - if (dis->len_payload) { DLOG("UDP: "); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } + if (dis->len_payload) { DLOG("UDP: len=%zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } } else DLOG("\n"); diff --git a/nfq/desync.h b/nfq/desync.h index 4aa42aa..4250f5c 100644 --- a/nfq/desync.h +++ b/nfq/desync.h @@ -32,6 +32,8 @@ enum dpi_desync_mode { DESYNC_DISORDER2, DESYNC_SPLIT, DESYNC_SPLIT2, + DESYNC_MULTISPLIT, + DESYNC_MULTIDISORDER, DESYNC_IPFRAG2, DESYNC_HOPBYHOP, DESYNC_DESTOPT, diff --git a/nfq/helpers.c b/nfq/helpers.c index 2d2d320..90c0452 100644 --- a/nfq/helpers.c +++ b/nfq/helpers.c @@ -11,6 +11,27 @@ #include "params.h" +int unique_size_t(size_t *pu, int ct) +{ + int i, j, u; + for (i = j = 0; j < ct; i++) + { + u = pu[j++]; + for (; j < ct && pu[j] == u; j++); + pu[i] = u; + } + return i; +} +static int cmp_size_t(const void * a, const void * b) +{ + return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b; +} +void qsort_size_t(size_t *array,size_t ct) +{ + qsort(array,ct,sizeof(*array),cmp_size_t); +} + + void rtrim(char *s) { if (s) diff --git a/nfq/helpers.h b/nfq/helpers.h index c06862e..58051a7 100644 --- a/nfq/helpers.h +++ b/nfq/helpers.h @@ -17,6 +17,9 @@ typedef union char _align[32]; // force 16-byte alignment for ip6_and int128 ops } sockaddr_in46; +int unique_size_t(size_t *pu, int ct); +void qsort_size_t(size_t *array,size_t ct); + void rtrim(char *s); void replace_char(char *s, char from, char to); char *strncasestr(const char *s,const char *find, size_t slen); diff --git a/nfq/nfqws.c b/nfq/nfqws.c index e07054f..f2dc4fc 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -673,10 +673,7 @@ static bool parse_l7_list(char *opt, uint32_t *l7) *l7 |= L7_PROTO_UNKNOWN; else return false; - if (e) - { - *e++=c; - } + if (e) *e++=c; p = e; } return true; @@ -723,15 +720,173 @@ static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6) *ipv6 = true; else return false; - if (e) - { - *e++=c; - } + if (e) *e++=c; p = e; } return true; } +static bool parse_httpreqpos(const char *s, struct proto_pos *sp) +{ + if (!strcmp(s, "method")) + { + sp->marker = PM_HTTP_METHOD; + sp->pos=2; + } + else if (!strcmp(s, "host")) + { + sp->marker = PM_HOST; + sp->pos=1; + } + else + return false; + return true; +} +static bool parse_tlspos(const char *s, struct proto_pos *sp) +{ + if (!strcmp(s, "sni")) + { + sp->marker = PM_HOST; + sp->pos=1; + } + else if (!strcmp(s, "sniext")) + { + sp->marker = PM_SNI_EXT; + sp->pos=0; + } + else if (!strcmp(s, "snisld")) + { + sp->marker = PM_HOST_MIDSLD; + sp->pos=1; + } + else + return false; + return true; +} + +static bool parse_int16(const char *p, int16_t *v) +{ + if (*p=='+' || *p=='-' || *p>='0' && *p<='9') + { + int i = atoi(p); + *v = (int16_t)i; + return *v==i; // check overflow + } + return false; +} +static bool parse_posmarker(const char *opt, uint8_t *posmarker) +{ + if (!strcmp(opt,"host")) + *posmarker = PM_HOST; + else if (!strcmp(opt,"endhost")) + *posmarker = PM_HOST_END; + else if (!strcmp(opt,"sld")) + *posmarker = PM_HOST_SLD; + else if (!strcmp(opt,"midsld")) + *posmarker = PM_HOST_MIDSLD; + else if (!strcmp(opt,"endsld")) + *posmarker = PM_HOST_ENDSLD; + else if (!strcmp(opt,"method")) + *posmarker = PM_HTTP_METHOD; + else if (!strcmp(opt,"sniext")) + *posmarker = PM_SNI_EXT; + else + return false; + return true; +} +static bool parse_split_pos(char *opt, struct proto_pos *split) +{ + if (parse_int16(opt,&split->pos)) + { + split->marker = PM_ABS; + return !!split->pos; + } + else + { + char c,*p=opt; + bool b; + + for (; *opt && *opt!='+' && *opt!='-'; opt++); + c=*opt; *opt=0; + b=parse_posmarker(p,&split->marker); + *opt=c; + if (!b) return false; + if (*opt) + return parse_int16(opt,&split->pos); + else + split->pos = 0; + } + return true; +} +static bool parse_split_pos_list(char *opt, struct proto_pos *splits, int splits_size, int *split_count) +{ + char c,*e,*p; + + for (p=opt, *split_count=0 ; p && *split_countsplit_unknown.marker=PM_ABS; + dp->split_unknown.pos=2; + for (i=0;isplit_count;i++) + { + if (dp->splits[i].marker==PM_ABS) + { + dp->split_unknown.pos=dp->splits[i].pos; + break; + } + } + if (PROTO_POS_EMPTY(&dp->split_http)) + { + dp->split_http=dp->split_unknown; + for (i=0;isplit_count;i++) + if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_HTTP_METHOD) + { + dp->split_http = dp->splits[i]; + break; + } + } + if (PROTO_POS_EMPTY(&dp->split_tls)) + { + dp->split_tls=dp->split_unknown; + for (i=0;isplit_count;i++) + if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_SNI_EXT) + { + dp->split_tls = dp->splits[i]; + break; + } + } +} +static void SplitDebug(void) +{ + struct desync_profile_list *dpl; + const struct desync_profile *dp; + LIST_FOREACH(dpl, ¶ms.desync_profiles, next) + { + dp = &dpl->dp; + DLOG("profile %d split_http %s %d\n",dp->n,posmarker_name(dp->split_http.marker),dp->split_http.pos); + DLOG("profile %d split_tls %s %d\n",dp->n,posmarker_name(dp->split_tls.marker),dp->split_tls.pos); + DLOG("profile %d split_unknown %s %d\n",dp->n,posmarker_name(dp->split_unknown.marker),dp->split_unknown.pos); + for(int x=0;xsplit_count;x++) + DLOG("profile %d multisplit %s %d\n",dp->n,posmarker_name(dp->splits[x].marker),dp->splits[x].pos); + } +} + + #ifdef __CYGWIN__ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *buf, size_t len) { @@ -757,10 +912,7 @@ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *bu if (n) strncat(buf," or ",len-strlen(buf)-1); strncat(buf, s1, len-strlen(buf)-1); - if (e) - { - *e++=c; - } + if (e) *e++=c; p = e; } strncat(buf, ")", len-strlen(buf)-1); @@ -912,7 +1064,9 @@ static void exithelp(void) " --hostspell\t\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" " --hostnospace\t\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" " --domcase\t\t\t\t\t; mix domain case : Host: TeSt.cOm\n" - " --dpi-desync=[,][,]\t; try to desync dpi state. modes : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen tamper\n" + " --dpi-desync=[,][,]\t; try to desync dpi state. modes :\n" + "\t\t\t\t\t\t; synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1\n" + "\t\t\t\t\t\t; disorder disorder2 split split2 multisplit multidisorder ipfrag2 udplen tamper\n" #ifdef __linux__ " --dpi-desync-fwmark=\t\t; override fwmark for desync packet. default = 0x%08X (%u)\n" #elif defined(SO_USER_COOKIE) @@ -925,9 +1079,9 @@ static void exithelp(void) " --dpi-desync-fooling=[,]\t\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2\n" " --dpi-desync-repeats=\t\t\t; send every desync packet N times\n" " --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n" - " --dpi-desync-split-pos=<1..%u>\t\t; data payload split position\n" - " --dpi-desync-split-http-req=method|host\t; split at specified logical part of plain http request\n" - " --dpi-desync-split-tls=sni|sniext|snisld\t; split at specified logical part of TLS ClientHello\n" + " --dpi-desync-split-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions. markers: method,host,endhost,sld,endsld,midsld,sniext\n" + "\t\t\t\t\t\t; full list is only used by multisplit and multidisorder\n" + "\t\t\t\t\t\t; single split takes first l7-protocol-compatible parameter if present, first abs value otherwise\n" " --dpi-desync-split-seqovl=\t\t; use sequence overlap before first sent original split segment\n" " --dpi-desync-split-seqovl-pattern=|0xHEX ; pattern for the fake part of overlap\n" " --dpi-desync-ipfrag-pos-tcp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n" @@ -953,7 +1107,6 @@ static void exithelp(void) DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT, #endif AUTOTTL_DEFAULT_DELTA,AUTOTTL_DEFAULT_MIN,AUTOTTL_DEFAULT_MAX, - DPI_DESYNC_MAX_FAKE_LEN, DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_UDP_DEFAULT, DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_TCP_DEFAULT, BADSEQ_INCREMENT_DEFAULT, BADSEQ_ACK_INCREMENT_DEFAULT, @@ -967,29 +1120,6 @@ static void exithelp_clean(void) exithelp(); } -bool parse_httpreqpos(const char *s, enum httpreqpos *pos) -{ - if (!strcmp(s, "method")) - *pos = httpreqpos_method; - else if (!strcmp(s, "host")) - *pos = httpreqpos_host; - else - return false; - return true; -} -bool parse_tlspos(const char *s, enum tlspos *pos) -{ - if (!strcmp(s, "sni")) - *pos = tlspos_sni; - else if (!strcmp(s, "sniext")) - *pos = tlspos_sniext; - else if (!strcmp(s, "snisld")) - *pos = tlspos_snisld; - else - return false; - return true; -} - #ifndef __OpenBSD__ // no static to not allow optimizer to inline this func (save stack) void config_from_file(const char *filename) @@ -1447,21 +1577,27 @@ int main(int argc, char **argv) dp->desync_skip_nosni = !optarg || atoi(optarg); break; case 23: /* dpi-desync-split-pos */ - if (sscanf(optarg,"%u",&dp->desync_split_pos)<1 || dp->desync_split_pos<1) { - DLOG_ERR("dpi-desync-split-pos is not valid\n"); - exit_clean(1); + int ct; + if (!parse_split_pos_list(optarg,dp->splits+dp->split_count,MAX_SPLITS-dp->split_count,&ct)) + { + DLOG_ERR("could not parse split pos list or too much positions (before parsing - %u, max - %u) : %s\n",dp->split_count,MAX_SPLITS,optarg); + exit_clean(1); + } + dp->split_count += ct; } break; case 24: /* dpi-desync-split-http-req */ - if (!parse_httpreqpos(optarg, &dp->desync_split_http_req)) + // obsolete arg + if (!parse_httpreqpos(optarg, &dp->split_http)) { DLOG_ERR("Invalid argument for dpi-desync-split-http-req\n"); exit_clean(1); } break; case 25: /* dpi-desync-split-tls */ - if (!parse_tlspos(optarg, &dp->desync_split_tls)) + // obsolete arg + if (!parse_tlspos(optarg, &dp->split_tls)) { DLOG_ERR("Invalid argument for dpi-desync-split-tls\n"); exit_clean(1); @@ -1912,6 +2048,13 @@ int main(int argc, char **argv) LIST_FOREACH(dpl, ¶ms.desync_profiles, next) { dp = &dpl->dp; + + if (!dp->split_count && (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)) + { + DLOG_ERR("multisplit requires explicit split pos\n"); + exit_clean(1); + } + // not specified - use desync_ttl value instead if (dp->desync_ttl6 == 0xFF) dp->desync_ttl6=dp->desync_ttl; if (!AUTOTTL_ENABLED(dp->desync_autottl6)) dp->desync_autottl6 = dp->desync_autottl; @@ -1919,8 +2062,7 @@ int main(int argc, char **argv) DLOG("[profile %d] autottl ipv4 %u:%u-%u\n",dp->n,dp->desync_autottl.delta,dp->desync_autottl.min,dp->desync_autottl.max); if (AUTOTTL_ENABLED(dp->desync_autottl6)) DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",dp->n,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max); - if (dp->desync_split_tls==tlspos_none && dp->desync_split_pos) dp->desync_split_tls=tlspos_pos; - if (dp->desync_split_http_req==httpreqpos_none && dp->desync_split_pos) dp->desync_split_http_req=httpreqpos_pos; + split_compat(dp); } if (!LoadAllHostLists()) @@ -1937,6 +2079,9 @@ int main(int argc, char **argv) DLOG("\nlists summary:\n"); HostlistsDebug(); IpsetsDebug(); + + DLOG("\nsplits summary:\n"); + SplitDebug(); DLOG("\n"); if (daemon) daemonize(); diff --git a/nfq/params.c b/nfq/params.c index 3cece72..8a7702c 100644 --- a/nfq/params.c +++ b/nfq/params.c @@ -169,7 +169,6 @@ struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head) memcpy(entry->dp.hostspell, "host", 4); // default hostspell entry->dp.desync_skip_nosni = true; - entry->dp.desync_split_pos = 2; entry->dp.desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT; entry->dp.desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT; entry->dp.desync_repeats = 1; diff --git a/nfq/params.h b/nfq/params.h index 7af45c6..8f5151d 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -38,6 +38,8 @@ #define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60 #define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3 +#define MAX_SPLITS 64 + enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG }; struct desync_profile @@ -53,9 +55,14 @@ struct desync_profile char hostspell[4]; enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2; bool desync_retrans,desync_skip_nosni,desync_any_proto; - unsigned int desync_repeats,desync_split_pos,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; - enum httpreqpos desync_split_http_req; - enum tlspos desync_split_tls; + unsigned int desync_repeats,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; + + // multisplit + struct proto_pos splits[MAX_SPLITS]; + int split_count; + // single split pos cache + struct proto_pos split_http,split_tls,split_unknown; + char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence unsigned int desync_start, desync_cutoff; uint8_t desync_ttl, desync_ttl6; diff --git a/nfq/protocol.c b/nfq/protocol.c index bec8362..fd280f4 100644 --- a/nfq/protocol.c +++ b/nfq/protocol.c @@ -24,6 +24,133 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t ** return true; } +const char *l7proto_str(t_l7proto l7) +{ + switch(l7) + { + case HTTP: return "http"; + case TLS: return "tls"; + case QUIC: return "quic"; + case WIREGUARD: return "wireguard"; + case DHT: return "dht"; + default: return "unknown"; + } +} +bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7) +{ + return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) || + (l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) || + (l7proto==TLS && (filter_l7 & L7_PROTO_TLS)) || + (l7proto==QUIC && (filter_l7 & L7_PROTO_QUIC)) || + (l7proto==WIREGUARD && (filter_l7 & L7_PROTO_WIREGUARD)) || + (l7proto==DHT && (filter_l7 & L7_PROTO_DHT)); +} + +#define PM_ABS 0 +#define PM_HOST 1 +#define PM_HOST_END 2 +#define PM_HOST_SLD 3 +#define PM_HOST_MIDSLD 4 +#define PM_HOST_ENDSLD 5 +#define PM_HTTP_METHOD 6 +#define PM_SNI_EXT 7 +bool IsHostMarker(uint8_t posmarker) +{ + switch(posmarker) + { + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + return true; + default: + return false; + } +} +const char *posmarker_name(uint8_t posmarker) +{ + switch(posmarker) + { + case PM_ABS: return "abs"; + case PM_HOST: return "host"; + case PM_HOST_END: return "endhost"; + case PM_HOST_SLD: return "sld"; + case PM_HOST_MIDSLD: return "midsld"; + case PM_HOST_ENDSLD: return "endsld"; + case PM_HTTP_METHOD: return "method"; + case PM_SNI_EXT: return "sniext"; + default: return "?"; + } +} + +static size_t CheckPos(size_t sz, ssize_t offset) +{ + return (offset>=0 && offsetmarker, sp->pos, data, sz); + case TLS: + return TLSPos(sp->marker, sp->pos, data, sz); + default: + return AnyProtoPos(sp->marker, sp->pos, data, sz); + } +} +void ResolveMultiPos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *splits, int split_count, size_t *pos, int *pos_count) +{ + int i,j; + for(i=j=0;i='A' && *method<='Z') method++; - if (i<3 || *method!=' ') break; - return method-http-1; - case httpreqpos_host: - if (HttpFindHostConst(&host,http,sz) && (host-http+7)='A' && *p<='Z') p++; + if (i<3 || *p!=' ') break; + return CheckPos(sz,method-data+pos); + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + if (HttpFindHostConst(&host,data,sz) && (host-data+7) aka.ru -// return false if SNI ext is bad or SLD is not found -static bool TLSHelloFindMiddleOfSLDInSNI(const uint8_t *ext, size_t elen, const uint8_t **p) -{ - size_t len; - if (!TLSHelloFindNLDInSNI(ext,elen,2,p,&len)) - return false; - // in case of one letter SLD (x.com) we split at '.' to prevent appearance of the whole SLD - *p = (len==1) ? *p+1 : *p+len/2; - return true; -} -size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type) +size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz) { size_t elen; const uint8_t *ext, *p; - switch(tpos_type) + size_t offset_host,len_host; + ssize_t offset; + + switch(posmarker) { - case tlspos_sni: - case tlspos_sniext: - if (TLSFindExt(tls,sz,0,&ext,&elen,false)) - return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1; - break; - case tlspos_snisld: - if (TLSFindExt(tls,sz,0,&ext,&elen,false)) - if (TLSHelloFindMiddleOfSLDInSNI(ext,elen,&p)) - return p-tls; - break; - case tlspos_pos: - break; - default: + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + case PM_SNI_EXT: + if (TLSFindExt(data,sz,0,&ext,&elen,false)) + { + if (posmarker==PM_SNI_EXT) + { + offset = ext-data+1+pos; + return (offset>=0 && offsetmarker==PM_ABS && (sp)->pos==0) +bool IsHostMarker(uint8_t posmarker); +const char *posmarker_name(uint8_t posmarker); +size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz); +size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz); +size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz); +size_t ResolvePos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *sp); +void ResolveMultiPos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *splits, int split_count, size_t *pos, int *pos_count); + extern const char *http_methods[9]; const char *HttpMethod(const uint8_t *data, size_t len); bool IsHttp(const uint8_t *data, size_t len); @@ -21,8 +54,6 @@ const char *HttpFind2ndLevelDomain(const char *host); int HttpReplyCode(const uint8_t *data, size_t len); // must be pre-checked by IsHttpReply bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host); -enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos }; -size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz); uint16_t TLSRecordDataLen(const uint8_t *data); size_t TLSRecordLen(const uint8_t *data); @@ -35,8 +66,6 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t ** bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK); bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK); bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK); -enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_snisld, tlspos_pos }; -size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type); bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len); bool IsDhtD1(const uint8_t *data, size_t len); @@ -56,4 +85,4 @@ bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid); bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len); bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len); -bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello); +//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);