mirror of
https://github.com/bol-van/zapret.git
synced 2025-08-10 01:02:03 +03:00
Compare commits
21 Commits
v70.4
...
cbdee74e5f
Author | SHA1 | Date | |
---|---|---|---|
|
cbdee74e5f | ||
|
743eb5a4a2 | ||
|
4e8e3a9ed9 | ||
|
b9b91a0e68 | ||
|
9de7b66eef | ||
|
a2ffa3455d | ||
|
60b97dbed0 | ||
|
e56e4f5f35 | ||
|
5305ea83c8 | ||
|
14b3dd459b | ||
|
66fda2c33d | ||
|
77df43b9cb | ||
|
85f2b37c88 | ||
|
e2d600fcc6 | ||
|
37eda0ad98 | ||
|
770be21e1c | ||
|
1b880d42f9 | ||
|
6387315c0b | ||
|
3d4b395bfe | ||
|
55950ed7d0 | ||
|
f2b0341484 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ mdig/mdig
|
||||
nfq/dvtws
|
||||
nfq/nfqws
|
||||
nfq/winws.exe
|
||||
nfq/WinDivert*
|
||||
tpws/tpws
|
||||
binaries/my/
|
||||
ipset/zapret-ip*.txt
|
||||
|
@@ -1168,7 +1168,7 @@ pktws_curl_test_update_vary()
|
||||
[ "$sec" = 0 ] || proto=tls
|
||||
test_has_fake $desync && {
|
||||
zerofake="--dpi-desync-fake-$proto=0x00000000"
|
||||
[ "$sec" = 0 ] || tlsmod="--dpi-desync-fake-tls-mod=rnd,rndsni,padencap"
|
||||
[ "$sec" = 0 ] || tlsmod="--dpi-desync-fake-tls-mod=rnd,dupsid,rndsni,padencap"
|
||||
}
|
||||
if test_has_fakedsplit $desync ; then
|
||||
splits="method+2 midsld"
|
||||
|
@@ -466,3 +466,8 @@ nfqws,tpws: optional systemd notify support. compile using 'make systemd'
|
||||
nfqws,tpws: systemd instance templates for nfqws and tpws
|
||||
nfqws,tpws: separate droproot from dropcaps
|
||||
tpws: detect WSL 1 and warn about non-working options
|
||||
|
||||
v70.5
|
||||
|
||||
nfqws: multiple --dpi-desync-fake-xxx
|
||||
nfqws: support of inter-packet fragmented QUIC CRYPTO
|
||||
|
@@ -1,7 +1,7 @@
|
||||
debian,ubuntu :
|
||||
|
||||
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev
|
||||
make -C /opt/zapret
|
||||
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libsystemd-dev
|
||||
make -C /opt/zapret systemd
|
||||
|
||||
FreeBSD :
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# zapret v70.4
|
||||
# zapret v70.5
|
||||
|
||||
# SCAMMER WARNING
|
||||
|
||||
@@ -265,6 +265,12 @@ Fakes are separate generated by nfqws packets carrying false information for DPI
|
||||
|
||||
`--dpi-desync-fooling` takes multiple comma separated values.
|
||||
|
||||
|
||||
Multiple parameters `--dpi-desync-fake-???` are supported except for the `--dpi-desync-fake-syndata`.
|
||||
Fakes are sent in the specified order. `--dpi-desync-repeats` resends each fake.
|
||||
Resulting order would be : `fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....`
|
||||
|
||||
|
||||
### FAKE mods
|
||||
|
||||
**nfqws** has built-in TLS fake. It can be customized with `--dpi-desync-fake-tls` option.
|
||||
@@ -282,6 +288,10 @@ It's possible to use TLS Client Hello with any fingerprint and any SNI.
|
||||
By default if custom fake is not defined `rnd,rndsni,dupsid` mods are applied. If defined - `none`.
|
||||
This behaviour is compatible with previous versions with addition of `dupsid`.
|
||||
|
||||
If TLS mod is enabled and there're multiple TLS fakes, all valid TLS Client Hello fakes are modified.
|
||||
If there's no TLS Client Hello program exits with error.
|
||||
|
||||
|
||||
### TCP segmentation
|
||||
|
||||
* `multisplit`. split request at specified in `--dpi-desync-split-pos` positions
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# zapret v70.4
|
||||
# zapret v70.5
|
||||
|
||||
# ВНИМАНИЕ, остерегайтесь мошенников
|
||||
|
||||
@@ -320,6 +320,10 @@ dvtws, собираемый из тех же исходников (см. [док
|
||||
|
||||
Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую.
|
||||
|
||||
Возможно задание множества фейков через повторение парамеров `--dpi-desync-fake-???`, кроме `--dpi-desync-fake-syndata`.
|
||||
Фейки будут отосланы в указанном порядке. `--dpi-desync-repeats` повторяет каждый отосланный фейк.
|
||||
Итоговый порядок будет такой : `fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....`
|
||||
|
||||
### МОДИФИКАЦИЯ ФЕЙКОВ
|
||||
|
||||
В nfqws зашит базовый вариант фейка для TLS. Его можно переопределить опцией `--dpi-desync-fake-tls`.
|
||||
@@ -339,6 +343,9 @@ dvtws, собираемый из тех же исходников (см. [док
|
||||
По умолчанию если не задан собственный фейк для TLS используются модификации `rnd,rndsni,dupsid`. Если фейк задан, используется `none`.
|
||||
Это соответствует поведению программы более старых версий с добавлением функции `dupsid`.
|
||||
|
||||
Если задан режим модификации и имеется множество TLS фейков, модифицируются все фейки, являющиеся TLS Client Hello.
|
||||
Если нет ни одного TLS Client Hello фейка, программа завершается с ошибкой.
|
||||
|
||||
### TCP СЕГМЕНТАЦИЯ
|
||||
|
||||
* `multisplit`. нарезаем запрос на указанных в `--dpi-desync-split-pos` позициях.
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
312
nfq/desync.c
312
nfq/desync.c
@@ -66,6 +66,9 @@ const uint8_t fake_tls_clienthello_default[648] = {
|
||||
#define PKTDATA_MAXDUMP 32
|
||||
#define IP_MAXDUMP 80
|
||||
|
||||
#define TCP_MAX_REASM 16384
|
||||
#define UDP_MAX_REASM 16384
|
||||
|
||||
bool desync_valid_zero_stage(enum dpi_desync_mode mode)
|
||||
{
|
||||
return mode==DESYNC_SYNACK || mode==DESYNC_SYNDATA;
|
||||
@@ -606,45 +609,48 @@ static uint16_t IP4_IP_ID_FIX(const struct ip *ip)
|
||||
// fake_mod buffer must at least sizeof(desync_profile->fake_tls)
|
||||
// size does not change
|
||||
// return : true - altered, false - not altered
|
||||
static bool runtime_tls_mod(const struct desync_profile *dp, uint8_t *fake_mod, const uint8_t *payload, size_t payload_len)
|
||||
static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache, uint8_t fake_tls_mod, const uint8_t *fake_data, size_t fake_data_size, const uint8_t *payload, size_t payload_len, uint8_t *fake_mod)
|
||||
{
|
||||
bool b=false;
|
||||
if (dp->fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
|
||||
if (modcache) // it's filled only if it's TLS
|
||||
{
|
||||
size_t sz_rec = pntoh16(dp->fake_tls+3) + payload_len;
|
||||
size_t sz_handshake = pntoh24(dp->fake_tls+6) + payload_len;
|
||||
size_t sz_ext = pntoh16(dp->fake_tls+dp->fake_tls_extlen_offset) + payload_len;
|
||||
size_t sz_pad = pntoh16(dp->fake_tls+dp->fake_tls_padlen_offset) + payload_len;
|
||||
if ((sz_rec & ~0xFFFF) || (sz_handshake & ~0xFFFFFF) || (sz_ext & ~0xFFFF) || (sz_pad & ~0xFFFF))
|
||||
DLOG("cannot apply padencap tls mod. length overflow.\n");
|
||||
else
|
||||
if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
|
||||
{
|
||||
memcpy(fake_mod,dp->fake_tls,dp->fake_tls_size);
|
||||
phton16(fake_mod+3,(uint16_t)sz_rec);
|
||||
phton24(fake_mod+6,(uint32_t)sz_handshake);
|
||||
phton16(fake_mod+dp->fake_tls_extlen_offset,(uint16_t)sz_ext);
|
||||
phton16(fake_mod+dp->fake_tls_padlen_offset,(uint16_t)sz_pad);
|
||||
size_t sz_rec = pntoh16(fake_data+3) + payload_len;
|
||||
size_t sz_handshake = pntoh24(fake_data+6) + payload_len;
|
||||
size_t sz_ext = pntoh16(fake_data+modcache->extlen_offset) + payload_len;
|
||||
size_t sz_pad = pntoh16(fake_data+modcache->padlen_offset) + payload_len;
|
||||
if ((sz_rec & ~0xFFFF) || (sz_handshake & ~0xFFFFFF) || (sz_ext & ~0xFFFF) || (sz_pad & ~0xFFFF))
|
||||
DLOG("fake[%d] cannot apply padencap tls mod. length overflow.\n", fake_n);
|
||||
else
|
||||
{
|
||||
memcpy(fake_mod,fake_data,fake_data_size);
|
||||
phton16(fake_mod+3,(uint16_t)sz_rec);
|
||||
phton24(fake_mod+6,(uint32_t)sz_handshake);
|
||||
phton16(fake_mod+modcache->extlen_offset,(uint16_t)sz_ext);
|
||||
phton16(fake_mod+modcache->padlen_offset,(uint16_t)sz_pad);
|
||||
b=true;
|
||||
}
|
||||
}
|
||||
if (fake_tls_mod & FAKE_TLS_MOD_RND)
|
||||
{
|
||||
if (!b) memcpy(fake_mod,fake_data,fake_data_size);
|
||||
fill_random_bytes(fake_mod+11,32); // random
|
||||
fill_random_bytes(fake_mod+44,fake_mod[43]); // session id
|
||||
b=true;
|
||||
}
|
||||
}
|
||||
if (dp->fake_tls_mod & FAKE_TLS_MOD_RND)
|
||||
{
|
||||
if (!b) memcpy(fake_mod,dp->fake_tls,dp->fake_tls_size);
|
||||
fill_random_bytes(fake_mod+11,32); // random
|
||||
fill_random_bytes(fake_mod+44,fake_mod[43]); // session id
|
||||
b=true;
|
||||
}
|
||||
if (dp->fake_tls_mod & FAKE_TLS_MOD_DUP_SID)
|
||||
{
|
||||
if (dp->fake_tls[43]!=payload[43])
|
||||
DLOG("cannot apply dupsid tls mod. fake and orig session id length mismatch.\n");
|
||||
else if (payload_len<(44+payload[43]))
|
||||
DLOG("cannot apply dupsid tls mod. data payload is not valid.\n");
|
||||
else
|
||||
if (fake_tls_mod & FAKE_TLS_MOD_DUP_SID)
|
||||
{
|
||||
if (!b) memcpy(fake_mod,dp->fake_tls,dp->fake_tls_size);
|
||||
memcpy(fake_mod+44,payload+44,fake_mod[43]); // session id
|
||||
b=true;
|
||||
if (fake_data[43]!=payload[43])
|
||||
DLOG("fake[%d] cannot apply dupsid tls mod. fake and orig session id length mismatch.\n",fake_n);
|
||||
else if (payload_len<(44+payload[43]))
|
||||
DLOG("fake[%d] cannot apply dupsid tls mod. data payload is not valid.\n",fake_n);
|
||||
else
|
||||
{
|
||||
if (!b) memcpy(fake_mod,fake_data,fake_data_size);
|
||||
memcpy(fake_mod+44,payload+44,fake_mod[43]); // session id
|
||||
b=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return b;
|
||||
@@ -880,8 +886,8 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
|
||||
if (!(dis->tcp->th_flags & TH_SYN) && dis->len_payload)
|
||||
{
|
||||
const uint8_t *fake;
|
||||
size_t fake_size;
|
||||
struct blob_collection_head *fake;
|
||||
|
||||
char host[256];
|
||||
bool bHaveHost=false;
|
||||
uint8_t *p, *phost=NULL;
|
||||
@@ -893,7 +899,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
int i;
|
||||
uint16_t ip_id;
|
||||
t_l7proto l7proto = UNKNOWN;
|
||||
uint8_t fake_mod[sizeof(dp->fake_tls)];
|
||||
|
||||
if (replay)
|
||||
{
|
||||
@@ -952,7 +957,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
!(ctrack->req_seq_finalized && seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
|
||||
{
|
||||
// do not reconstruct unexpected large payload (they are feeding garbage ?)
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_TCP,TLSRecordLen(dis->data_payload),16384,dis->data_payload,dis->len_payload))
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_TCP,TLSRecordLen(dis->data_payload),TCP_MAX_REASM,dis->data_payload,dis->len_payload))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
@@ -1183,16 +1188,13 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
switch(l7proto)
|
||||
{
|
||||
case HTTP:
|
||||
fake = dp->fake_http;
|
||||
fake_size = dp->fake_http_size;
|
||||
fake = &dp->fake_http;
|
||||
break;
|
||||
case TLS:
|
||||
fake = runtime_tls_mod(dp,fake_mod,rdata_payload,rlen_payload) ? fake_mod : dp->fake_tls;
|
||||
fake_size = dp->fake_tls_size;
|
||||
fake = &dp->fake_tls;
|
||||
break;
|
||||
default:
|
||||
fake = dp->fake_unknown;
|
||||
fake_size = dp->fake_unknown_size;
|
||||
fake = &dp->fake_unknown;
|
||||
break;
|
||||
}
|
||||
if (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)
|
||||
@@ -1273,13 +1275,8 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
else
|
||||
seqovl_pos = 0;
|
||||
|
||||
// we do not need reasm buffer anymore
|
||||
reasm_orig_cancel(ctrack);
|
||||
rdata_payload=NULL;
|
||||
|
||||
uint32_t fooling_orig = FOOL_NONE;
|
||||
bool bFake = false;
|
||||
pkt1_len = sizeof(pkt1);
|
||||
switch(dp->desync_mode)
|
||||
{
|
||||
case DESYNC_FAKE_KNOWN:
|
||||
@@ -1291,28 +1288,69 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
}
|
||||
case DESYNC_FAKE:
|
||||
if (reasm_offset) break;
|
||||
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_fake,IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
|
||||
dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
|
||||
fake, fake_size, pkt1, &pkt1_len))
|
||||
|
||||
{
|
||||
return verdict;
|
||||
struct blob_item *fake_item;
|
||||
uint8_t *fake_data;
|
||||
uint8_t fake_data_buf[FAKE_MAX_TCP];
|
||||
int n=0;
|
||||
|
||||
ip_id = IP4_IP_ID_FIX(dis->ip);
|
||||
|
||||
LIST_FOREACH(fake_item, fake, next)
|
||||
{
|
||||
n++;
|
||||
switch(l7proto)
|
||||
{
|
||||
case TLS:
|
||||
if ((fake_item->size <= sizeof(fake_data_buf)) &&
|
||||
runtime_tls_mod(n,(struct fake_tls_mod_cache *)fake_item->extra, dp->fake_tls_mod, fake_item->data, fake_item->size, rdata_payload, rlen_payload, fake_data_buf))
|
||||
{
|
||||
fake_data = fake_data_buf;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fake_data = fake_item->data;
|
||||
}
|
||||
pkt1_len = sizeof(pkt1);
|
||||
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_fake,IP4_TOS(dis->ip),ip_id,IP6_FLOW(dis->ip6),
|
||||
dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
|
||||
fake_data, fake_item->size, pkt1, &pkt1_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
DLOG("sending fake[%d] : ", n);
|
||||
hexdump_limited_dlog(fake_data,fake_item->size,PKTDATA_MAXDUMP); DLOG("\n");
|
||||
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
ip_id=IP4_IP_ID_NEXT(ip_id);
|
||||
}
|
||||
}
|
||||
DLOG("sending fake : ");
|
||||
hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n");
|
||||
bFake = true;
|
||||
break;
|
||||
case DESYNC_RST:
|
||||
case DESYNC_RSTACK:
|
||||
if (reasm_offset) break;
|
||||
pkt1_len = sizeof(pkt1);
|
||||
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),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
|
||||
dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
|
||||
NULL, 0, pkt1, &pkt1_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
DLOG("sending fake RST/RSTACK\n");
|
||||
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
bFake = true;
|
||||
break;
|
||||
case DESYNC_HOPBYHOP:
|
||||
@@ -1323,8 +1361,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
(!split_pos && (dp->desync_mode2==DESYNC_FAKEDSPLIT || dp->desync_mode2==DESYNC_FAKEDDISORDER)) ||
|
||||
(!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER))))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
rdata_payload=NULL;
|
||||
|
||||
pkt1_len = sizeof(pkt1);
|
||||
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),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
|
||||
ttl_orig,0,0,IP6_FLOW(dis->ip6),
|
||||
fooling_orig,0,0,
|
||||
dis->data_payload, dis->len_payload, pkt1, &pkt1_len))
|
||||
{
|
||||
@@ -1341,11 +1383,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
break;
|
||||
}
|
||||
|
||||
if (bFake)
|
||||
{
|
||||
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
|
||||
return verdict;
|
||||
}
|
||||
// we do not need reasm buffer anymore
|
||||
reasm_orig_cancel(ctrack);
|
||||
rdata_payload=NULL;
|
||||
|
||||
enum dpi_desync_mode desync_mode = dp->desync_mode2==DESYNC_NONE ? dp->desync_mode : dp->desync_mode2;
|
||||
switch(desync_mode)
|
||||
@@ -1875,8 +1915,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
|
||||
if (dis->len_payload)
|
||||
{
|
||||
const uint8_t *fake;
|
||||
size_t fake_size;
|
||||
struct blob_collection_head *fake;
|
||||
char host[256];
|
||||
bool bHaveHost=false;
|
||||
uint16_t ip_id;
|
||||
@@ -1917,29 +1956,82 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
return verdict; // cannot be first packet
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t defrag[16384];
|
||||
uint8_t defrag[UDP_MAX_REASM];
|
||||
size_t hello_offset, hello_len, defrag_len = sizeof(defrag);
|
||||
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len))
|
||||
bool bFull;
|
||||
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len,&bFull))
|
||||
{
|
||||
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
|
||||
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
|
||||
|
||||
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
|
||||
|
||||
if (ctrack)
|
||||
if (bFull)
|
||||
{
|
||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
DLOG("QUIC initial contains CRYPTO with full fragment coverage\n");
|
||||
|
||||
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
|
||||
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
|
||||
|
||||
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
|
||||
|
||||
if (ctrack)
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
|
||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,UDP_MAX_REASM,UDP_MAX_REASM,clean,clean_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
||||
{
|
||||
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG_ERR("rawpacket_queue failed !\n");
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
if (bReqFull)
|
||||
{
|
||||
replay_queue(&ctrack->delayed);
|
||||
reasm_orig_fin(ctrack);
|
||||
}
|
||||
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsHello)
|
||||
{
|
||||
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
|
||||
if (!bHaveHost && dp->desync_skip_nosni)
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
else
|
||||
{
|
||||
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG("QUIC initial contains CRYPTO with partial fragment coverage\n");
|
||||
if (ctrack)
|
||||
{
|
||||
if (ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,UDP_MAX_REASM,UDP_MAX_REASM,clean,clean_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
||||
{
|
||||
@@ -1951,28 +2043,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
if (bReqFull)
|
||||
{
|
||||
replay_queue(&ctrack->delayed);
|
||||
reasm_orig_fin(ctrack);
|
||||
}
|
||||
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsHello)
|
||||
{
|
||||
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
|
||||
if (!bHaveHost && dp->desync_skip_nosni)
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
|
||||
if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1991,7 +2064,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
{
|
||||
// received payload without host. it means we are out of the request retransmission phase. stop counter
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
|
||||
|
||||
reasm_orig_cancel(ctrack);
|
||||
|
||||
if (IsWireguardHandshakeInitiation(dis->data_payload,dis->len_payload))
|
||||
@@ -2110,20 +2183,16 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
switch(l7proto)
|
||||
{
|
||||
case QUIC:
|
||||
fake = dp->fake_quic;
|
||||
fake_size = dp->fake_quic_size;
|
||||
fake = &dp->fake_quic;
|
||||
break;
|
||||
case WIREGUARD:
|
||||
fake = dp->fake_wg;
|
||||
fake_size = dp->fake_wg_size;
|
||||
fake = &dp->fake_wg;
|
||||
break;
|
||||
case DHT:
|
||||
fake = dp->fake_dht;
|
||||
fake_size = dp->fake_dht_size;
|
||||
fake = &dp->fake_dht;
|
||||
break;
|
||||
default:
|
||||
fake = dp->fake_unknown_udp;
|
||||
fake_size = dp->fake_unknown_udp_size;
|
||||
fake = &dp->fake_unknown_udp;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2140,7 +2209,6 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
}
|
||||
|
||||
bool bFake = false;
|
||||
pkt1_len = sizeof(pkt1);
|
||||
switch(dp->desync_mode)
|
||||
{
|
||||
case DESYNC_FAKE_KNOWN:
|
||||
@@ -2150,12 +2218,30 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
break;
|
||||
}
|
||||
case DESYNC_FAKE:
|
||||
if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len))
|
||||
return verdict;
|
||||
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;
|
||||
{
|
||||
struct blob_item *fake_item;
|
||||
int n=0;
|
||||
|
||||
ip_id = IP4_IP_ID_FIX(dis->ip);
|
||||
|
||||
LIST_FOREACH(fake_item, fake, next)
|
||||
{
|
||||
n++;
|
||||
pkt1_len = sizeof(pkt1);
|
||||
if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst,
|
||||
ttl_fake, IP4_TOS(dis->ip),ip_id,IP6_FLOW(dis->ip6),
|
||||
dp->desync_fooling_mode, NULL, 0, 0,
|
||||
fake_item->data, fake_item->size, pkt1, &pkt1_len))
|
||||
{
|
||||
return verdict;
|
||||
}
|
||||
DLOG("sending fake[%d] : ", n);
|
||||
hexdump_limited_dlog(fake_item->data,fake_item->size,PKTDATA_MAXDUMP); DLOG("\n");
|
||||
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
|
||||
return verdict;
|
||||
ip_id=IP4_IP_ID_NEXT(ip_id);
|
||||
}
|
||||
}
|
||||
bFake = true;
|
||||
break;
|
||||
case DESYNC_HOPBYHOP:
|
||||
@@ -2164,9 +2250,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
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_udp(dp->desync_mode2)))
|
||||
{
|
||||
pkt1_len = sizeof(pkt1);
|
||||
if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst,
|
||||
ttl_orig,IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
|
||||
fooling_orig,NULL,0,0,
|
||||
ttl_orig,0,0,IP6_FLOW(dis->ip6),fooling_orig,NULL,0,0,
|
||||
dis->data_payload, dis->len_payload, pkt1, &pkt1_len))
|
||||
{
|
||||
return verdict;
|
||||
|
166
nfq/nfqws.c
166
nfq/nfqws.c
@@ -1007,74 +1007,67 @@ static void SplitDebug(void)
|
||||
}
|
||||
|
||||
static const char * tld[]={"com","org","net","edu","gov","biz"};
|
||||
static void onetime_tls_mod(struct desync_profile *dp)
|
||||
|
||||
static bool onetime_tls_mod_blob(int profile_n, int fake_n, uint8_t fake_tls_mod, uint8_t *fake_tls, size_t *fake_tls_size, size_t fake_tls_buf_size, struct fake_tls_mod_cache *modcache)
|
||||
{
|
||||
const uint8_t *ext;
|
||||
size_t extlen, slen;
|
||||
|
||||
if (dp->n && !(dp->fake_tls_mod & (FAKE_TLS_MOD_SET|FAKE_TLS_MOD_CUSTOM_FAKE)))
|
||||
dp->fake_tls_mod |= FAKE_TLS_MOD_RND|FAKE_TLS_MOD_RND_SNI|FAKE_TLS_MOD_DUP_SID; // old behavior compat + dup_sid
|
||||
if (!(dp->fake_tls_mod & ~FAKE_TLS_MOD_SAVE_MASK))
|
||||
return; // nothing to do
|
||||
if (!IsTLSClientHello(dp->fake_tls,dp->fake_tls_size,false) || (dp->fake_tls_size<(44+dp->fake_tls[43]))) // has session id ?
|
||||
modcache->extlen_offset = modcache->padlen_offset = 0;
|
||||
if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
|
||||
{
|
||||
DLOG_ERR("profile %d tls mod set but tls fake structure invalid\n", dp->n);
|
||||
exit_clean(1);
|
||||
}
|
||||
if (dp->fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
|
||||
{
|
||||
if (!TLSFindExtLen(dp->fake_tls,dp->fake_tls_size,&dp->fake_tls_extlen_offset))
|
||||
if (!TLSFindExtLen(fake_tls,*fake_tls_size,&modcache->extlen_offset))
|
||||
{
|
||||
DLOG_ERR("profile %d padencap set but tls fake structure invalid\n", dp->n);
|
||||
exit_clean(1);
|
||||
DLOG_ERR("profile %d fake[%d] padencap set but tls fake structure invalid\n", profile_n, fake_n);
|
||||
return false;
|
||||
}
|
||||
DLOG("profile %d fake tls extensions length offset : %zu\n", dp->n, dp->fake_tls_extlen_offset);
|
||||
if (TLSFindExt(dp->fake_tls,dp->fake_tls_size,21,&ext,&extlen,false))
|
||||
DLOG("profile %d fake[%d] tls extensions length offset : %zu\n", profile_n, fake_n, modcache->extlen_offset);
|
||||
if (TLSFindExt(fake_tls,*fake_tls_size,21,&ext,&extlen,false))
|
||||
{
|
||||
if ((ext-dp->fake_tls+extlen)!=dp->fake_tls_size)
|
||||
if ((ext-fake_tls+extlen)!=*fake_tls_size)
|
||||
{
|
||||
DLOG_ERR("profile %d fake tls padding ext is present but it's not at the end. padding ext offset %zu, padding ext size %zu, fake size %zu\n", dp->n, ext-dp->fake_tls, extlen, dp->fake_tls_size);
|
||||
exit_clean(1);
|
||||
DLOG_ERR("profile %d fake[%d] tls padding ext is present but it's not at the end. padding ext offset %zu, padding ext size %zu, fake size %zu\n", profile_n, fake_n, ext-fake_tls, extlen, *fake_tls_size);
|
||||
return false;
|
||||
}
|
||||
dp->fake_tls_padlen_offset = ext-dp->fake_tls-2;
|
||||
DLOG("profile %d fake tls padding ext is present, padding length offset %zu\n", dp->n, dp->fake_tls_padlen_offset);
|
||||
modcache->padlen_offset = ext-fake_tls-2;
|
||||
DLOG("profile %d fake[%d] tls padding ext is present, padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((dp->fake_tls_size+4)>sizeof(dp->fake_tls))
|
||||
if ((*fake_tls_size+4)>fake_tls_buf_size)
|
||||
{
|
||||
DLOG_ERR("profile %d fake tls padding is absent and there's not space to add it\n", dp->n);
|
||||
exit_clean(1);
|
||||
DLOG_ERR("profile %d fake[%d] tls padding is absent and there's no space to add it\n", profile_n, fake_n);
|
||||
return false;
|
||||
}
|
||||
phton16(dp->fake_tls+dp->fake_tls_size,21);
|
||||
dp->fake_tls_size+=2;
|
||||
dp->fake_tls_padlen_offset=dp->fake_tls_size;
|
||||
phton16(dp->fake_tls+dp->fake_tls_size,0);
|
||||
dp->fake_tls_size+=2;
|
||||
phton16(dp->fake_tls+dp->fake_tls_extlen_offset,pntoh16(dp->fake_tls+dp->fake_tls_extlen_offset)+4);
|
||||
phton16(dp->fake_tls+3,pntoh16(dp->fake_tls+3)+4); // increase tls record len
|
||||
phton24(dp->fake_tls+6,pntoh24(dp->fake_tls+6)+4); // increase tls handshake len
|
||||
DLOG("profile %d fake tls padding is absent. added. padding ledgth offset %zu\n", dp->n, dp->fake_tls_padlen_offset);
|
||||
phton16(fake_tls+*fake_tls_size,21);
|
||||
*fake_tls_size+=2;
|
||||
modcache->padlen_offset=*fake_tls_size;
|
||||
phton16(fake_tls+*fake_tls_size,0);
|
||||
*fake_tls_size+=2;
|
||||
phton16(fake_tls+modcache->extlen_offset,pntoh16(fake_tls+modcache->extlen_offset)+4);
|
||||
phton16(fake_tls+3,pntoh16(fake_tls+3)+4); // increase tls record len
|
||||
phton24(fake_tls+6,pntoh24(fake_tls+6)+4); // increase tls handshake len
|
||||
DLOG("profile %d fake[%d] tls padding is absent. added. padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
|
||||
}
|
||||
}
|
||||
if (dp->fake_tls_mod & FAKE_TLS_MOD_RND_SNI)
|
||||
if (fake_tls_mod & FAKE_TLS_MOD_RND_SNI)
|
||||
{
|
||||
if (!TLSFindExt(dp->fake_tls,dp->fake_tls_size,0,&ext,&extlen,false))
|
||||
if (!TLSFindExt(fake_tls,*fake_tls_size,0,&ext,&extlen,false))
|
||||
{
|
||||
DLOG_ERR("profile %d rndsni set but tls fake does not have SNI\n", dp->n);
|
||||
exit_clean(1);
|
||||
DLOG_ERR("profile %d fake[%d] rndsni set but tls fake does not have SNI\n", profile_n, fake_n);
|
||||
return false;
|
||||
}
|
||||
if (!TLSAdvanceToHostInSNI(&ext,&extlen,&slen))
|
||||
{
|
||||
DLOG_ERR("profile %d rndsni set but tls fake has invalid SNI structure\n", dp->n);
|
||||
exit_clean(1);
|
||||
DLOG_ERR("profile %d fake[%d] rndsni set but tls fake has invalid SNI structure\n", profile_n, fake_n);
|
||||
return false;
|
||||
}
|
||||
if (!slen)
|
||||
{
|
||||
DLOG_ERR("profile %d rndsni set but tls fake has zero sized SNI\n", dp->n);
|
||||
exit_clean(1);
|
||||
DLOG_ERR("profile %d fake[%d] rndsni set but tls fake has zero sized SNI\n", profile_n, fake_n);
|
||||
return false;
|
||||
}
|
||||
uint8_t *sni = dp->fake_tls + (ext - dp->fake_tls);
|
||||
uint8_t *sni = fake_tls + (ext - fake_tls);
|
||||
|
||||
char *s1=NULL, *s2=NULL;
|
||||
if (params.debug)
|
||||
@@ -1100,11 +1093,66 @@ static void onetime_tls_mod(struct desync_profile *dp)
|
||||
if (s1 && (s2 = malloc(slen+1)))
|
||||
{
|
||||
memcpy(s2,sni,slen); s2[slen]=0;
|
||||
DLOG("profile %d generated random SNI : %s -> %s\n",dp->n,s1,s2);
|
||||
DLOG("profile %d fake[%d] generated random SNI : %s -> %s\n",profile_n,fake_n,s1,s2);
|
||||
}
|
||||
free(s1); free(s2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool onetime_tls_mod(struct desync_profile *dp)
|
||||
{
|
||||
if (dp->n && !(dp->fake_tls_mod & (FAKE_TLS_MOD_SET|FAKE_TLS_MOD_CUSTOM_FAKE)))
|
||||
dp->fake_tls_mod |= FAKE_TLS_MOD_RND|FAKE_TLS_MOD_RND_SNI|FAKE_TLS_MOD_DUP_SID; // old behavior compat + dup_sid
|
||||
if (!(dp->fake_tls_mod & ~FAKE_TLS_MOD_SAVE_MASK))
|
||||
return true; // nothing to do
|
||||
|
||||
struct blob_item *fake_tls;
|
||||
int n=0;
|
||||
bool bMod=false;
|
||||
|
||||
LIST_FOREACH(fake_tls, &dp->fake_tls, next)
|
||||
{
|
||||
++n;
|
||||
if (!IsTLSClientHello(fake_tls->data,fake_tls->size,false) || (fake_tls->size < (44+fake_tls->data[43]))) // has session id ?
|
||||
{
|
||||
DLOG("profile %d fake[%d] tls mod set but tls fake structure invalid. mod skipped.\n", dp->n, n);
|
||||
continue;
|
||||
|
||||
}
|
||||
bMod = true;
|
||||
if (!fake_tls->extra)
|
||||
{
|
||||
fake_tls->extra = malloc(sizeof(struct fake_tls_mod_cache));
|
||||
if (!fake_tls->extra) return false;
|
||||
}
|
||||
if (!onetime_tls_mod_blob(dp->n,n,dp->fake_tls_mod,fake_tls->data,&fake_tls->size,fake_tls->size_buf,(struct fake_tls_mod_cache*)fake_tls->extra))
|
||||
return false;
|
||||
}
|
||||
if (!bMod)
|
||||
DLOG_ERR("profile %d tls fake list does not have any valid TLS ClientHello\n", dp->n);
|
||||
return bMod;
|
||||
}
|
||||
|
||||
static void load_blob_to_collection(const char *filename, struct blob_collection_head *blobs, size_t max_size, size_t size_reserve)
|
||||
{
|
||||
struct blob_item *blob = blob_collection_add(blobs);
|
||||
uint8_t *p;
|
||||
if (!blob || (!(blob->data = malloc(max_size+size_reserve))))
|
||||
{
|
||||
DLOG_ERR("out of memory\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
blob->size = max_size;
|
||||
load_file_or_exit(filename,blob->data,&blob->size);
|
||||
p = realloc(blob->data,blob->size+size_reserve);
|
||||
if (!p)
|
||||
{
|
||||
DLOG_ERR("out of memory\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
blob->data = p;
|
||||
blob->size_buf = blob->size+size_reserve;
|
||||
}
|
||||
|
||||
|
||||
@@ -1992,12 +2040,10 @@ int main(int argc, char **argv)
|
||||
dp->desync_any_proto = !optarg || atoi(optarg);
|
||||
break;
|
||||
case 38: /* dpi-desync-fake-http */
|
||||
dp->fake_http_size = sizeof(dp->fake_http);
|
||||
load_file_or_exit(optarg,dp->fake_http,&dp->fake_http_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_http, FAKE_MAX_TCP,0);
|
||||
break;
|
||||
case 39: /* dpi-desync-fake-tls */
|
||||
dp->fake_tls_size = sizeof(dp->fake_tls);
|
||||
load_file_or_exit(optarg,dp->fake_tls,&dp->fake_tls_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_tls, FAKE_MAX_TCP,4);
|
||||
dp->fake_tls_mod |= FAKE_TLS_MOD_CUSTOM_FAKE;
|
||||
break;
|
||||
case 40: /* dpi-desync-fake-tls-mod */
|
||||
@@ -2008,28 +2054,23 @@ int main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 41: /* dpi-desync-fake-unknown */
|
||||
dp->fake_unknown_size = sizeof(dp->fake_unknown);
|
||||
load_file_or_exit(optarg,dp->fake_unknown,&dp->fake_unknown_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_unknown, FAKE_MAX_TCP, 0);
|
||||
break;
|
||||
case 42: /* dpi-desync-fake-syndata */
|
||||
dp->fake_syndata_size = sizeof(dp->fake_syndata);
|
||||
load_file_or_exit(optarg,dp->fake_syndata,&dp->fake_syndata_size);
|
||||
break;
|
||||
case 43: /* dpi-desync-fake-quic */
|
||||
dp->fake_quic_size = sizeof(dp->fake_quic);
|
||||
load_file_or_exit(optarg,dp->fake_quic,&dp->fake_quic_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_quic, FAKE_MAX_UDP, 0);
|
||||
break;
|
||||
case 44: /* dpi-desync-fake-wireguard */
|
||||
dp->fake_wg_size = sizeof(dp->fake_wg);
|
||||
load_file_or_exit(optarg,dp->fake_wg,&dp->fake_wg_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_wg, FAKE_MAX_UDP, 0);
|
||||
break;
|
||||
case 45: /* dpi-desync-fake-dht */
|
||||
dp->fake_dht_size = sizeof(dp->fake_dht);
|
||||
load_file_or_exit(optarg,dp->fake_dht,&dp->fake_dht_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_dht, FAKE_MAX_UDP, 0);
|
||||
break;
|
||||
case 46: /* dpi-desync-fake-unknown-udp */
|
||||
dp->fake_unknown_udp_size = sizeof(dp->fake_unknown_udp);
|
||||
load_file_or_exit(optarg,dp->fake_unknown_udp,&dp->fake_unknown_udp_size);
|
||||
load_blob_to_collection(optarg, &dp->fake_unknown_udp, FAKE_MAX_UDP, 0);
|
||||
break;
|
||||
case 47: /* dpi-desync-udplen-increment */
|
||||
if (sscanf(optarg,"%d",&dp->udplen_increment)<1 || dp->udplen_increment>0x7FFF || dp->udplen_increment<-0x8000)
|
||||
@@ -2475,7 +2516,16 @@ int main(int argc, char **argv)
|
||||
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);
|
||||
split_compat(dp);
|
||||
onetime_tls_mod(dp);
|
||||
if (!dp_fake_defaults(dp))
|
||||
{
|
||||
DLOG_ERR("could not fill fake defaults\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
if (!onetime_tls_mod(dp))
|
||||
{
|
||||
DLOG_ERR("could not mod tls\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
#ifndef __CYGWIN__
|
||||
if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1))
|
||||
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
|
||||
|
64
nfq/params.c
64
nfq/params.c
@@ -65,6 +65,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
|
||||
{
|
||||
va_copy(args2,args);
|
||||
DLOG_CON(format,syslog_priority,args2);
|
||||
va_end(args2);
|
||||
}
|
||||
if (params.debug)
|
||||
{
|
||||
@@ -184,18 +185,8 @@ void dp_init(struct desync_profile *dp)
|
||||
dp->desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT;
|
||||
dp->desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT;
|
||||
dp->desync_repeats = 1;
|
||||
dp->fake_tls_size = sizeof(fake_tls_clienthello_default);
|
||||
memcpy(dp->fake_tls,fake_tls_clienthello_default,dp->fake_tls_size);
|
||||
dp->fake_tls_mod = 0;
|
||||
dp->fake_http_size = strlen(fake_http_request_default);
|
||||
memcpy(dp->fake_http,fake_http_request_default,dp->fake_http_size);
|
||||
dp->fake_quic_size = 620; // must be 601+ for TSPU hack
|
||||
dp->fake_quic[0] = 0x40; // russian TSPU QUIC short header fake
|
||||
dp->fake_wg_size = 64;
|
||||
dp->fake_dht_size = 64;
|
||||
dp->fake_unknown_size = 256;
|
||||
dp->fake_syndata_size = 16;
|
||||
dp->fake_unknown_udp_size = 64;
|
||||
dp->wscale=-1; // default - dont change scale factor (client)
|
||||
dp->desync_ttl6 = 0xFF; // unused
|
||||
dp->desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
|
||||
@@ -207,11 +198,55 @@ void dp_init(struct desync_profile *dp)
|
||||
dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT;
|
||||
dp->filter_ipv4 = dp->filter_ipv6 = true;
|
||||
}
|
||||
bool dp_fake_defaults(struct desync_profile *dp)
|
||||
{
|
||||
struct blob_item *item;
|
||||
if (blob_collection_empty(&dp->fake_http))
|
||||
if (!blob_collection_add_blob(&dp->fake_http,fake_http_request_default,strlen(fake_http_request_default),0))
|
||||
return false;
|
||||
if (blob_collection_empty(&dp->fake_tls))
|
||||
{
|
||||
if (!blob_collection_add_blob(&dp->fake_tls,fake_tls_clienthello_default,sizeof(fake_tls_clienthello_default),4))
|
||||
return false;
|
||||
}
|
||||
if (blob_collection_empty(&dp->fake_unknown))
|
||||
{
|
||||
if (!(item=blob_collection_add_blob(&dp->fake_unknown,NULL,256,0)))
|
||||
return false;
|
||||
memset(item->data,0,item->size);
|
||||
}
|
||||
if (blob_collection_empty(&dp->fake_quic))
|
||||
{
|
||||
if (!(item=blob_collection_add_blob(&dp->fake_quic,NULL,620,0)))
|
||||
return false;
|
||||
memset(item->data,0,item->size);
|
||||
item->data[0] = 0x40;
|
||||
}
|
||||
if (blob_collection_empty(&dp->fake_wg))
|
||||
{
|
||||
if (!(item=blob_collection_add_blob(&dp->fake_wg,NULL,64,0)))
|
||||
return false;
|
||||
memset(item->data,0,item->size);
|
||||
}
|
||||
if (blob_collection_empty(&dp->fake_dht))
|
||||
{
|
||||
if (!(item=blob_collection_add_blob(&dp->fake_dht,NULL,64,0)))
|
||||
return false;
|
||||
memset(item->data,0,item->size);
|
||||
}
|
||||
if (blob_collection_empty(&dp->fake_unknown_udp))
|
||||
{
|
||||
if (!(item=blob_collection_add_blob(&dp->fake_unknown_udp,NULL,64,0)))
|
||||
return false;
|
||||
memset(item->data,0,item->size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
|
||||
{
|
||||
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
|
||||
if (!entry) return NULL;
|
||||
|
||||
|
||||
dp_init(&entry->dp);
|
||||
|
||||
// add to the tail
|
||||
@@ -234,6 +269,13 @@ static void dp_clear_dynamic(struct desync_profile *dp)
|
||||
ipset_collection_destroy(&dp->ips_collection_exclude);
|
||||
port_filters_destroy(&dp->pf_tcp);
|
||||
port_filters_destroy(&dp->pf_udp);
|
||||
blob_collection_destroy(&dp->fake_http);
|
||||
blob_collection_destroy(&dp->fake_tls);
|
||||
blob_collection_destroy(&dp->fake_unknown);
|
||||
blob_collection_destroy(&dp->fake_unknown_udp);
|
||||
blob_collection_destroy(&dp->fake_quic);
|
||||
blob_collection_destroy(&dp->fake_wg);
|
||||
blob_collection_destroy(&dp->fake_dht);
|
||||
HostFailPoolDestroy(&dp->hostlist_auto_fail_counters);
|
||||
}
|
||||
void dp_clear(struct desync_profile *dp)
|
||||
|
19
nfq/params.h
19
nfq/params.h
@@ -46,8 +46,16 @@
|
||||
#define FAKE_TLS_MOD_RND_SNI 0x40
|
||||
#define FAKE_TLS_MOD_PADENCAP 0x80
|
||||
|
||||
#define FAKE_MAX_TCP 1460
|
||||
#define FAKE_MAX_UDP 1472
|
||||
|
||||
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
|
||||
|
||||
struct fake_tls_mod_cache
|
||||
{
|
||||
size_t extlen_offset, padlen_offset;
|
||||
};
|
||||
|
||||
struct desync_profile
|
||||
{
|
||||
int n; // number of the profile
|
||||
@@ -74,12 +82,12 @@ struct desync_profile
|
||||
autottl desync_autottl, desync_autottl6;
|
||||
uint32_t desync_fooling_mode;
|
||||
uint32_t desync_badseq_increment, desync_badseq_ack_increment;
|
||||
uint8_t fake_http[1460],fake_unknown[1460],fake_syndata[1460],seqovl_pattern[1460],fsplit_pattern[1460];
|
||||
uint8_t fake_unknown_udp[1472],udplen_pattern[1472],fake_quic[1472],fake_wg[1472],fake_dht[1472];
|
||||
size_t fake_http_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size;
|
||||
|
||||
uint8_t fake_tls[1460],fake_tls_mod;
|
||||
size_t fake_tls_size, fake_tls_extlen_offset, fake_tls_padlen_offset;
|
||||
struct blob_collection_head fake_http,fake_tls,fake_unknown,fake_unknown_udp,fake_quic,fake_wg,fake_dht;
|
||||
uint8_t fake_syndata[FAKE_MAX_TCP],seqovl_pattern[FAKE_MAX_TCP],fsplit_pattern[FAKE_MAX_TCP],udplen_pattern[FAKE_MAX_UDP];
|
||||
size_t fake_syndata_size;
|
||||
|
||||
uint8_t fake_tls_mod;
|
||||
|
||||
int udplen_increment;
|
||||
|
||||
@@ -113,6 +121,7 @@ void dp_entry_destroy(struct desync_profile_list *entry);
|
||||
void dp_list_destroy(struct desync_profile_list_head *head);
|
||||
bool dp_list_have_autohostlist(struct desync_profile_list_head *head);
|
||||
void dp_init(struct desync_profile *dp);
|
||||
bool dp_fake_defaults(struct desync_profile *dp);
|
||||
void dp_clear(struct desync_profile *dp);
|
||||
|
||||
struct params_s
|
||||
|
61
nfq/pools.c
61
nfq/pools.c
@@ -517,3 +517,64 @@ bool port_filters_deny_if_empty(struct port_filters_head *head)
|
||||
if (LIST_FIRST(head)) return true;
|
||||
return pf_parse("0",&pf) && port_filter_add(head,&pf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct blob_item *blob_collection_add(struct blob_collection_head *head)
|
||||
{
|
||||
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
|
||||
if (entry)
|
||||
{
|
||||
// insert to the end
|
||||
struct blob_item *itemc,*iteml=LIST_FIRST(head);
|
||||
if (iteml)
|
||||
{
|
||||
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
|
||||
LIST_INSERT_AFTER(iteml, entry, next);
|
||||
}
|
||||
else
|
||||
LIST_INSERT_HEAD(head, entry, next);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve)
|
||||
{
|
||||
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
|
||||
if (!entry) return NULL;
|
||||
if (!(entry->data = malloc(size+size_reserve)))
|
||||
{
|
||||
free(entry);
|
||||
return NULL;
|
||||
}
|
||||
if (data) memcpy(entry->data,data,size);
|
||||
entry->size = size;
|
||||
entry->size_buf = size+size_reserve;
|
||||
|
||||
// insert to the end
|
||||
struct blob_item *itemc,*iteml=LIST_FIRST(head);
|
||||
if (iteml)
|
||||
{
|
||||
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
|
||||
LIST_INSERT_AFTER(iteml, entry, next);
|
||||
}
|
||||
else
|
||||
LIST_INSERT_HEAD(head, entry, next);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void blob_collection_destroy(struct blob_collection_head *head)
|
||||
{
|
||||
struct blob_item *entry;
|
||||
while ((entry = LIST_FIRST(head)))
|
||||
{
|
||||
LIST_REMOVE(entry, next);
|
||||
free(entry->extra);
|
||||
free(entry->data);
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
bool blob_collection_empty(const struct blob_collection_head *head)
|
||||
{
|
||||
return !LIST_FIRST(head);
|
||||
}
|
||||
|
14
nfq/pools.h
14
nfq/pools.h
@@ -146,3 +146,17 @@ bool port_filter_add(struct port_filters_head *head, const port_filter *pf);
|
||||
void port_filters_destroy(struct port_filters_head *head);
|
||||
bool port_filters_in_range(const struct port_filters_head *head, uint16_t port);
|
||||
bool port_filters_deny_if_empty(struct port_filters_head *head);
|
||||
|
||||
|
||||
struct blob_item {
|
||||
uint8_t *data; // main data blob
|
||||
size_t size; // main data blob size
|
||||
size_t size_buf;// main data blob allocated size
|
||||
void *extra; // any data without size
|
||||
LIST_ENTRY(blob_item) next;
|
||||
};
|
||||
LIST_HEAD(blob_collection_head, blob_item);
|
||||
struct blob_item *blob_collection_add(struct blob_collection_head *head);
|
||||
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve);
|
||||
void blob_collection_destroy(struct blob_collection_head *head);
|
||||
bool blob_collection_empty(const struct blob_collection_head *head);
|
||||
|
@@ -844,7 +844,16 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
|
||||
return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
|
||||
}
|
||||
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
|
||||
struct range64
|
||||
{
|
||||
uint64_t offset,len;
|
||||
};
|
||||
#define MAX_DEFRAG_PIECES 128
|
||||
static int cmp_range64(const void * a, const void * b)
|
||||
{
|
||||
return (((struct range64*)a)->offset < ((struct range64*)b)->offset) ? -1 : (((struct range64*)a)->offset > ((struct range64*)b)->offset) ? 1 : 0;
|
||||
}
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull)
|
||||
{
|
||||
// Crypto frame can be split into multiple chunks
|
||||
// chromium randomly splits it and pads with zero/one bytes to force support the standard
|
||||
@@ -853,13 +862,15 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
if (*defrag_len<10) return false;
|
||||
uint8_t *defrag_data = defrag+10;
|
||||
size_t defrag_data_len = *defrag_len-10;
|
||||
|
||||
uint8_t ft;
|
||||
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
|
||||
bool found=false;
|
||||
struct range64 ranges[MAX_DEFRAG_PIECES];
|
||||
int i,range=0;
|
||||
|
||||
while(pos<clean_len)
|
||||
{
|
||||
// frame type
|
||||
ft = clean[pos];
|
||||
pos++;
|
||||
if (ft>1) // 00 - padding, 01 - ping
|
||||
@@ -867,6 +878,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
if (ft!=6) return false; // dont want to know all possible frame type formats
|
||||
|
||||
if (pos>=clean_len) return false;
|
||||
if (range>=MAX_DEFRAG_PIECES) return false;
|
||||
|
||||
if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
|
||||
pos += tvb_get_varint(clean+pos, &offset);
|
||||
@@ -875,7 +887,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
pos += tvb_get_varint(clean+pos, &sz);
|
||||
if ((pos+sz)>clean_len) return false;
|
||||
|
||||
if ((offset+sz)>defrag_data_len) return false;
|
||||
if ((offset+sz)>defrag_data_len) return false; // defrag buf overflow
|
||||
if (zeropos < offset)
|
||||
// make sure no uninitialized gaps exist in case of not full fragment coverage
|
||||
memset(defrag_data+zeropos,0,offset-zeropos);
|
||||
@@ -886,6 +898,10 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
|
||||
found=true;
|
||||
pos+=sz;
|
||||
|
||||
ranges[range].offset = offset;
|
||||
ranges[range].len = sz;
|
||||
range++;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
@@ -897,6 +913,23 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
phton64(defrag+2,szmax);
|
||||
defrag[2] |= 0xC0; // 64 bit value
|
||||
*defrag_len = (size_t)(szmax+10);
|
||||
|
||||
qsort(ranges, range, sizeof(*ranges), cmp_range64);
|
||||
|
||||
//for(i=0 ; i<range ; i++)
|
||||
// printf("RANGE %zu len %zu\n",ranges[i].offset,ranges[i].len);
|
||||
|
||||
for(i=0,offset=0,*bFull=true ; i<range ; i++)
|
||||
{
|
||||
if (ranges[i].offset!=offset)
|
||||
{
|
||||
*bFull = false;
|
||||
break;
|
||||
}
|
||||
offset += ranges[i].len;
|
||||
}
|
||||
|
||||
//printf("bFull=%u\n",*bFull);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
@@ -87,5 +87,6 @@ uint8_t QUICDraftVersion(uint32_t version);
|
||||
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);
|
||||
// returns true if crypto frames were found . bFull = true if crypto frame fragments have full coverage
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull);
|
||||
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);
|
||||
|
@@ -3,7 +3,7 @@ CFLAGS += -std=gnu99 -Os -flto=auto
|
||||
CFLAGS_SYSTEMD = -DUSE_SYSTEMD
|
||||
CFLAGS_BSD = -Wno-address-of-packed-member
|
||||
LIBS = -lz -lpthread
|
||||
LIBS_SYSTEMD = -lz -lsystemd
|
||||
LIBS_SYSTEMD = -lsystemd
|
||||
LIBS_ANDROID = -lz
|
||||
SRC_FILES = *.c
|
||||
SRC_FILES_ANDROID = $(SRC_FILES) andr/*.c
|
||||
@@ -14,7 +14,7 @@ tpws: $(SRC_FILES)
|
||||
$(CC) -s $(CFLAGS) -o tpws $(SRC_FILES) $(LIBS) $(LDFLAGS)
|
||||
|
||||
systemd: $(SRC_FILES)
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o tpws $(SRC_FILES) $(LIBS_SYSTEMD) $(LDFLAGS)
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o tpws $(SRC_FILES) $(LIBS) $(LIBS_SYSTEMD) $(LDFLAGS)
|
||||
|
||||
android: $(SRC_FILES)
|
||||
$(CC) -s $(CFLAGS) -o tpws $(SRC_FILES_ANDROID) $(LIBS_ANDROID) $(LDFLAGS)
|
||||
|
@@ -50,6 +50,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, int lev
|
||||
{
|
||||
va_copy(args2,args);
|
||||
DLOG_CON(format,syslog_priority,args2);
|
||||
va_end(args2);
|
||||
}
|
||||
if (params.debug>=level)
|
||||
{
|
||||
|
Reference in New Issue
Block a user