diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index 7bb9a83..28e1576 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index 15d484e..e9b2d24 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/freebsd-x64/dvtws b/binaries/freebsd-x64/dvtws index b3a3651..513bec6 100755 Binary files a/binaries/freebsd-x64/dvtws and b/binaries/freebsd-x64/dvtws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index c9fe48b..e8eb093 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index 4376be7..223b290 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index 1a384b3..1a69408 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index 868d6e8..54fdbc6 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index 3aec84f..20bd295 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index d3eeea1..8ed8243 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/docs/changes.txt b/docs/changes.txt index a9c5537..0a51dfe 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -237,3 +237,9 @@ nfqws : udplen fooling supports packet shrinking (negative increment value) v49 QUIC support integrated to the main system and setup + +v50 + +DHT protocol support. +DPI desync mode 'tamper' for DHT. +HEX string support in addition to binary files. diff --git a/docs/readme.eng.md b/docs/readme.eng.md index e5c4e11..4d3c268 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -154,7 +154,7 @@ nfqws takes the following parameters: --hostspell ; exact spelling of "Host" header. must be 4 chars. default is "host" --hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size --domcase ; mix domain case : Host: TeSt.cOm - --dpi-desync=[,][,] ; try to desync dpi state. modes : synack fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen + --dpi-desync=[,][,] ; try to desync dpi state. modes : synack fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen tamper --dpi-desync-fwmark= ; override fwmark for desync packet. default = 0x40000000 (1073741824) --dpi-desync-ttl= ; set ttl for desync packet --dpi-desync-ttl6= ; set ipv6 hop limit for desync packet. by default ttl value is used. @@ -173,6 +173,7 @@ nfqws takes the following parameters: --dpi-desync-fake-unknown=|0xHEX ; file containing unknown protocol fake payload --dpi-desync-fake-quic=|0xHEX ; file containing fake QUIC Initial --dpi-desync-fake-wireguard=|0xHEX ; file containing fake wireguard handshake initiation + --dpi-desync-fake-dht=|0xHEX ; file containing fake DHT (d1..e) --dpi-desync-fake-unknown-udp=|0xHEX ; file containing unknown udp protocol fake payload --dpi-desync-udplen-increment= ; increase or decrease udp packet length by N bytes (default 2). negative values decrease length. --dpi-desync-udplen-pattern=|0xHEX ; udp tail fill pattern @@ -427,14 +428,15 @@ Set conntrack timeouts appropriately. UDP attacks are limited. Its not possible to fragment UDP on transport level, only on network (ip) level. Only desync modes `fake`,`hopbyhop`,`destopt`,`ipfrag1` and `ipfrag2` are applicable. -`fake`,`hopbyhop`,`destopt` can be used in combo with `ipfrag2` and `udplen`. +`fake`,`hopbyhop`,`destopt` can be used in combo with `ipfrag2`. +`fake` can be used in combo with `udplen` and `tamper`. -`udplen` increases udp payload size by `--dpi-desync-udplen-increment` bytes. Padding is filled with zeroes. +`udplen` increases udp payload size by `--dpi-desync-udplen-increment` bytes. Padding is filled with zeroes by default but can be overriden with a pattern. This option can resist DPIs that track outgoing UDP packet sizes. Requires that application protocol does not depend on udp payload size. QUIC initial packets are recognized. Decryption and hostname extraction is supported so `--hostlist` parameter will work. -Wireguard handshake initiation is also recognized. +Wireguard handshake initiation and DHT packets are also recognized. For other protocols desync use `--dpi-desync-any-protocol`. Conntrack supports udp. `--dpi-desync-cutoff` will work. UDP conntrack timeout can be set in the 4th parameter of `--ctrack-timeouts`. diff --git a/docs/readme.txt b/docs/readme.txt index d9b5d18..8ad0f47 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -1,4 +1,4 @@ -zapret v.49 +zapret v.50 English ------- @@ -207,7 +207,7 @@ nfqws --hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета --hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase --domcase ; домен после Host: сделать таким : TeSt.cOm - --dpi-desync=[,][,,][, ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000 --dpi-desync-ttl= ; установить ttl для десинхронизирующих пакетов --dpi-desync-ttl6= ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение ttl @@ -223,6 +223,7 @@ nfqws --dpi-desync-fake-tls=|0xHEX ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному w3.org --dpi-desync-fake-unknown=|0xHEX ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт --dpi-desync-fake-quic=|0xHEX ; файл, содержащий фейковый QUIC Initial + --dpi-desync-fake-dht=|0xHEX ; файл, содержащий фейковый пейлоад DHT протокола для dpi-desync=fake, на замену стандартным нулям 64 байт --dpi-desync-fake-unknown-udp=|0xHEX ; файл, содержащий фейковый пейлоад неизвестного udp протокола для dpi-desync=fake, на замену стандартным нулям 64 байт --dpi-desync-udplen-increment= ; насколько увеличивать длину udp пейлоада в режиме udplen --dpi-desync-udplen-pattern=|0xHEX ; чем добивать udp пакет в режиме udplen. по умолчанию - нули @@ -464,14 +465,17 @@ window size итоговый размер окна стал максимальн ПОДДЕРЖКА UDP Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip. -Для UDP действуют только режимы десинхронизации fake,hopbyhop,destopt,ipfrag1,ipfrag2,udplen. -Возможно сочетание fake,hopbyhop,destopt с ipfrag2 и udplen. +Для UDP действуют только режимы десинхронизации fake,hopbyhop,destopt,ipfrag1,ipfrag2,udplen,tamper. +Возможно сочетание fake,hopbyhop,destopt с ipfrag2, fake,fakeknown с udplen и tamper. udplen увеличивает размер udp пакета на указанное в --dpi-desync-udplen-increment количество байтов. -Паддинг заполняется нулями. Предназначено для обмана DPI, ориентирующегося на размеры пакетов. +Паддинг заполняется нулями по умолчанию, но можно задать свой паттерн. +Предназначено для обмана DPI, ориентирующегося на размеры пакетов. Может сработать, если пользовательсткий протокол не привязан жестко к размеру udp пейлоада. +Режим tamper означает модификацию пакетов известных протоколов особенным для протокола образом. +На текущий момент работает только с DHT. Поддерживается определение пакетов QUIC Initial с расшифровкой содержимого и имени хоста, то есть параметр --hostlist будет работать. -Определяется пакет wireguard handshake initiation. +Определяются пакеты wireguard handshake initiation и DHT (начинается с 'd1', кончается 'e'). Для десинхронизации других протоколов обязательно указывать --dpi-desync-any-protocol. Реализован conntrack для udp. Можно пользоваться --dpi-desync-cutoff. Таймаут conntrack для udp можно изменить 4-м параметром в --ctrack-timeouts. diff --git a/nfq/desync.c b/nfq/desync.c index 6d9864f..bcb37d5 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -75,7 +75,7 @@ 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; + 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; } bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode) { @@ -83,7 +83,7 @@ bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode) } bool desync_valid_second_stage_udp(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_UDPLEN || mode==DESYNC_IPFRAG2; + return mode==DESYNC_NONE || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER || mode==DESYNC_IPFRAG2; } enum dpi_desync_mode desync_mode_from_string(const char *s) { @@ -117,6 +117,8 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) return DESYNC_IPFRAG1; else if (!strcmp(s,"udplen")) return DESYNC_UDPLEN; + else if (!strcmp(s,"tamper")) + return DESYNC_TAMPER; return DESYNC_INVALID; } @@ -734,6 +736,13 @@ packet_process_result dpi_desync_udp_packet(uint32_t fwmark, const char *ifout, fake_size = params.fake_wg_size; bKnownProtocol = true; } + else if (IsDhtD1(data_payload,len_payload)) + { + DLOG("packet contains DHT d1...e\n") + fake = params.fake_dht; + fake_size = params.fake_dht_size; + bKnownProtocol = true; + } else { if (!params.desync_any_proto) return res; @@ -846,6 +855,37 @@ packet_process_result dpi_desync_udp_packet(uint32_t fwmark, const char *ifout, if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return res; return drop; + case DESYNC_TAMPER: + if (IsDhtD1(data_payload,len_payload)) + { + size_t szbuf,szcopy; + memcpy(pkt2,"d2:zz1:x",8); + pkt2_len=8; + szbuf=sizeof(pkt2)-pkt2_len; + szcopy=len_payload-1; + if (szcopy>szbuf) + { + DLOG("packet is too long to tamper"); + return res; + } + memcpy(pkt2+pkt2_len,data_payload+1,szcopy); + pkt2_len+=szcopy; + pkt1_len = sizeof(pkt1); + if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,fooling_orig, NULL, 0 , 0, pkt2, pkt2_len, pkt1, &pkt1_len)) + { + DLOG("could not construct packet with modified length. too large ?\n"); + return res; + } + DLOG("resending tampered DHT\n"); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + return res; + return drop; + } + else + { + DLOG("payload is not tamperable\n"); + return res; + } case DESYNC_IPFRAG2: { diff --git a/nfq/desync.h b/nfq/desync.h index 4725e62..798c418 100644 --- a/nfq/desync.h +++ b/nfq/desync.h @@ -34,7 +34,8 @@ enum dpi_desync_mode { DESYNC_HOPBYHOP, DESYNC_DESTOPT, DESYNC_IPFRAG1, - DESYNC_UDPLEN + DESYNC_UDPLEN, + DESYNC_TAMPER }; extern const char *fake_http_request_default; diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 664feb4..8da6397 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -522,7 +522,7 @@ static void exithelp() " --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 fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen\n" + " --dpi-desync=[,][,]\t; try to desync dpi state. modes : synack fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 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) @@ -547,6 +547,7 @@ static void exithelp() " --dpi-desync-fake-unknown=|0xHEX\t; file containing unknown protocol fake payload\n" " --dpi-desync-fake-quic=|0xHEX\t; file containing fake QUIC Initial\n" " --dpi-desync-fake-wireguard=|0xHEX\t; file containing fake wireguard handshake initiation\n" + " --dpi-desync-fake-dht=|0xHEX\t\t; file containing DHT protocol fake payload (d1...e)\n" " --dpi-desync-fake-unknown-udp=|0xHEX\t; file containing unknown udp protocol fake payload\n" " --dpi-desync-udplen-increment=\t\t; increase or decrease udp packet length by N bytes (default %u). negative values decrease length.\n" " --dpi-desync-udplen-pattern=|0xHEX\t; udp tail fill pattern\n" @@ -660,6 +661,7 @@ int main(int argc, char **argv) params.fake_quic_size = 620; // must be 601+ for TSPU hack params.fake_quic[0] = 0x40; // russian TSPU QUIC short header fake params.fake_wg_size = 64; + params.fake_dht_size = 64; params.fake_unknown_size = 256; params.fake_unknown_udp_size = 64; params.wscale=-1; // default - dont change scale factor (client) @@ -728,15 +730,16 @@ int main(int argc, char **argv) {"dpi-desync-fake-unknown",required_argument,0,0},// optidx=30 {"dpi-desync-fake-quic",required_argument,0,0},// optidx=31 {"dpi-desync-fake-wireguard",required_argument,0,0},// optidx=32 - {"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=33 - {"dpi-desync-udplen-increment",required_argument,0,0},// optidx=34 - {"dpi-desync-udplen-pattern",required_argument,0,0},// optidx=35 - {"dpi-desync-cutoff",required_argument,0,0},// optidx=36 - {"hostlist",required_argument,0,0}, // optidx=37 - {"hostlist-exclude",required_argument,0,0}, // optidx=38 + {"dpi-desync-fake-dht",required_argument,0,0},// optidx=33 + {"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=34 + {"dpi-desync-udplen-increment",required_argument,0,0},// optidx=35 + {"dpi-desync-udplen-pattern",required_argument,0,0},// optidx=36 + {"dpi-desync-cutoff",required_argument,0,0},// optidx=37 + {"hostlist",required_argument,0,0}, // optidx=38 + {"hostlist-exclude",required_argument,0,0}, // optidx=39 #ifdef __linux__ - {"bind-fix4",no_argument,0,0}, // optidx=39 - {"bind-fix6",no_argument,0,0}, // optidx=40 + {"bind-fix4",no_argument,0,0}, // optidx=40 + {"bind-fix6",no_argument,0,0}, // optidx=41 #endif {NULL,0,NULL,0} }; @@ -1022,18 +1025,22 @@ int main(int argc, char **argv) params.fake_wg_size = sizeof(params.fake_wg); load_file_or_exit(optarg,params.fake_wg,¶ms.fake_wg_size); break; - case 33: /* dpi-desync-fake-unknown-udp */ + case 33: /* dpi-desync-fake-dht */ + params.fake_dht_size = sizeof(params.fake_dht); + load_file_or_exit(optarg,params.fake_dht,¶ms.fake_dht_size); + break; + case 34: /* dpi-desync-fake-unknown-udp */ params.fake_unknown_udp_size = sizeof(params.fake_unknown_udp); load_file_or_exit(optarg,params.fake_unknown_udp,¶ms.fake_unknown_udp_size); break; - case 34: /* dpi-desync-udplen-increment */ + case 35: /* dpi-desync-udplen-increment */ if (sscanf(optarg,"%d",¶ms.udplen_increment)<1 || params.udplen_increment>0x7FFF || params.udplen_increment<-0x8000) { fprintf(stderr, "dpi-desync-udplen-increment must be integer within -32768..32767 range\n"); exit_clean(1); } break; - case 35: /* dpi-desync-udplen-pattern */ + case 36: /* dpi-desync-udplen-pattern */ { char buf[sizeof(params.udplen_pattern)]; size_t sz=sizeof(buf); @@ -1041,21 +1048,21 @@ int main(int argc, char **argv) fill_pattern(params.udplen_pattern,sizeof(params.udplen_pattern),buf,sz); } break; - case 36: /* desync-cutoff */ + case 37: /* desync-cutoff */ if (!parse_cutoff(optarg, ¶ms.desync_cutoff, ¶ms.desync_cutoff_mode)) { fprintf(stderr, "invalid desync-cutoff value\n"); exit_clean(1); } break; - case 37: /* hostlist */ + case 38: /* hostlist */ if (!strlist_add(¶ms.hostlist_files, optarg)) { fprintf(stderr, "strlist_add failed\n"); exit_clean(1); } break; - case 38: /* hostlist-exclude */ + case 39: /* hostlist-exclude */ if (!strlist_add(¶ms.hostlist_exclude_files, optarg)) { fprintf(stderr, "strlist_add failed\n"); @@ -1063,10 +1070,10 @@ int main(int argc, char **argv) } break; #ifdef __linux__ - case 39: /* bind-fix4 */ + case 40: /* bind-fix4 */ params.bind_fix4 = true; break; - case 40: /* bind-fix6 */ + case 41: /* bind-fix6 */ params.bind_fix6 = true; break; #endif diff --git a/nfq/params.h b/nfq/params.h index 9e307ae..b12741d 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -50,8 +50,8 @@ struct params_s uint8_t desync_fooling_mode; uint32_t desync_fwmark; // unused in BSD uint32_t desync_badseq_increment, desync_badseq_ack_increment; - uint8_t fake_http[1432],fake_tls[1432],fake_quic[1472],fake_wg[1472],fake_unknown[1432],fake_unknown_udp[1472], udplen_pattern[1472]; - size_t fake_http_size,fake_tls_size,fake_quic_size,fake_wg_size,fake_unknown_size,fake_unknown_udp_size; + uint8_t fake_http[1432],fake_tls[1432],fake_quic[1472],fake_wg[1472],fake_dht[1472],fake_unknown[1432],fake_unknown_udp[1472], udplen_pattern[1472]; + size_t fake_http_size,fake_tls_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_unknown_udp_size; int udplen_increment; bool droproot; uid_t uid; diff --git a/nfq/protocol.c b/nfq/protocol.c index 2782b37..c811d3b 100644 --- a/nfq/protocol.c +++ b/nfq/protocol.c @@ -192,7 +192,10 @@ bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len) { return len==148 && data[0]==1 && data[1]==0 && data[2]==0 && data[3]==0; } - +bool IsDhtD1(const uint8_t *data, size_t len) +{ + return len>=3 && data[0]=='d' && data[1]=='1' && data[len-1]=='e'; +} /* Returns the QUIC draft version or 0 if not applicable. */ uint8_t QUICDraftVersion(uint32_t version) diff --git a/nfq/protocol.h b/nfq/protocol.h index 0bb746a..808baa9 100644 --- a/nfq/protocol.h +++ b/nfq/protocol.h @@ -16,6 +16,7 @@ bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host); bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len); +bool IsDhtD1(const uint8_t *data, size_t len); #define QUIC_MAX_CID_LENGTH 20 typedef struct quic_cid {