diff --git a/docs/readme.en.md b/docs/readme.en.md index 22742bb7..2e07f1a3 100644 --- a/docs/readme.en.md +++ b/docs/readme.en.md @@ -197,7 +197,7 @@ nfqws takes the following parameters: --dpi-desync-fooling=[,] ; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2 --dpi-desync-repeats= ; send every desync packet N times --dpi-desync-skip-nosni=0|1 ; 1(default)=do not act on ClientHello without SNI (ESNI ?) - --dpi-desync-split-pos=N|-N|marker+N|marker-N ; comma separated list of split positions + --dpi-desync-split-pos=N|-N|marker+N|marker-N|@file ; comma separated list of split positions or @file (one position per line) ; markers: method,host,endhost,sld,endsld,midsld,sniext ; full list is only used by multisplit and multidisorder ; fakedsplit/fakeddisorder use first l7-protocol-compatible parameter if present, first abs value otherwise @@ -442,7 +442,8 @@ When splitting all markers are resolved to absolute offsets. If a relative posit In `multisplit`or `multidisorder` case split is cancelled if no position remained. -`fakedsplit` и `fakeddisorder` use only one split position. It's searched from the `--dpi-desync-split-pos` list by a special alorightm. +`fakedsplit` и `fakeddisorder` use only one split position. It's searched from the `--dpi-desync-split-pos` list by a special algorithm. +If split positions were loaded from `@file`, one position is randomly selected from that file list for each fake packet. First relative markers are searched. If no suitable found absolute markers are searched. If nothing found position 1 is used. For example, `--dpi-desync-split-pos=method+2,midsld,5` means `method+2` for http, `midsld` for TLS and 5 for others. diff --git a/docs/readme.md b/docs/readme.md index 378c4ccd..17d455a1 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -230,7 +230,7 @@ dvtws, собираемый из тех же исходников (см. [док --dpi-desync-fooling= ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack ts hopbyhop hopbyhop2 --dpi-desync-repeats= ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты) --dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI ---dpi-desync-split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации в режимах split и disorder +--dpi-desync-split-pos=N|-N|marker+N|marker-N|@file ; список через запятую маркеров для tcp сегментации или @file (одна позиция в строке) --dpi-desync-split-seqovl=N|-N|marker+N|marker-N ; единичный маркер, определяющий величину перекрытия sequence в режимах split и disorder. для split поддерживается только положительное число. --dpi-desync-split-seqovl-pattern=[+ofs]@|0xHEX ; чем заполнять фейковую часть overlap --dpi-desync-fakedsplit-pattern=[+ofs]@|0xHEX ; чем заполнять фейки в fakedsplit/fakeddisorder @@ -540,6 +540,7 @@ hex строка начинается с "0x". Имя файла можно пи В вариантах `multisplit` и `multidisorder` если не осталось ни одной позиции, разбиение не происходит. Варианты `fakedsplit` и `fakeddisorder` применяют только одну позицию сплита. Ее поиск среди списка `--dpi-desync-split-pos` осуществляется особым образом. +Если список задан через `@file`, для каждого формируемого фейка позиция выбирается случайно из строк этого файла. Сначала сверяются все относительные маркеры. Если среди них найден подходящий, применяется он. В противном случае сверяются все абсолютные маркеры. Если и среди них ничего не найдено, применяется позиция 1. diff --git a/nfq/desync.c b/nfq/desync.c index a9d35d0f..32e35526 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -1964,8 +1964,18 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint else if (dp->desync_mode == DESYNC_FAKEDSPLIT || dp->desync_mode == DESYNC_FAKEDDISORDER || dp->desync_mode2 == DESYNC_FAKEDSPLIT || dp->desync_mode2 == DESYNC_FAKEDDISORDER) { multisplit_count = 0; + split_pos = 0; + if (dp->split_random_count > 0) + { + int n = random() % dp->split_random_count; + for (i = 0, split_pos = 0; i < dp->split_random_count && !split_pos; i++) + { + int k = dp->split_random_start + (n + i) % dp->split_random_count; + split_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, dp->splits + k); + } + } // first look for non-abs split - for (i = 0, split_pos = 0; i < dp->split_count && !split_pos; i++) + for (i = 0; i < dp->split_count && !split_pos; i++) if (dp->splits[i].marker != PM_ABS) split_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, dp->splits + i); // second look for abs split diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 7a2eb463..9c4dea98 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -1198,6 +1198,42 @@ static bool parse_split_pos_list(char *opt, struct proto_pos *splits, int splits if (p) return false; // too much splits return true; } +static bool load_split_pos_file(const char *filename, struct proto_pos *splits, int splits_size, int *split_count) +{ + FILE *f = fopen(filename, "r"); + if (!f) + { + DLOG_PERROR("fopen split-pos file"); + return false; + } + char line[256], *p, *trim_pos; + int line_num = 0; + *split_count = 0; + while (fgets(line, sizeof(line), f)) + { + line_num++; + if (*split_count >= splits_size) + { + fclose(f); + return false; + } + if ((p = strchr(line, '\n'))) *p = 0; + if ((p = strchr(line, '\r'))) *p = 0; + for (p = line; *p == ' ' || *p == '\t'; p++); + if (!*p || *p == '#') continue; + trim_pos = p + strlen(p) - 1; + while (trim_pos > p && (*trim_pos == ' ' || *trim_pos == '\t')) *trim_pos-- = 0; + if (!parse_split_pos(p, splits + *split_count)) + { + DLOG_ERR("split-pos file %s line %d: invalid split pos: %s\n", filename, line_num, p); + fclose(f); + return false; + } + (*split_count)++; + } + fclose(f); + return *split_count > 0; +} static bool parse_domain_list(char *opt, hostlist_pool **pp) { @@ -2047,7 +2083,7 @@ static void exithelp(void) " --dpi-desync-fooling=[,]\t\t\t; can use multiple comma separated values. modes : none md5sig badseq badsum datanoack ts hopbyhop hopbyhop2\n" " --dpi-desync-repeats=\t\t\t\t; send every desync packet N times\n" " --dpi-desync-skip-nosni=0|1\t\t\t\t; 1(default)=do not act on ClientHello without SNI\n" - " --dpi-desync-split-pos=N|-N|marker+N|marker-N\t\t; comma separated list of split positions\n" + " --dpi-desync-split-pos=N|-N|marker+N|marker-N|@file\t; comma separated list of split positions or file (one position per line)\n" "\t\t\t\t\t\t\t; markers: method,host,endhost,sld,endsld,midsld,sniext\n" "\t\t\t\t\t\t\t; full list is only used by multisplit and multidisorder\n" "\t\t\t\t\t\t\t; fakedsplit/fakeddisorder use first l7-protocol-compatible parameter if present, first abs value otherwise\n" @@ -3037,7 +3073,17 @@ int main(int argc, char **argv) case IDX_DPI_DESYNC_SPLIT_POS: { int ct; - if (!parse_split_pos_list(optarg, dp->splits + dp->split_count, MAX_SPLITS - dp->split_count, &ct)) + if (*optarg == '@') + { + if (!load_split_pos_file(optarg + 1, dp->splits + dp->split_count, MAX_SPLITS - dp->split_count, &ct)) + { + DLOG_ERR("could not load split pos list from file or too much positions (before parsing - %u, max - %u) : %s\n", dp->split_count, MAX_SPLITS, optarg + 1); + exit_clean(1); + } + if (!dp->split_random_count) dp->split_random_start = dp->split_count; + dp->split_random_count += ct; + } + else 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); diff --git a/nfq/params.h b/nfq/params.h index 66391f4b..7d8c8abd 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -131,6 +131,7 @@ struct desync_profile // multisplit struct proto_pos splits[MAX_SPLITS]; int split_count; + int split_random_start, split_random_count; struct proto_pos seqovl,hostfakesplit_midhost; char dup_start_mode, dup_cutoff_mode; // n - packets, d - data packets, s - relative sequence