Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-15 14:36:09 +00:00

57 Commits
v0.1.2 ... v0.2

Author SHA1 Message Date
bol-van
b91fb6a424 nfqws2: lexra compile fix 2025-11-30 15:39:18 +03:00
bol-van
97e16b5611 blockcheck2: fix -wf winws parameters 2025-11-30 15:25:53 +03:00
bol-van
8fe63df846 update docs 2025-11-30 15:17:32 +03:00
bol-van
494abc0e53 update docs ver 2025-11-30 15:09:21 +03:00
bol-van
ef300e5d21 blockcheck 2025-11-30 15:03:52 +03:00
bol-van
1f70b21a4d nfqws2: fix crash 2025-11-30 11:08:54 +03:00
bol-van
520bf5142f nfqws2: allow negative numbers in bu8,bu16,bu24,bu32,swap16,swap32 2025-11-30 09:25:43 +03:00
bol-van
b2a611adba nfqws2: fix crash 2025-11-30 09:02:58 +03:00
bol-van
24320b6671 nfqws2: do not prohibit truncate calls 2025-11-29 23:05:00 +03:00
bol-van
709db5d135 doc typo 2025-11-29 22:55:01 +03:00
bol-van
6515cd3227 zapret-lib: fix rawsend_segmented 2025-11-29 21:07:59 +03:00
bol-van
574ad480f8 zapret-lib: fix mss issue in rawsend_segmented 2025-11-28 16:30:42 +03:00
bol-van
bebb2ccabf zapret-antidpi: allow some desync funcs to work with arbitrary blobs 2025-11-28 16:21:13 +03:00
bol-van
4923ac7bc5 replace ip6_hopbyhop_x2 to ip6_hopbyhop2 2025-11-27 21:33:03 +03:00
bol-van
605af78ce8 fix rawsend tests on windows 2025-11-27 09:45:52 +03:00
bol-van
b496bac9ab zapret-pcap: improve comment 2025-11-27 08:48:59 +03:00
bol-van
9ba7ad6263 zapret-pcap: improve comment 2025-11-27 08:48:16 +03:00
bol-van
36a980469f zapret-pcap: 'keep' parameter 2025-11-27 08:47:28 +03:00
bol-van
284b345482 zapret-pcap: separate pcap write functions 2025-11-27 08:44:36 +03:00
bol-van
5866bc7721 winws2: gettid support 2025-11-26 22:14:41 +03:00
bol-van
bcf11b8f72 nfqws2: support \: escapement in --lua-desync args 2025-11-26 18:18:39 +03:00
bol-van
ee11c760fd zapret-lib: fix comment 2025-11-26 18:01:50 +03:00
bol-van
e1c6802b52 nfqws2: getpid and gettid luacall 2025-11-26 17:51:46 +03:00
bol-van
8d2b7d9ef2 zapret-pcap: cache file name 2025-11-26 17:11:52 +03:00
bol-van
733171fb3c zapret_pcap: fix comment 2025-11-26 15:38:39 +03:00
bol-van
9eaf346253 zapret-pcap.lua 2025-11-26 15:30:54 +03:00
bol-van
1cfec4d737 nfqws2: update code 2025-11-26 14:11:29 +03:00
bol-van
39c6a71481 move basic desync functions to zapret-lib.lua 2025-11-25 16:35:15 +03:00
bol-van
35b97450fe nfqws2: optimize mtproto detection 2025-11-25 14:49:33 +03:00
bol-van
cfb8b9f11f nfqws2: optimize aes-ctr and mtproto detection 2025-11-25 14:47:18 +03:00
bol-van
5e737f314b fix lua5.4 incompat 2025-11-25 13:26:55 +03:00
bol-van
6a26ea85a3 update docs 2025-11-25 13:08:40 +03:00
bol-van
1eb780bb06 update docs 2025-11-25 13:04:25 +03:00
bol-van
db2412a6b4 nfqws2: # and % arg subst 2025-11-25 13:03:28 +03:00
bol-van
de15c25def zapret-antidpi: luaexec 2025-11-25 13:03:11 +03:00
bol-van
3ee6d2fb48 nfqws2: make --writeable dir optional and support it in all OS 2025-11-25 10:35:04 +03:00
bol-van
9822fd0b36 github: delete windivert filters from embedded build 2025-11-24 20:57:51 +03:00
bol-van
38454aabfa winws2: fix sandbox reinit error 2025-11-24 18:32:28 +03:00
bol-van
14503e0a57 winws2: set low integrity on writable folder 2025-11-24 18:27:18 +03:00
bol-van
ef421bad9c winws2: create writeable zapret2 dir in LocalLow and set WRITEABLE env 2025-11-24 17:33:17 +03:00
bol-van
e963b6f20b winws2: use cygwin_conv_path 2025-11-24 17:21:46 +03:00
bol-van
c52a3a2e75 winws2: define APPDATALOW env for LUA code for file storage 2025-11-24 17:08:04 +03:00
bol-van
b2f7fac102 fix missing update 2025-11-24 15:40:48 +03:00
bol-van
ea566720a5 nfqws: optimize logging to a file 2025-11-24 15:34:35 +03:00
bol-van
22c7ee257e Merge pull request #7 from Pavel4e5/typos
Fix typos in function descriptions
2025-11-24 13:55:06 +03:00
bol-van
a5a81424c8 winws2: enable sandbox even if nlm/ssid filter is present 2025-11-24 13:40:13 +03:00
bol-van
fba42f8a00 winws2: fix utf8 file names when setting mandatory level 2025-11-24 08:50:51 +03:00
bol-van
b4aff06c35 winws2: fix utf8 file names when setting mandatory level 2025-11-24 08:19:42 +03:00
bol-van
2b85262ee2 winws2: set low mandatory label on logs and autohostlist 2025-11-23 23:25:16 +03:00
bol-van
3b92197bb3 winws2: init lua after sandbox init 2025-11-23 21:03:39 +03:00
bol-van
9d49f35324 winws2: set low mandatory if possible 2025-11-23 20:52:33 +03:00
bol-van
ed4eb043a2 zapret-wgobfs: optimize key cache 2025-11-23 19:06:24 +03:00
bol-van
3a66f86621 zapret-wgobfs: optimize key cache 2025-11-23 19:02:47 +03:00
bol-van
c80efcc983 zapret-tests.lua: aes_ctr const tests 2025-11-23 16:09:11 +03:00
bol-van
c65b28c3f7 nfqws2: optimize aes-ctr gamma xor 2025-11-23 15:15:39 +03:00
Pavel4e5
6d74e6e873 Rename tcp_unflags_set to tcp_flags_unset 2025-11-23 16:16:39 +05:00
Pavel4e5
200ca70f82 Rename tcp_unflags_set to tcp_flags_unset 2025-11-23 16:14:24 +05:00
48 changed files with 4343 additions and 272 deletions

View File

@@ -551,7 +551,7 @@ jobs:
(
cd ${{ env.repo_dir }}
rm -rf binaries/{android*,freebsd*,win*} \
init.d/{openrc,pfsense,runit,s6,systemd} \
init.d/{openrc,pfsense,runit,s6,systemd,windivert.filter.examples} \
nfq2 ip2net mdig docs Makefile
)
tar --owner=0 --group=0 -czf ${{ env.repo_dir }}-openwrt-embedded.tar.gz ${{ env.repo_dir }}

View File

@@ -0,0 +1,58 @@
LIST_HTTP="${LIST_HTTP:-$TESTDIR/list_http.txt}"
LIST_HTTPS_TLS12="${LIST_HTTPS_TLS12:-$TESTDIR/list_https_tls12.txt}"
LIST_HTTPS_TLS13="${LIST_HTTPS_TLS13:-$TESTDIR/list_https_tls13.txt}"
LIST_QUIC="${LIST_QUIC:-$TESTDIR/list_quic.txt}"
check_list()
{
# $1 - test function
# $2 - domain
# $3 - file
local line ok=0
[ -f "$3" ] || {
echo "no strategy file '$3'"
return 1
}
while IFS= read -r line; do
case "$line" in
""|\#*) continue ;;
esac
line=$(echo "$line" | tr -d "\r\n")
eval pktws_curl_test_update "$1" "$2" $line && ok=1
done < "$3"
[ "$ok" = 1 ]
}
pktws_check_http()
{
# $1 - test function
# $2 - domain
check_list "$1" "$2" "$LIST_HTTP"
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
check_list "$1" "$2" "$LIST_HTTPS_TLS12"
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
check_list "$1" "$2" "$LIST_HTTPS_TLS13"
}
pktws_check_http3()
{
# $1 - test function
# $2 - domain
check_list "$1" "$2" "$LIST_QUIC"
}

View File

@@ -0,0 +1,10 @@
Простой тестер стратегий по списку из файла.
Скопируйте эту директорию под другим именем в blockcheck2.d, отредактируйте list файлы, впишите туда свои стратегии.
В диалоге blockcheck2.sh выберите тест с названием вашей директории.
Можно комментировать строки символом '#' в начале строки.
Альтернативный путь до файлов стратегий можно задать переменными LIST_HTTP, LIST_HTTPS_TLS12, LIST_HTTPS_TLS13, LIST_QUIC.
This is simple strategy tester from a file.
Copy this folder, write your strategies into list files and select your test in blockcheck2 dialog.
Lines can be commented using the '#' symbol at the line start.
Strategy list files paths can be overriden in env variables : LIST_HTTP, LIST_HTTPS_TLS12, LIST_HTTPS_TLS13, LIST_QUIC.

View File

@@ -0,0 +1,4 @@
# write nfqws2 parameters here
--payload=http_req --lua-desync=http_hostcase
--payload=http_req --lua-desync=http_methodeol
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_ts=-1000

View File

@@ -0,0 +1,3 @@
# write nfqws2 parameters here
--payload tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_ts=-1000
--payload=tls_client_hello --lua-desync=fake:blob=0x00000000:tcp_md5:repeats=1 --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,dupsid:repeats=1 --lua-desync=multisplit:pos=2

View File

@@ -0,0 +1,4 @@
# write nfqws2 parameters here
--payload tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_ts=-1000
--payload tls_client_hello --lua-desync=tcpseg:pos=0,-1:seqovl=1 --lua-desync=drop
--payload tls_client_hello --lua-desync=luaexec:code="desync.pat=tls_mod(fake_default_tls,'rnd,rndsni,dupsid,padencap',desync.reasm_data)" --lua-desync=tcpseg:pos=0,-1:seqovl=#pat:seqovl_pattern=pat --lua-desync=drop

View File

@@ -0,0 +1,3 @@
# write nfqws2 parameters here
--payload quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=11
--payload quic_initial --lua-desync=send:ipfrag --lua-desync=drop

View File

@@ -0,0 +1,12 @@
pktws_check_http()
{
# $1 - test function
# $2 - domain
local s
[ "$NOTEST_BASIC_HTTP" = 1 ] && { echo "SKIPPED"; return; }
for s in 'http_hostcase' 'http_hostcase:spell=hoSt' 'http_domcase' 'http_methodeol'; do
pktws_curl_test_update $1 $2 --payload http_req --lua-desync=$s
done
}

View File

@@ -0,0 +1,38 @@
. "$TESTDIR/def.inc"
pktws_check_http()
{
# $1 - test function
# $2 - domain
local PAYLOAD="--payload http_req" repeats ok
for repeats in 1 20 100 260; do
# send starting bytes of original payload
pktws_curl_test_update "$1" "$2" $PAYLOAD --lua-desync=tcpseg:pos=0,method+2:ip_id=rnd:repeats=$repeats && ok=1
pktws_curl_test_update "$1" "$2" $PAYLOAD --lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=$repeats && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
local PAYLOAD="--payload tls_client_hello" repeats ok
for repeats in 1 20 100 260; do
# send starting bytes of original payload
pktws_curl_test_update "$1" "$2" $PAYLOAD --lua-desync=tcpseg:pos=0,1:ip_id=rnd:repeats=$repeats && ok=1
pktws_curl_test_update "$1" "$2" $PAYLOAD --lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=$repeats && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls12 "$1" "$2"
}

View File

@@ -0,0 +1,66 @@
pktws_simple_split_tests()
{
# $1 - test function
# $2 - domain/uri
# $3 - splits
# $4 - PRE args for nfqws2
local pos ok ok_any pre="$4"
local splitf splitfs="multisplit multidisorder"
ok_any=0
for splitf in multisplit multidisorder; do
eval need_$splitf=0
ok=0
for pos in $3; do
pktws_curl_test_update $1 $2 $pre $PAYLOAD --lua-desync=$splitf:pos=$pos && ok=1
done
[ "$ok" = 1 -a "$SCANLEVEL" != force ] || eval need_$splitf=1
[ "$ok" = 1 ] && ok_any=1
done
[ "$ok_any" = 1 ]
}
pktws_check_http()
{
# $1 - test function
# $2 - domain
local splits_http='method+2 midsld method+2,midsld'
local PAYLOAD="--payload http_req"
[ "$NOTEST_MULTI_HTTP" = 1 ] && { echo "SKIPPED"; return; }
pktws_simple_split_tests "$1" "$2" "$splits_http"
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
local splits_tls='2 1 sniext+1 sniext+4 host+1 midsld 1,midsld 1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1'
local PAYLOAD="--payload tls_client_hello"
[ "$NOTEST_MULTI_HTTPS" = 1 ] && { echo "SKIPPED"; return; }
pktws_simple_split_tests "$1" "$2" "$splits_tls" "$3"
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
# do not use 'need' values obtained with wssize
local need_multisplit_save=$need_multisplit need_multidisorder_save=$need_multidisorder
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
need_multisplit=$need_multisplit_save; need_multidisorder=$need_multidisorder_save
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,94 @@
pktws_check_http()
{
# $1 - test function
# $2 - domain
[ "$NOTEST_SEQOVL_HTTP" = 1 ] && { echo "SKIPPED"; return; }
local PAYLOAD="--payload http_req"
local ok pat= split f f2
pat=${SEQOVL_PATTERN_HTTP:+seqovl_pat}
pat=${pat:-fake_default_http}
pktws_curl_test_update $1 $2 $PAYLOAD --lua-desync=tcpseg:pos=0,-1:seqovl=1 --lua-desync=drop
pktws_curl_test_update $1 $2 ${SEQOVL_PATTERN_HTTP:+--blob=$pat:@"$SEQOVL_PATTERN_HTTP" }$PAYLOAD --lua-desync=tcpseg:pos=0,-1:seqovl=#$pat:seqovl_pattern=$pat --lua-desync=drop
ok=0
for split in method+2 method+2,midsld; do
pktws_curl_test_update $1 $2 $PAYLOAD --lua-desync=multisplit:pos=$split:seqovl=1 && ok=1
pktws_curl_test_update $1 $2 ${SEQOVL_PATTERN_HTTP:+--blob=$pat:@"$SEQOVL_PATTERN_HTTP" }$PAYLOAD --lua-desync=multisplit:pos=$split:seqovl=#$pat:seqovl_pattern=$pat && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
for split in 'method+1 method+2' 'midsld-1 midsld' 'method+1 method+2,midsld'; do
f="$(extract_arg 1 $split)"
f2="$(extract_arg 2 $split)"
pktws_curl_test_update $1 $2 $PAYLOAD --lua-desync=multidisorder:pos=$f2:seqovl=$f
pktws_curl_test_update $1 $2 ${SEQOVL_PATTERN_HTTP:+--blob=$pat:@"$SEQOVL_PATTERN_HTTP" }$PAYLOAD --lua-desync=multidisorder:pos=$f2:seqovl=$f:seqovl_pattern=$pat
done
}
pktws_seqovl_tests_tls()
{
# $1 - test function
# $2 - domain/uri
# $3 - PRE args for nfqws2
local ok ok_any
local testf=$1 domain="$2" pre="$3"
local pat rnd_mod padencap_mod split f f2
local PAYLOAD="--payload tls_client_hello"
pat=${SEQOVL_PATTERN_HTTPS:+seqovl_pat}
pat=${pat:-fake_default_tls}
rnd_mod="--lua-init=$pat=tls_mod($pat,'rnd')"
padencap_mod="--lua-desync=luaexec:code=desync.pat=tls_mod($pat,'rnd,dupsid,padencap',desync.reasm_data)"
ok=0
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=tcpseg:pos=0,-1:seqovl=1 --lua-desync=drop && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$rnd_mod $pre $PAYLOAD --lua-desync=tcpseg:pos=0,-1:seqovl=#$pat:seqovl_pattern=$pat --lua-desync=drop && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$pre $PAYLOAD $padencap_mod --lua-desync=tcpseg:pos=0,-1:seqovl=#pat:seqovl_pattern=pat --lua-desync=drop && ok=1
ok_any=$ok
ok=0
for split in 10 10,sniext+1 10,sniext+4 10,midsld; do
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=multisplit:pos=$split:seqovl=1 && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$rnd_mod $pre $PAYLOAD --lua-desync=multisplit:pos=$split:seqovl=#$pat:seqovl_pattern=$pat && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$pre $PAYLOAD $padencap_mod --lua-desync=multisplit:pos=$split:seqovl=#pat:seqovl_pattern=pat && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
for split in '1 2' 'sniext sniext+1' 'sniext+3 sniext+4' 'midsld-1 midsld' '1 2,midsld'; do
f="$(extract_arg 1 $split)"
f2="$(extract_arg 2 $split)"
pktws_curl_test_update $1 $2 $PAYLOAD --lua-desync=multidisorder:pos=$f2:seqovl=$f && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$rnd_mod $pre $PAYLOAD --lua-desync=multidisorder:pos=$f2:seqovl=$f:seqovl_pattern=$pat && ok=1
done
[ "$ok" = 1 ] && ok_any=1
[ "$ok_any" = 1 ]
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_SEQOVL_HTTPS" = 1 ] && { echo "SKIPPED"; return; }
pktws_seqovl_tests_tls "$1" "$2" "$3"
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_seqovl_tests_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
pktws_seqovl_tests_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_seqovl_tests_tls "$1" "$2"
}

View File

@@ -0,0 +1,149 @@
. "$TESTDIR/def.inc"
pktws_check_http()
{
# $1 - test function
# $2 - domain
[ "$NOTEST_FAKE_HTTP" = 1 ] && { echo "SKIPPED"; return; }
local testf=$1 domain="$2"
local ok ok_any ttls attls f ff fake fooling
local PAYLOAD="--payload=http_req"
if [ -n "$FAKE_HTTP" ]; then
fake=fake_http
else
fake=fake_default_http
fi
need_fake=0
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
ok_any=0
ok=0
for ttl in $ttls; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for ff in $fake 0x00000000; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD "--lua-desync=fake:blob=${ff}:ip${IPVV}_ttl=$ttl:repeats=$FAKE_REPEATS" $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for ff in $fake 0x00000000; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=fake_http:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS && ok=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --payload empty "--out-range=<s1" --lua-desync=send:tcp_md5 && ok=1
done
done
for ttl in $attls; do
for ff in $fake 0x00000000; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:ip${IPVV}_autottl=-$ttl,3-20:repeats=$FAKE_REPEATS $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && need_fake=1
[ $ok = 1 ] && okany=1
[ $okany = 1 ]
}
pktws_fake_https_vary_()
{
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5"
shift; shift; shift
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=multisplit:blob=$fake:$fooling:pos=2:nodrop:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid,padencap:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 ] && ok=1
}
pktws_fake_https_vary()
{
local ok_any=0 fooling="$3"
pktws_fake_https_vary_ "$1" "$2" "$3" "$4" "$5" && ok_any=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && \
pktws_fake_https_vary_ "$1" "$2" "$3" "$4" "${5:+$5 }--payload=empty --out-range=<s1 --lua-desync=send:tcp_md5" && ok_any=1
[ "$ok_any" = 1 ]
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_FAKE_HTTPS" = 1 ] && { echo "SKIPPED"; return; }
local testf=$1 domain="$2" pre="$3"
local ok ok_any ttls attls f fake fooling
local PAYLOAD="--payload=tls_client_hello"
shift; shift
if [ -n "$FAKE_HTTPS" ]; then
fake=fake_tls
else
fake=fake_default_tls
fi
need_fake=0
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
ok_any=0
ok=0
for ttl in $ttls; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
pktws_fake_https_vary $testf $domain "$fooling" "$pre"
done
for ttl in $attls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && need_fake=1
[ $ok = 1 ] && okany=1
[ $okany = 1 ]
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
# do not use 'need' values obtained with wssize
local need_fake_save=$need_fake
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
need_fake=$need_fake_save
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,103 @@
. "$TESTDIR/def.inc"
pktws_check_faked()
{
# $1 - test function
# $2 - domain
# $3 - payload_type
# $4 - splits
# $5 - pattern
# $6 - PRE args for nfqws2
local testf=$1 domain="$2" pre="$6"
local ok ok_any ttls attls f fooling
local splitf splitfs= split splits="$4"
local PAYLOAD="--payload=$3"
local FAKED_PATTERN="$5"
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
# do not test fakedsplit if multisplit works
[ "$need_multisplit" = 0 -a "$SCANLEVEL" != force ] || splitfs=fakedsplit
# do not test fakeddisorder if multidisorder works
[ "$need_multidisorder" = 0 -a "$SCANLEVEL" != force ] || splitfs="${splitfs:+$splitfs }fakeddisorder"
ok_any=0
for splitf in $splitfs; do
ok=0
for ttl in $ttls; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for split in $splits; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKED_PATTERN:+--blob=faked_pat:@"$FAKED_PATTERN" }$pre $PAYLOAD --lua-desync=$splitf:${FAKED_PATTERN:+pattern=faked_pat:}pos=$split:ip${IPVV}_ttl=$ttl:repeats=$FAKE_REPEATS $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
pktws_curl_test_update $testf $domain ${FAKED_PATTERN:+--blob=faked_pat:@"$FAKED_PATTERN" }$pre $PAYLOAD --lua-desync=$splitf:${FAKED_PATTERN:+pattern=faked_pat:}pos=$split:$fooling && ok=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKED_PATTERN:+--blob=faked_pat:@"$FAKED_PATTERN" }$pre $PAYLOAD --lua-desync=$splitf:${FAKED_PATTERN:+pattern=faked_pat:}pos=$split:$fooling:repeats=$FAKE_REPEATS --payload empty --out-range="<s1" --lua-desync=send:tcp_md5 && ok=1
done
done
for ttl in $attls; do
for split in $splits; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKED_PATTERN:+--blob=faked_pat:@"$FAKED_PATTERN" }$pre $PAYLOAD --lua-desync=$splitf:${FAKED_PATTERN:+pattern=faked_pat:}pos=$split:ip${IPVV}_autottl=-$ttl,3-20:repeats=$FAKE_REPEATS $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && eval need_$splitf=1
[ $ok = 1 ] && ok_any=1
done
[ "$ok_any" = 1 ]
}
pktws_check_http()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_FAKED_HTTP" = 1 ] && { echo "SKIPPED"; return; }
local splits='method+2 midsld method+2,midsld'
pktws_check_faked $1 "$2" http_req "$splits" "$FAKED_PATTERN_HTTP" "$3"
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_FAKED_HTTPS" = 1 ] && { echo "SKIPPED"; return; }
local splits='2 1 sniext+1 sniext+4 host+1 midsld 1,midsld 1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1'
pktws_check_faked $1 "$2" tls_client_hello "$splits" "$FAKED_PATTERN_HTTPS" "$3"
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
# do not use 'need' values obtained with wssize
local need_fakedsplit_save=$need_fakedsplit need_fakeddisorder_save=$need_fakeddisorder
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
need_fakedsplit=$need_fakedsplit_save need_fakeddisorder=$need_fakeddisorder_save
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,101 @@
. "$TESTDIR/def.inc"
pktws_hostfake_vary_()
{
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5" disorder
shift; shift; shift
for disorder in '' 'disorder_after:'; do
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
done
[ "$ok_any" = 1 ] && ok=1
}
pktws_hostfake_vary()
{
local ok_any=0 fooling="$3"
pktws_hostfake_vary_ "$1" "$2" "$3" "$4" "$5" && ok_any=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && \
pktws_hostfake_vary_ "$1" "$2" "$3" "$4" "${5:+$5 }--payload=empty --out-range=<s1 --lua-desync=send:tcp_md5" && ok_any=1
[ "$ok_any" = 1 ]
}
pktws_check_hostfake()
{
# $1 - test function
# $2 - domain
# $3 - payload_type
# $4 - PRE args for nfqws2
local testf=$1 domain="$2" pre="$4"
local ok ttls attls f fooling
local PAYLOAD="--payload=$3"
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
need_hostfakesplit=0
ok=0
for ttl in $ttls; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_hostfake_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
pktws_hostfake_vary $testf $domain "$fooling" "$pre"
done
for ttl in $attls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_hostfake_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && eval need_hostfake=1
[ $ok = 1 ]
}
pktws_check_http()
{
# $1 - test function
# $2 - domain
[ "$NOTEST_HOSTFAKE_HTTP" = 1 ] && { echo "SKIPPED"; return; }
pktws_check_hostfake $1 "$2" http_req
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_HOSTFAKE_HTTPS" = 1 ] && { echo "SKIPPED"; return; }
pktws_check_hostfake $1 "$2" tls_client_hello "$3"
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
# do not use 'need' values obtained with wssize
local need_hostfakesplit_save=$need_hostfakesplit
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
need_hostfakesplit=$need_hostfakesplit_save
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,160 @@
. "$TESTDIR/def.inc"
pktws_check_http()
{
# $1 - test function
# $2 - domain
[ "$NOTEST_FAKE_MULTI_HTTP" = 1 ] && { echo "SKIPPED"; return 0; }
local testf=$1 domain="$2"
local ok ttls attls f ff fake fooling splitf splitfs= split splits='method+2 midsld method+2,midsld'
local PAYLOAD="--payload=http_req"
if [ -n "$FAKE_HTTP" ]; then
fake=fake_http
else
fake=fake_default_http
fi
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
# do not test fake + multisplit if multisplit works
[ "$need_multisplit" = 0 -a "$SCANLEVEL" != force ] || splitfs=multisplit
# do not test fake + multidisorder if multidisorder works
[ "$need_multidisorder" = 0 -a "$SCANLEVEL" != force ] || splitfs="${splitfs:+$splitfs }multidisorder"
for splitf in $splitfs; do
ok=0
for ttl in $ttls; do
for split in $splits; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for ff in $fake 0x00000000; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD "--lua-desync=fake:blob=${ff}:ip${IPVV}_ttl=$ttl:repeats=$FAKE_REPEATS" --lua-desync=$splitf:pos=$split $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
for ff in $fake 0x00000000; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split && ok=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=fake_http:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split --payload empty "--out-range=<s1" --lua-desync=send:tcp_md5 && ok=1
done
done
done
for ttl in $attls; do
for split in $splits; do
for ff in $fake 0x00000000; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:ip${IPVV}_autottl=-$ttl,3-20:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
done
done
done
}
pktws_fake_https_vary_()
{
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5"
shift; shift; shift
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=multisplit:blob=$fake:$fooling:pos=2:nodrop:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid,padencap:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
[ "$ok_any" = 1 ] && ok=1
}
pktws_fake_https_vary()
{
local ok_any=0 fooling="$3"
pktws_fake_https_vary_ "$1" "$2" "$3" "$4" "$5" && ok_any=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && \
pktws_fake_https_vary_ "$1" "$2" "$3" "$4" "${5:+$5 }--payload=empty --out-range=<s1 --lua-desync=send:tcp_md5" && ok_any=1
[ "$ok_any" = 1 ]
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_FAKE_MULTI_HTTPS" = 1 ] && { echo "SKIPPED"; return 0; }
local testf=$1 domain="$2" pre="$3"
local ok ok_any ttls attls f fake fooling splitf splitfs= split splits='2 1 sniext+1 sniext+4 host+1 midsld 1,midsld 1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1'
local PAYLOAD="--payload=tls_client_hello"
shift; shift
if [ -n "$FAKE_HTTPS" ]; then
fake=fake_tls
else
fake=fake_default_tls
fi
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
# do not test fake + multisplit if multisplit works
[ "$need_multisplit" = 0 -a "$SCANLEVEL" != force ] || splitfs=multisplit
# do not test fake + multidisorder if multidisorder works
[ "$need_multidisorder" = 0 -a "$SCANLEVEL" != force ] || splitfs="${splitfs:+$splitfs }multidisorder"
ok_any=0
for splitf in $splitfs; do
ok=0
for ttl in $ttls; do
for split in $splits; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
pktws_fake_https_vary $testf $domain "$fooling" "$pre"
done
done
for ttl in $attls; do
for split in $splits; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
done
done
[ "$ok" = 1 ] && ok_any=1
done
[ "$ok_any" = 1 ]
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,161 @@
. "$TESTDIR/def.inc"
pktws_check_http()
{
# $1 - test function
# $2 - domain
[ "$NOTEST_FAKE_FAKED_HTTP" = 1 ] && { echo "SKIPPED"; return 0; }
local testf=$1 domain="$2"
local ok ttls attls f ff fake fooling splitf splitfs= split splits='method+2 midsld method+2,midsld'
local PAYLOAD="--payload=http_req"
if [ -n "$FAKE_HTTP" ]; then
fake=fake_http
else
fake=fake_default_http
fi
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
# do not test fake + multisplit if multisplit works
[ "$need_fakedsplit" = 0 -a "$SCANLEVEL" != force ] || splitfs=fakedsplit
# do not test fake + fakeddisorder if fakeddisorder works
[ "$need_fakeddisorder" = 0 -a "$SCANLEVEL" != force ] || splitfs="${splitfs:+$splitfs }fakeddisorder"
for splitf in $splitfs; do
ok=0
for ttl in $ttls; do
for split in $splits; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for ff in $fake 0x00000000; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }${FAKED_PATTERN_HTTP:+--blob=faked_pat:@"$FAKED_PATTERN_HTTP" }$PAYLOAD "--lua-desync=fake:blob=${ff}:ip${IPVV}_ttl=$ttl:repeats=$FAKE_REPEATS" --lua-desync=$splitf:${FAKED_PATTERN_HTTP:+pattern=faked_pat:}pos=$split:ip${IPVV}_ttl=$ttl:repeats=$FAKE_REPEATS $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
for ff in $fake 0x00000000; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }${FAKED_PATTERN_HTTP:+--blob=faked_pat:@"$FAKED_PATTERN_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTP:+pattern=faked_pat:}pos=$split:$fooling:repeats=$FAKE_REPEATS && ok=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }${FAKED_PATTERN_HTTP:+--blob=faked_pat:@"$FAKED_PATTERN_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTP:+pattern=faked_pat:}pos=$split:$fooling:repeats=$FAKE_REPEATS --payload empty "--out-range=<s1" --lua-desync=send:tcp_md5 && ok=1
done
done
done
for ttl in $attls; do
for split in $splits; do
for ff in $fake 0x00000000; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }${FAKED_PATTERN_HTTP:+--blob=faked_pat:@"$FAKED_PATTERN_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:ip${IPVV}_autottl=-$ttl,3-20:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTP:+pattern=faked_pat:}pos=$split:ip${IPVV}_autottl=-$ttl,3-20:repeats=$FAKE_REPEATS $f && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
done
done
done
}
pktws_fake_https_vary_()
{
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5"
shift; shift; shift
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=multisplit:blob=$fake:$fooling:pos=2:nodrop:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid,padencap:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
[ "$ok_any" = 1 ] && ok=1
}
pktws_fake_https_vary()
{
local ok_any=0 fooling="$3"
pktws_fake_https_vary_ "$1" "$2" "$3" "$4" "$5" && ok_any=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && \
pktws_fake_https_vary_ "$1" "$2" "$3" "$4" "${5:+$5 }--payload=empty --out-range=<s1 --lua-desync=send:tcp_md5" && ok_any=1
[ "$ok_any" = 1 ]
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_FAKE_FAKED_HTTPS" = 1 ] && { echo "SKIPPED"; return 0; }
local testf=$1 domain="$2" pre="$3"
local ok ok_any ttls attls f fake fooling splitf splitfs= split splits='2 1 sniext+1 sniext+4 host+1 midsld 1,midsld 1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1'
local PAYLOAD="--payload=tls_client_hello"
shift; shift
if [ -n "$FAKE_HTTPS" ]; then
fake=fake_tls
else
fake=fake_default_tls
fi
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
# do not test fake + fakedsplit if fakedsplit works
[ "$need_fakedsplit" = 0 -a "$SCANLEVEL" != force ] || splitfs=fakedsplit
# do not test fake + fakeddisorder if fakeddisorder works
[ "$need_fakeddisorder" = 0 -a "$SCANLEVEL" != force ] || splitfs="${splitfs:+$splitfs }fakeddisorder"
ok_any=0
for splitf in $splitfs; do
ok=0
for ttl in $ttls; do
for split in $splits; do
# orig-ttl=1 with start/cutoff limiter drops empty ACK packet in response to SYN,ACK. it does not reach DPI or server.
# missing ACK is transmitted in the first data packet of TLS/HTTP proto
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
pktws_fake_https_vary $testf $domain "$fooling" "$pre"
done
done
for ttl in $attls; do
for split in $splits; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
done
done
[ "$ok" = 1 ] && ok_any=1
done
[ "$ok_any" = 1 ]
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,113 @@
. "$TESTDIR/def.inc"
pktws_hostfake_vary_()
{
local testf=$1 domain="$2" fooling="$3" pre="$4" post="$5" disorder
shift; shift; shift
for disorder in '' 'disorder_after:'; do
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}$fooling:repeats=$FAKE_REPEATS $post && ok=1
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:$fooling:repeats=$FAKE_REPEATS $post && ok=1
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:$fooling:repeats=$FAKE_REPEATS $post && ok=1
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok=1
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok=1
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok=1
done
}
pktws_hostfake_vary()
{
local fooling="$3"
pktws_hostfake_vary_ "$1" "$2" "$3" "$4" "$5"
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && \
pktws_hostfake_vary_ "$1" "$2" "$3" "$4" "${5:+$5 }--payload=empty --out-range=<s1 --lua-desync=send:tcp_md5"
}
pktws_check_hostfake()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
local testf=$1 domain="$2" pre="$3"
local ok ttls attls f fake fooling
[ "$need_hostfakesplit" = 0 ] && return 0
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
attls=$(seq -s ' ' $MIN_AUTOTTL_DELTA $MAX_AUTOTTL_DELTA)
ok=0
for ttl in $ttls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_hostfake_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
pktws_hostfake_vary $testf $domain "$fooling" "$pre" && ok=1
done
for ttl in $attls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_hostfake_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && {
ok=1
[ "$SCANLEVEL" = force ] || break
}
done
done
[ "$ok" = 1 ]
}
pktws_check_http()
{
[ "$NOTEST_FAKE_HOSTFAKE_HTTP" = 1 ] && { echo "SKIPPED"; return 0; }
local PAYLOAD="--payload=http_req"
local FAKE="$FAKE_HTTP"
if [ -n "$FAKE_HTTP" ]; then
fake=bfake
else
fake=fake_default_http
fi
pktws_check_hostfake "$1" "$2"
}
pktws_check_https_tls()
{
# $1 - test function
# $2 - domain
# $3 - PRE args for nfqws2
[ "$NOTEST_FAKE_HOSTFAKE_HTTPS" = 1 ] && { echo "SKIPPED"; return 0; }
local PAYLOAD="--payload=tls_client_hello"
local FAKE="$FAKE_HTTPS"
if [ -n "$FAKE_HTTPS" ]; then
fake=bfake
else
fake=fake_default_tls
fi
pktws_check_hostfake "$1" "$2" "$3"
}
pktws_check_https_tls12()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
}
pktws_check_https_tls13()
{
# $1 - test function
# $2 - domain
pktws_check_https_tls "$1" "$2"
}

View File

@@ -0,0 +1,28 @@
pktws_check_http3()
{
# $1 - test function
# $2 - domain
[ "$NOTEST_QUIC" = 1 ] && { echo "SKIPPED"; return; }
local repeats fake pos
local PAYLOAD="--payload quic_initial"
if [ -n "$FAKE_QUIC" ]; then
fake=fake_quic
else
fake=fake_default_quic
fi
for repeats in 1 2 5 10 20; do
pktws_curl_test_update $1 $2 ${FAKE_QUIC:+--blob=$fake:@"$FAKE_QUIC" }$PAYLOAD --lua-desync=fake:blob=$fake:repeats=$repeats && [ "$SCANLEVEL" != force ] && break
done
for pos in 8 16 32 64; do
pktws_curl_test_update $1 $2 $PAYLOAD --lua-desync=send:ipfrag:ipfrag_pos_udp=$pos --lua-desync=drop && [ "$SCANLEVEL" != force ] && break
done
for pos in 8 16 32 64; do
pktws_curl_test_update $1 $2 ${FAKE_QUIC:+--blob=$fake:@"$FAKE_QUIC" }$PAYLOAD --lua-desync=fake:blob=$fake:repeats=$FAKE_REPEATS --lua-desync=send:ipfrag:ipfrag_pos_udp=$pos --lua-desync=drop && [ "$SCANLEVEL" != force ] && break
done
}

View File

@@ -0,0 +1,7 @@
FOOLINGS46_TCP=${FOOLINGS46_TCP:-"tcp_md5 badsum tcp_seq=-3000 tcp_seq=1000000 tcp_ack=-66000:tcp_ts_up tcp_ts=-1000 tcp_flags_unset=ACK tcp_flags_set=SYN"}
FOOLINGS6_TCP=${FOOLINGS6_TCP:-"ip6_hopbyhop ip6_hopbyhop:ip6_hopbyhop2 ip6_destopt ip6_routing ip6_ah"}
FOOLINGS_TCP="$FOOLINGS46_TCP"
[ "$IPV" = 6 ] && FOOLINGS_TCP="$FOOLINGS_TCP $FOOLINGS6_TCP"
FOOLINGS_UDP="badsum"
FAKE_REPEATS=${FAKE_REPEATS:-1}

1906
blockcheck2.sh Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -105,6 +105,10 @@ split_by_separator()
[ -n "$3" ] && eval $3="\$before"
[ -n "$4" ] && eval $4="\$after"
}
tolower()
{
echo "$@" | tr 'A-Z' 'a-z'
}
dir_is_not_empty()
{

58
common/dialog.sh Normal file
View File

@@ -0,0 +1,58 @@
read_yes_no()
{
# $1 - default (Y/N)
local A
read A
[ -z "$A" ] || ([ "$A" != "Y" ] && [ "$A" != "y" ] && [ "$A" != "N" ] && [ "$A" != "n" ]) && A=$1
[ "$A" = "Y" ] || [ "$A" = "y" ] || [ "$A" = "1" ]
}
ask_yes_no()
{
# $1 - default (Y/N or 0/1)
# $2 - text
local DEFAULT=$1
[ "$1" = "1" ] && DEFAULT=Y
[ "$1" = "0" ] && DEFAULT=N
[ -z "$DEFAULT" ] && DEFAULT=N
printf "$2 (default : $DEFAULT) (Y/N) ? "
read_yes_no $DEFAULT
}
ask_yes_no_var()
{
# $1 - variable name for answer : 0/1
# $2 - text
local DEFAULT
eval DEFAULT="\$$1"
if ask_yes_no "$DEFAULT" "$2"; then
eval $1=1
else
eval $1=0
fi
}
ask_list()
{
# $1 - mode var
# $2 - space separated value list
# $3 - (optional) default value
local M_DEFAULT
eval M_DEFAULT="\$$1"
local M_ALL=$M_DEFAULT
local M=""
local m
[ -n "$3" ] && { find_str_in_list "$M_DEFAULT" "$2" || M_DEFAULT="$3" ;}
n=1
for m in $2; do
echo $n : $m
n=$(($n+1))
done
printf "your choice (default : $M_DEFAULT) : "
read m
[ -n "$m" ] && M=$(echo $2 | cut -d ' ' -f$m 2>/dev/null)
[ -z "$M" ] && M="$M_DEFAULT"
echo selected : $M
eval $1="\"$M\""
[ "$M" != "$M_OLD" ]
}

28
common/elevate.sh Normal file
View File

@@ -0,0 +1,28 @@
require_root()
{
local exe preserve_env
echo \* checking privileges
[ $(id -u) -ne "0" ] && {
echo root is required
exe="$EXEDIR/$(basename "$0")"
exists sudo && {
echo elevating with sudo
exec sudo -E sh "$exe"
}
exists su && {
echo elevating with su
case "$UNAME" in
Linux)
preserve_env="--preserve-environment"
;;
FreeBSD|OpenBSD|Darwin)
preserve_env="-m"
;;
esac
exec su $preserve_env root -c "sh \"$exe\""
}
echo su or sudo not found
exitp 2
}
HAVE_ROOT=1
}

64
common/fwtype.sh Normal file
View File

@@ -0,0 +1,64 @@
linux_ipt_avail()
{
exists iptables && exists ip6tables
}
linux_maybe_iptables_fwtype()
{
linux_ipt_avail && FWTYPE=iptables
}
linux_nft_avail()
{
exists nft
}
linux_fwtype()
{
[ -n "$FWTYPE" ] && return
FWTYPE=unsupported
linux_get_subsys
if [ "$SUBSYS" = openwrt ] ; then
# linux kernel is new enough if fw4 is there
if [ -x /sbin/fw4 ] && linux_nft_avail ; then
FWTYPE=nftables
else
linux_maybe_iptables_fwtype
fi
else
SUBSYS=
# generic linux
# flowtable is implemented since kernel 4.16
if linux_nft_avail && linux_min_version 4 16; then
FWTYPE=nftables
else
linux_maybe_iptables_fwtype
fi
fi
export FWTYPE
}
get_fwtype()
{
[ -n "$FWTYPE" ] && return
local UNAME="$(uname)"
case "$UNAME" in
Linux)
linux_fwtype
;;
FreeBSD)
if exists ipfw ; then
FWTYPE=ipfw
else
FWTYPE=unsupported
fi
;;
*)
FWTYPE=unsupported
;;
esac
export FWTYPE
}

39
common/virt.sh Normal file
View File

@@ -0,0 +1,39 @@
get_virt()
{
local vm s v UNAME
UNAME=$(uname)
case "$UNAME" in
Linux)
if exists systemd-detect-virt; then
vm=$(systemd-detect-virt --vm)
elif [ -f /sys/class/dmi/id/product_name ]; then
read s </sys/class/dmi/id/product_name
for v in KVM QEMU VMware VMW VirtualBox Xen Bochs Parallels BHYVE Hyper-V; do
case "$s" in
"$v"*)
vm=$v
break
;;
esac
done
fi
;;
esac
echo "$vm" | awk '{print tolower($0)}'
}
check_virt()
{
echo \* checking virtualization
local vm="$(get_virt)"
if [ -n "$vm" ]; then
if [ "$vm" = "none" ]; then
echo running on bare metal
else
echo "!!! WARNING. $vm virtualization detected !!!"
echo '!!! WARNING. vmware and virtualbox are known to break most of the DPI bypass techniques when network is NATed using internal hypervisor NAT !!!'
echo '!!! WARNING. if this is your case make sure you are bridged not NATed !!!'
fi
else
echo cannot detect
fi
}

123
config.default Normal file
View File

@@ -0,0 +1,123 @@
# this file is included from init scripts
# change values here
# can help in case /tmp has not enough space
#TMPDIR=/opt/zapret/tmp
# redefine user for zapret daemons. required on Keenetic
#WS_USER=nobody
# override firewall type : iptables,nftables,ipfw
#FWTYPE=iptables
# nftables only : set this to 0 to use pre-nat mode. default is post-nat.
# pre-nat mode disables some bypass techniques for forwarded traffic but allows to see client IP addresses in debug log
#POSTNAT=0
# options for ipsets
# maximum number of elements in sets. also used for nft sets
SET_MAXELEM=522288
# too low hashsize can cause memory allocation errors on low RAM systems , even if RAM is enough
# too large hashsize will waste lots of RAM
IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
# dynamically generate additional ip. $1 = ipset/nfset/table name
#IPSET_HOOK="/etc/zapret.ipset.hook"
# options for ip2net. "-4" or "-6" auto added by ipset create script
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5"
# options for auto hostlist
AUTOHOSTLIST_RETRANS_THRESHOLD=3
AUTOHOSTLIST_FAIL_THRESHOLD=3
AUTOHOSTLIST_FAIL_TIME=60
# 1 = debug autohostlist positives to ipset/zapret-hosts-auto-debug.log
AUTOHOSTLIST_DEBUGLOG=0
# number of parallel threads for domain list resolves
MDIG_THREADS=30
# ipset/*.sh can compress large lists
GZIP_LISTS=1
# command to reload ip/host lists after update
# comment or leave empty for auto backend selection : ipset or ipfw if present
# on BSD systems with PF no auto reloading happens. you must provide your own command
# set to "-" to disable reload
#LISTS_RELOAD="pfctl -f /etc/pf.conf"
# mark bit used by nfqws to prevent loop
DESYNC_MARK=0x40000000
DESYNC_MARK_POSTNAT=0x20000000
# do not pass outgoing traffic to tpws/nfqws not marked with this bit
# this setting allows to write your own rules to limit traffic that should be fooled
# for example based on source IP or incoming interface name
# no filter if not defined
#FILTER_MARK=0x10000000
NFQWS2_ENABLE=0
# redirect outgoing traffic with connbytes limiter applied in both directions.
NFQWS2_PORTS_TCP=80,443
NFQWS2_PORTS_UDP=443
# PKT_OUT means connbytes dir original
# PKT_IN means connbytes dir reply
# this is --dpi-desync-cutoff=nX kernel mode implementation for linux. it saves a lot of CPU.
NFQWS2_TCP_PKT_OUT=$((6+$AUTOHOSTLIST_RETRANS_THRESHOLD))
NFQWS2_TCP_PKT_IN=3
NFQWS2_UDP_PKT_OUT=$((6+$AUTOHOSTLIST_RETRANS_THRESHOLD))
NFQWS2_UDP_PKT_IN=0
# redirect outgoing traffic without connbytes limiter and incoming with connbytes limiter
# normally it's needed only for stateless DPI that matches every packet in a single TCP session
# typical example are plain HTTP keep alives
# this mode can be very CPU consuming. enable with care !
#NFQWS2_PORTS_TCP_KEEPALIVE=80
#NFQWS2_PORTS_UDP_KEEPALIVE=
# use <HOSTLIST> and <HOSTLIST_NOAUTO> placeholders to engage standard hostlists and autohostlist in ipset dir
# hostlist markers are replaced to empty string if MODE_FILTER does not satisfy
# <HOSTLIST_NOAUTO> appends ipset/zapret-hosts-auto.txt as normal list
NFQWS2_OPT="
--filter-tcp=80 --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 --lua-desync=multisplit:pos=method+2 <HOSTLIST> --new
--filter-tcp=443 --payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000 --lua-desync=multidisorder:pos=1,midsld <HOSTLIST> --new
--filter-udp=443 --payload=quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=6 <HOSTLIST_NOAUTO> --new
"
# none,ipset,hostlist,autohostlist
MODE_FILTER=none
# donttouch,none,software,hardware
FLOWOFFLOAD=donttouch
# openwrt: specify networks to be treated as WAN. default wans are interfaces with default route
#OPENWRT_WAN4="wan vpn"
#OPENWRT_WAN6="wan6 vpn6"
# for routers based on desktop linux and macos. has no effect in openwrt.
# optionally CHOOSE WAN/WAN6 NETWORK INTERFACES
# or leave them commented if its not router
# it's possible to specify multiple interfaces like this : IFACE_WAN="eth0 eth1 eth2"
# if IFACE_WAN6 is not defined it take the value of IFACE_WAN
#IFACE_WAN=eth1
#IFACE_WAN6="ipsec0 wireguard0 he_net"
# should start/stop command of init scripts apply firewall rules ?
# not applicable to openwrt with firewall3+iptables
INIT_APPLY_FW=1
# firewall apply hooks
#INIT_FW_PRE_UP_HOOK="/etc/firewall.zapret.hook.pre_up"
#INIT_FW_POST_UP_HOOK="/etc/firewall.zapret.hook.post_up"
#INIT_FW_PRE_DOWN_HOOK="/etc/firewall.zapret.hook.pre_down"
#INIT_FW_POST_DOWN_HOOK="/etc/firewall.zapret.hook.post_down"
# do not work with ipv4
#DISABLE_IPV4=1
# do not work with ipv6
DISABLE_IPV6=1
# drop icmp time exceeded messages for nfqws tampered connections
# in POSTNAT mode this can interfere with default mtr/traceroute in tcp or udp mode. use source port not redirected to nfqws
# set to 0 if you are not expecting connection breakage due to icmp in response to TCP SYN or UDP
FILTER_TTL_EXPIRED_ICMP=1
# select which init script will be used to get ip or host list
# possible values : get_user.sh get_antizapret.sh get_combined.sh get_reestr.sh get_hostlist.sh
# comment if not required
#GETLIST=

View File

@@ -12,3 +12,25 @@ v0.1.2
* nfqws2: 'known' protocol and payload filter
* nfqws2: 'aes_ctr' luacall
* zapret-antidpi: rst
* github actions: remove FFI from luajit
v0.1.4
* winws2: set low mandatory level in process token if possible : no --wlan-filter or --nlm-filter (no windivert reinit required)
* nfqws2: optimize debug logging to file
v0.1.5
* nfqws2: # and % arg substitution
* zapret-antidpi: luaexec
* zapret-pcap: simple packet capture to .cap file
v0.2
* blockcheck2
* nfqws2: several crash fixes
* nfqws2: bu8,bu16,bu24,bu32,swap16,swap32 functions now work with negative int
* nfqws2: getpid,gettid,uname,get_clock luacalls
* zapret-lib: bugfixes
* zapret-lib: remove ip6_hopbyhop_x2 fooling, separately add second hopbyhop header using ip6_hopbyhop2
* zapret-pcap

View File

@@ -1,4 +1,4 @@
# zapret2 v0.1
# zapret2 v0.2
## Зачем это нужно
@@ -115,10 +115,10 @@ nfqws --qnum 200 --debug \
Конкретный вызов `--lua-desync` функции называется инстансом. Инстанс - это связка имени функции, номера вызова внутри профиля и номера самого профиля.
Это похоже на одну программу, которую можно запустить много раз с разными параметрами.
Другое немаловажное отличие - поддержка автоматической tcp сегментации. Вам больше не нужно думать о размерах отсылаемых tcp пакетов.
Другое немаловажное отличие - поддержка автоматической tcp сегментации средствами `zapret-lib.lua`. Вам больше не нужно думать о размерах отсылаемых tcp пакетов.
По каждому соединению отслеживается MSS. Если пакет не влезает в MSS, выполняется сегментация.
Например, это может случиться при отправке tls фейка с kyber. Или если вы режете kyber tls так, что одна из частей получается размером 1600 байт,
что, очевидно, не влезает в MTU. Или если вы задали seqovl=10000. В *nfqws1* такое значение вызвало бы ошибку. В *nfqws2* будет отправлено
что, очевидно, не влезает в MTU. Или если вы задали seqovl=10000. В *nfqws1* такое значение вызвало бы ошибку. Функция LUA `rawsend_dissect_segmented` отправит
несколько tcp сегментов с начальным sequence -10000 общим размером 10000 байт, в последнем из которых будет кусок оригинального сообщения.
В *nfqws2* нет жестко зашитых параметров кастомных фейков типа `--dpi-desync-fake-tls`, `dpi-desync-fake-http` и тд.
@@ -365,6 +365,37 @@ start "zapret: http,https,quic" /min "%~dp0winws2.exe" ^
--lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2
```
И напоследок стоит продемонстрировать как делаются нестандартные вещи. То, что очень непросто запрограммировать в чисто описательном виде
в фиксированном коде, не превращая программу в монстро-комбайн, перегруженный частными функциями и разваливающийся под своей тяжестью со временем,
когда эти частные функции перестают быть нужны и забываются.
Надо послать исходный запрос с известным пейлоадом с seqovl случайного размера от 5 до 10 символов со случайным содержимым, состоящим из букв от a до z.
Здесь раскрывается не декларативный характер стратегий, а алгоритмический. Стратегия - это программа, и пишите ее вы на языке программирования.
Для облегчения простых или стандартных действий есть готовые средства, так что далеко не всегда надо писать свою функцию.
Частенько можно обойтись простенькими кусками LUA кода в дополнение к имеющимся.
Здесь используется функция `luaexec`, предназначенная для динамического выполнения LUA кода в процессе обработки текущего диссекта.
Она инициализирует требуемый blob, записывая его в таблицу desync, которая передается от инстанса к инстансу.
Следующий инстанс `tcpseg` использует `rnd` как blob - источник seqovl паттерна.
Символы `%` и `#` используются для разименования блобов и подстановки их размера соответственно. Реализовано на уровне C кода.
desync функция получает уже подставленные значения. В данном случае seqovl устанавливается как размер сгенерированного блоба.
Функция `tcpseg` предназначена для отсылки tcp сегмента - части текущего пейлоада (или реасма - сборки нескольких пакетов, например в случае tls kyber).
`pos=0,-1` - это диапазон, состоящий из двух маркеров - начала и конца. 0 - положительный абсолютный маркер, соответствующий началу пакета.
-1 - отрицательный абсолютный маркер, соответствующий концу пакета. Получается, мы отсылаем целиком текущий пейлоад, но с seqovl.
`tcpseg` не дропает пакет. Его надо дропнуть отдельно. По умолчанию `tcpseg` работает только с известными пейлоадами, а функция `drop` - с любыми.
Поэтому нужно ей указать дропать только известные пейлоады.
Такая связка из 3 инстансов решает поставленную задачу без кучи частных параметров вида `--dpi-desync...`.
```
nfqws2 \
--lua-desync=luaexec:code='desync.rnd=brandom_az(math.random(5,10))' \
--lua-desync=tcpseg:pos=0,-1:seqovl=#rnd:seqovl_pattern=rnd \
--lua-desync=drop:payload=known
```
### Какие есть еще параметры
Как узнать какие есть еще функции и какие у них бывают параметры ? Смотрите `zapret-antidpi.lua`. Перед каждой функцией подробно описано какие параметры она берет.

View File

@@ -25,8 +25,9 @@ standard fooling :
* ip6_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
* ip6_hopbyhop[=hex] - add hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_hopbyhop2[=hex] - add 2 hopbyhop ipv6 headers with optional data. data size must be 6+N*8. all zero by default.
* ip6_hopbyhop2[=hex] - add second hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_destopt[=hex] - add destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_destopt2[=hex] - add second destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_routing[=hex] - add routing ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_ah[=hex] - add authentication ipv6 header with optional data. data size must be 6+N*4. 0000 + 4 random bytes by default.
@@ -35,7 +36,7 @@ standard fooling :
* tcp_ts=N - add N to timestamp value
* tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
* tcp_flags_set=<list> - set tcp flags in comma separated list
* tcp_unflags_set=<list> - unset tcp flags in comma separated list
* tcp_flags_unset=<list> - unset tcp flags in comma separated list
* tcp_ts_up - move timestamp tcp option to the top if present (workaround for badack without badseq fooling)
* fool=fool_function - custom fooling function : fool_func(dis, fooling_options)
@@ -70,18 +71,6 @@ standard ipfrag :
]]
-- dummy test function. does nothing.
-- no args
function pass(ctx, desync)
DLOG("pass")
end
-- prints desync to DLOG
function pktdebug(ctx, desync)
DLOG("desync:")
var_debug(desync)
end
-- drop packet
-- standard args : direction, payload
function drop(ctx, desync)
@@ -378,6 +367,8 @@ end
-- arg : pos=<posmarker list> . position marker list. for example : "1,host,midsld+1,-10"
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
-- arg : seqovl_pattern=<blob> . override pattern
-- arg : blob=<blob> - use this data instead of desync.dis.payload
-- arg : nodrop - do not drop current dissect
function multisplit(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
@@ -385,7 +376,7 @@ function multisplit(ctx, desync)
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
@@ -411,7 +402,7 @@ function multisplit(ctx, desync)
end
end
replay_drop_set(desync)
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
else
DLOG("multisplit: no valid split positions")
end
@@ -420,7 +411,7 @@ function multisplit(ctx, desync)
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
@@ -430,6 +421,8 @@ end
-- arg : pos=<postmarker list> . position marker list. example : "1,host,midsld+1,-10"
-- arg : seqovl=N . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N must be less than the first split pos.
-- arg : seqovl_pattern=<blob> . override pattern
-- arg : blob=<blob> - use this data instead of desync.dis.payload
-- arg : nodrop - do not drop current dissect
function multidisorder(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
@@ -437,7 +430,7 @@ function multidisorder(ctx, desync)
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
@@ -474,7 +467,7 @@ function multidisorder(ctx, desync)
end
end
replay_drop_set(desync)
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
else
DLOG("multidisorder: no valid split positions")
end
@@ -483,7 +476,7 @@ function multidisorder(ctx, desync)
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
@@ -494,6 +487,8 @@ end
-- arg : midhost=<posmarker> - additionally split segment containing host at specified posmarker. must be within host+1 .. endhost-1 or split won't happen. example : "midsld"
-- arg : nofake1, nofake2 - do not send individual fakes
-- arg : disorder_after=<posmarker> - send after_host part in 2 disordered segments. if posmarker is empty string use marker "-1"
-- arg : blob=<blob> - use this data instead of desync.dis.payload
-- arg : nodrop - do not drop current dissect
function hostfakesplit(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
@@ -501,7 +496,7 @@ function hostfakesplit(ctx, desync)
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local pos = resolve_range(data, desync.l7payload, "host,endhost-1", true)
@@ -584,7 +579,7 @@ function hostfakesplit(ctx, desync)
if not rawsend_payload_segmented(desync,part,pos[2], opts_orig) then return VERDICT_PASS end
replay_drop_set(desync)
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
else
DLOG("hostfakesplit: host range cannot be resolved")
end
@@ -593,7 +588,7 @@ function hostfakesplit(ctx, desync)
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
@@ -605,6 +600,8 @@ end
-- arg : pattern=<blob> . fill fake parts with this pattern
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
-- arg : seqovl_pattern=<blob> . override seqovl pattern
-- arg : blob=<blob> - use this data instead of desync.dis.payload
-- arg : nodrop - do not drop current dissect
function fakedsplit(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
@@ -612,7 +609,7 @@ function fakedsplit(ctx, desync)
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
@@ -674,7 +671,7 @@ function fakedsplit(ctx, desync)
end
replay_drop_set(desync)
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
else
DLOG("fakedsplit: cannot resolve pos '"..desync.arg.pos.."'")
@@ -684,7 +681,7 @@ function fakedsplit(ctx, desync)
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
@@ -696,6 +693,8 @@ end
-- arg : pattern=<blob> . fill fake parts with this pattern
-- arg : seqovl=N . decrease seq number of the second segment by N and fill N bytes with pattern (default - all zero). N must be less than the split pos.
-- arg : seqovl_pattern=<blob> . override seqovl pattern
-- arg : blob=<blob> - use this data instead of desync.dis.payload
-- arg : nodrop - do not drop current dissect
function fakeddisorder(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
@@ -703,7 +702,7 @@ function fakeddisorder(ctx, desync)
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
@@ -775,7 +774,7 @@ function fakeddisorder(ctx, desync)
end
replay_drop_set(desync)
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
else
DLOG("fakeddisorder: cannot resolve pos '"..desync.arg.pos.."'")
@@ -785,7 +784,7 @@ function fakeddisorder(ctx, desync)
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
@@ -795,6 +794,7 @@ end
-- arg : pos=<postmarker list> . position marker list. 2 pos required, only 2 first pos used. example : "host,endhost"
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
-- arg : seqovl_pattern=<blob> . override pattern
-- arg : blob=<blob> - use this data instead of desync.dis.payload
function tcpseg(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
@@ -805,7 +805,7 @@ function tcpseg(ctx, desync)
error("tcpseg: no pos specified")
end
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
if b_debug then DLOG("tcpseg: pos: "..desync.arg.pos) end

View File

@@ -3,6 +3,41 @@ NOT3=bitnot(3)
NOT7=bitnot(7)
math.randomseed(os.time())
-- basic desync function
-- execute given lua code. "desync" is temporary set as global var to be accessible to the code
-- useful for simple fast actions without writing a func
-- arg: code=<lua_code>
function luaexec(ctx, desync)
if not desync.arg.code then
error("luaexec: no 'code' parameter")
end
local fname = desync.func_instance.."_luaexec_code"
if not _G[fname] then
_G[fname] = load(desync.arg.code, fname)
end
-- allow dynamic code to access desync
_G.desync = desync
_G[fname]()
_G.desync = nil
end
-- basic desync function
-- does nothing just acknowledges when it's called
-- no args
function pass(ctx, desync)
DLOG("pass")
end
-- basic desync function
-- prints desync to DLOG
function pktdebug(ctx, desync)
DLOG("desync:")
var_debug(desync)
end
-- prepare standard rawsend options from desync
-- repeats - how many time send the packet
-- ifout - override outbound interface (if --bind_fix4, --bind-fix6 enabled)
@@ -163,6 +198,9 @@ function blob(desync, name, def)
end
return blob
end
function blob_or_def(desync, name, def)
return name and blob(desync,name,def) or def
end
-- repeat pattern as needed to extract part of it with any length
-- pat="12345" len=10 offset=4 => "4512345123"
@@ -413,7 +451,7 @@ end
-- ip6_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
-- ip6_hopbyhop[=hex] - add hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_hopbyhop2 - add 2 hopbyhop ipv6 headers with optional data. data size must be 6+N*8. all zero by default.
-- ip6_hopbyhop2[=hex] - add second hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_destopt[=hex] - add destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_routing[=hex] - add routing ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_ah[=hex] - add authentication ipv6 header with optional data. data size must be 6+N*4. 0000 + 4 random bytes by default.
@@ -423,7 +461,7 @@ end
-- tcp_ts=N - add N to timestamp value
-- tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
-- tcp_flags_set=<list> - set tcp flags in comma separated list
-- tcp_unflags_set=<list> - unset tcp flags in comma separated list
-- tcp_flags_unset=<list> - unset tcp flags in comma separated list
-- tcp_ts_up - move timestamp tcp option to the top if it's present. this allows linux not to accept badack segments without badseq. this is very strange discovery but it works.
-- fool - custom fooling function : fool_func(dis, fooling_options)
@@ -459,6 +497,7 @@ function apply_fooling(desync, dis, fooling_options)
if not ttl and tonumber(arg_ttl) then
ttl = tonumber(arg_ttl)
end
--io.stderr:write("TTL "..tostring(ttl).."\n")
return ttl
end
local function move_ts_top()
@@ -506,14 +545,14 @@ function apply_fooling(desync, dis, fooling_options)
end
if dis.ip6 then
local bin
if fooling_options.ip6_hopbyhop_x2 then
bin = prepare_bin(fooling_options.ip6_hopbyhop2_x2,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
elseif fooling_options.ip6_hopbyhop then
if fooling_options.ip6_hopbyhop then
bin = prepare_bin(fooling_options.ip6_hopbyhop,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
end
if fooling_options.ip6_hopbyhop2 then
bin = prepare_bin(fooling_options.ip6_hopbyhop2,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
end
-- for possible unfragmentable part
if fooling_options.ip6_destopt then
bin = prepare_bin(fooling_options.ip6_destopt,"\x00\x00\x00\x00\x00\x00")
@@ -666,6 +705,7 @@ function packet_len(dis)
return l3l4_len(dis) + #dis.payload
end
-- option : ipfrag.ipfrag_disorder - send fragments from last to first
function rawsend_dissect_ipfrag(dis, options)
if options and options.ipfrag and options.ipfrag.ipfrag then
local frag_func = options.ipfrag.ipfrag=="" and "ipfrag2" or options.ipfrag.ipfrag
@@ -708,17 +748,18 @@ function rawsend_dissect_segmented(desync, dis, mss, options)
apply_fooling(desync, discopy, options and options.fooling)
if dis.tcp then
local extra_len = l3l4_extra_len(dis)
local extra_len = l3l4_extra_len(discopy)
if extra_len >= mss then return false end
local max_data = mss - extra_len
if #discopy.payload > max_data then
local pos=1
local len
local payload=discopy.payload
while pos <= #dis.payload do
len = #dis.payload - pos + 1
while pos <= #payload do
len = #payload - pos + 1
if len > max_data then len = max_data end
discopy.payload = string.sub(dis.payload,pos,pos+len-1)
discopy.payload = string.sub(payload,pos,pos+len-1)
if not rawsend_dissect_ipfrag(discopy, options) then
-- stop if failed
return false
@@ -842,6 +883,20 @@ function genhost(len, template)
end
end
function is_absolute_path(path)
if string.sub(path,1,1)=='/' then return true end
local un = uname()
return string.sub(un.sysname,1,6)=="CYGWIN" and string.sub(path,2,2)==':'
end
function append_path(path,file)
return string.sub(path,#path,#path)=='/' and path..file or path.."/"..file
end
function writeable_file_name(filename)
if is_absolute_path(filename) then return filename end
local writedir = os.getenv("WRITEABLE")
if not writedir then return filename end
return append_path(writedir, filename)
end
-- arg : wsize=N . tcp window size
-- arg : scale=N . tcp option scale factor
@@ -876,7 +931,6 @@ end
-- option : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8
-- option : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
-- option : ipfrag_disorder - send fragments from last to first
function ipfrag2(dis, ipfrag_options)
local function frag_idx(exthdr)
-- fragment header after hopbyhop, destopt, routing

39
lua/zapret-pcap.lua Normal file
View File

@@ -0,0 +1,39 @@
function pcap_write_header(file)
-- big endian, nanoseconds in timestamps, ver 2.4, max packet size - 0x4000 (16384), 0x65 - l3 packets without l2
file:write("\xA1\xB2\x3C\x4D\x00\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x65")
end
function pcap_write_packet(file, raw)
local sec, nsec = clock_gettime();
file:write(bu32(sec)..bu32(nsec)..bu32(#raw)..bu32(#raw))
file:write(raw)
file:close()
end
function pcap_write(file, raw)
local pos = file:seek()
if (pos==0) then
pcap_write_header(file)
end
pcap_write_packet(file, raw)
end
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-pcap.lua --writeable=zdir --in-range=a --lua-desync=pcap:file=test.pcap
-- arg : file=<filename> - file for storing pcap data. if --writeable is specified and filename is relative - append filename to writeable path
-- arg : keep - do not overwrite file, append packets to existing
function pcap(ctx, desync)
if not desync.arg.file or #desync.arg.file==0 then
error("pcap requires 'file' parameter")
end
local fn_cache_name = desync.func_instance.."_fn"
if not _G[fn_cache_name] then
_G[fn_cache_name] = writeable_file_name(desync.arg.file)
if not desync.arg.keep then
-- overwrite file
os.remove(_G[fn_cache_name])
end
end
local f = io.open(_G[fn_cache_name], "a")
if not f then
error("pcap: could not write to '".._G[fn_cache_name].."'")
end
pcap_write(f, raw_packet(ctx))
end

View File

@@ -5,20 +5,20 @@ function test_assert(b)
assert(b, "test failed")
end
function test_run(tests)
function test_run(tests,...)
for k,f in pairs(tests) do
f()
f(...)
end
end
function test_all()
test_run({test_crypto, test_bin, test_ipstr, test_dissect, test_csum, test_resolve, test_rawsend})
function test_all(...)
test_run({test_crypto, test_bin, test_ipstr, test_dissect, test_csum, test_resolve, test_rawsend},...)
end
function test_crypto()
test_run({test_random, test_aes, test_aes_gcm, test_aes_ctr, test_hkdf, test_hash})
function test_crypto(...)
test_run({test_random, test_aes, test_aes_gcm, test_aes_ctr, test_hkdf, test_hash},...)
end
function test_random()
@@ -211,19 +211,44 @@ function test_aes_ctr()
test_assert(decrypted==clear_text)
print("* decrypting with bad key")
decrypted = aes_ctr(bu8(u8(string.sub(key,1,1))+1)..string.sub(key,2), iv, encrypted)
decrypted = aes_ctr(bu8(bitand(u8(string.sub(key,1,1))+1,0xFF))..string.sub(key,2), iv, encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
print("* decrypting with bad iv")
decrypted = aes_ctr(key, bu8(u8(string.sub(iv,1,1))+1)..string.sub(iv,2), encrypted)
decrypted = aes_ctr(key, bu8(bitand(u8(string.sub(iv,1,1))+1,0xFF))..string.sub(iv,2), encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
end
end
-- openssl enc -aes-256-ctr -d -in rnd.bin -out rnd_decrypted.bin -K c39383634d87eb3b6e56edf2c8c0ba99cc8cadf000fb2cd737e37947eecde5fd -iv d745164b233f10b93945526ffe94b87f
print("* aes_ctr const tests")
local data="\x9d\x9c\xa0\x78\x2e\x17\x84\xfc\x87\xc7\xf5\xdf\x5b\xb5\x71\xfd\xb9\xcb\xd2\x4d\xae\x2f\xf0\x19\xf3\xad\x79\xa8\x9a\xb4\xed\x28\x88\x3c\xe1\x78\x91\x23\x27\xd4\x8d\x94\xb3\xd0\x81\x88\xd2\x55\x95\x8a\x88\x70\x67\x99\x75\xb2\xee\x30\x0f\xe7\xc6\x32\x10"
local iv="\xd7\x45\x16\x4b\x23\x3f\x10\xb9\x39\x45\x52\x6f\xfe\x94\xb8\x7f"
local tests = {
{
key="\xc3\x93\x83\x63\x4d\x87\xeb\x3b\x6e\x56\xed\xf2\xc8\xc0\xba\x99\xcc\x8c\xad\xf0\x00\xfb\x2c\xd7\x37\xe3\x79\x47\xee\xcd\xe5\xfd",
result="\x8C\x2C\x15\x99\x83\x37\x33\xEE\xA1\x70\xA7\x4A\x44\x2E\x6F\x56\x22\x41\xE1\xFC\xC5\x84\x21\x1C\x16\xC6\xE9\x75\x22\x57\x55\x4A\x02\x04\xCE\xAD\xE9\x0A\x45\xAB\x4E\x38\xB8\xB2\x6F\x95\xDA\x46\x4F\x9E\xB1\xFF\xF4\x40\x8A\x57\x25\xD2\xF6\xB6\x93\x65\x75"
},
{
key="\xc3\x93\x83\x63\x4d\x87\xeb\x3b\x6e\x56\xed\xf2\xc8\xc0\xba\x99\xcc\x8c\xad\xf0\x00\xfb\x2c\xd7",
result="\xB0\x4C\xC9\xDB\x0C\xE5\x67\x51\x1D\x24\x3C\x15\x87\x1B\xF9\x62\x84\x8C\xD0\x57\x33\x93\xE0\x71\x91\x3A\x11\x26\xCA\x77\xA7\x54\xBD\xC6\x5E\x96\x60\x2C\x94\x0F\xBA\x3E\x79\xDC\x48\xA0\x22\x97\xA7\x77\x55\xC8\x14\xEA\xC2\xF5\xA0\x88\x6F\xE2\x44\x32\x68"
},
{
key="\xc3\x93\x83\x63\x4d\x87\xeb\x3b\x6e\x56\xed\xf2\xc8\xc0\xba\x99",
result="\xD9\xAC\xC7\x7D\xC8\xC9\xF1\x59\x9A\xDF\x15\xF3\x58\x61\xFD\x2B\x1D\x01\x9A\x5F\x04\x53\xA2\xA8\xFD\x52\xDC\x8A\xE9\x3B\x2E\x5E\x0D\x13\xCB\xBD\x16\xED\xC1\xF2\x0D\x68\x62\xB7\xD5\x0F\x8D\xD4\xEB\xA1\xC5\x75\xF2\x0B\x26\x75\x1D\x7E\x5A\x37\xA6\x8A\xCD"
}
}
for k,t in pairs(tests) do
local decrypted = aes_ctr(t.key, iv, data)
io.write("KEY SIZE "..(#t.key*8).." ")
print( decrypted==t.result and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted==t.result)
end
end
function test_ub()
for k,f in pairs({{u8,bu8,0xFF,8}, {u16,bu16,0xFFFF,16}, {u24,bu24,0xFFFFFF,24}, {u32,bu32,0xFFFFFFFF,32}}) do
@@ -274,8 +299,8 @@ function test_bit()
test_assert(v2==v3)
end
function test_bin()
test_run({test_ub, test_bit})
function test_bin(...)
test_run({test_ub, test_bit},...)
end
@@ -536,9 +561,46 @@ function test_resolve()
print("resolve_pos http non-existent : "..tostring(pos))
end
function test_rawsend()
function test_rawsend(opts)
local ifout = (opts and opts.ifout) and opts.ifout
local function rawsend_fail_warning()
if not opts or not opts.ifout or #opts.ifout==0 then
local un = uname()
if string.sub(un.sysname,1,6)=="CYGWIN" then
print("windivert requires interface name in the form '<int>.<int>'. take it from winws2 output with '--debug' option and call test_rawsend({ifout=interface_name})")
end
end
end
local function rawsend_dissect_print(dis, options)
if options then
options.ifout = ifout
else
options = { ifout = ifout }
end
local b = rawsend_dissect(dis, options)
if not b then
print("rawsend_dissect failed")
rawsend_fail_warning()
end
return b
end
local function rawsend_print(raw, options)
if options then
options.ifout = ifout
else
options = { ifout = ifout }
end
print("rawsend: "..string2hex(raw))
local b = rawsend(raw, options)
if not b then
print("rawsend failed")
rawsend_fail_warning()
end
return b
end
local ip, ip6, udp, dis, ddis, raw_ip, raw_udp, raw
local payload = brandom(math.random(100,1200))
local b
ip = {
ip_tos = 0,
@@ -555,20 +617,21 @@ function test_rawsend()
}
dis = {ip = ip, udp = udp, payload = payload}
print("send ipv4 udp")
test_assert(rawsend_dissect(dis, {repeats=3}))
test_assert(rawsend_dissect_print(dis, {repeats=3}))
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv4 udp frag "..k)
test_assert(rawsend_dissect(d))
test_assert(rawsend_dissect_print(d))
end
raw_ip = reconstruct_iphdr(ip)
local ip2=ip
ip2.ip_len = IP_BASE_LEN + UDP_BASE_LEN + #payload
raw_ip = reconstruct_iphdr(ip2)
raw_udp = reconstruct_udphdr({uh_sport = udp.uh_sport, uh_dport = udp.uh_dport, uh_ulen = UDP_BASE_LEN + #payload})
raw_udp = csum_udp_fix(raw_ip,raw_udp,payload)
raw = raw_ip .. raw_udp .. payload
print("send ipv4 udp using pure rawsend without dissect")
test_assert(rawsend(raw, {repeats=5}))
test_assert(rawsend_print(raw, {repeats=5}))
ip6 = {
ip6_flow = 0x60000000,
@@ -578,22 +641,22 @@ function test_rawsend()
}
dis = {ip6 = ip6, udp = udp, payload = payload}
print("send ipv6 udp")
test_assert(rawsend_dissect(dis, {repeats=3}))
test_assert(rawsend_dissect_print(dis, {repeats=3}))
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k)
test_assert(rawsend_dissect(d))
test_assert(rawsend_dissect_print(d))
end
ip6.exthdr={{ type = IPPROTO_HOPOPTS, data = "\x00\x00\x00\x00\x00\x00" }}
print("send ipv6 udp with hopbyhop ext header")
test_assert(rawsend_dissect(dis, {repeats=3}))
test_assert(rawsend_dissect_print(dis, {repeats=3}))
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k.." with hopbyhop ext header")
test_assert(rawsend_dissect(d))
test_assert(rawsend_dissect_print(d))
end
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" })
@@ -602,7 +665,7 @@ function test_rawsend()
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k.." with hopbyhop, destopt ext headers in unfragmentable part and another destopt ext header in fragmentable part")
test_assert(rawsend_dissect(d, {fwmark = 0x50EA}))
test_assert(rawsend_dissect_print(d, {fwmark = 0x50EA}))
end
fix_ip6_next(ip6) -- required to forge next proto in the second fragment
@@ -612,6 +675,6 @@ function test_rawsend()
print("send ipv6 udp frag "..k.." with hopbyhop, destopt ext headers in unfragmentable part and another destopt ext header in fragmentable part. forge next proto in fragment header of the second fragment to TCP")
-- reconstruct dissect using next proto fields in the dissect. do not auto fix next proto chain.
-- by default reconstruct fixes next proto chain
test_assert(rawsend_dissect(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true}))
test_assert(rawsend_dissect_print(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true}))
end
end

View File

@@ -11,15 +11,12 @@ function wgobfs(ctx, desync)
local padmin = desync.arg.padmin and tonumber(desync.arg.padmin) or 0
local padmax = desync.arg.padmax and tonumber(desync.arg.padmax) or 16
local function genkey()
-- cache key in lua_state of conntrack is present
if desync.track and desync.track.lua_state.wgobfs_key then
key = desync.track.lua_state.wgobfs_key
end
-- cache key in a global var bound to instance name
local key_cache_name = desync.func_instance.."_key"
key = _G[key_cache_name]
if not key then
key = hkdf("sha256", "wgobfs_salt", desync.arg.secret, nil, 16)
if desync.track then
desync.track.lua_state.wgobfs_key = key
end
_G[key_cache_name] = key
end
return key
end

View File

@@ -3,46 +3,74 @@
#define AES_BLOCKLEN 16
// add 64-bit value to 16-byte big endian counter
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
void ctr_add(uint8_t *counter, uint64_t add)
{
#ifndef __BYTE_ORDER__
#error "__BYTE_ORDER__ not defined"
#endif
uint64_t *c = (uint64_t*)counter;
#if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
uint64_t sum = c[1] + add;
if (sum < c[1]) // overflow
c[0]++;
c[1] = sum;
#else
uint64_t lsw = __builtin_bswap64(c[1]);
uint64_t sum = lsw + add;
if (sum < lsw) // overflow
c[0] = __builtin_bswap64(__builtin_bswap64(c[0]) + 1);
c[1] = __builtin_bswap64(sum);
#endif
}
// increment 16-byte big endian counter
static inline void ctr_increment(uint8_t *counter)
{
for (int8_t bi = (AES_BLOCKLEN - 1); (bi >= 0) && !++counter[bi]; bi--);
}
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
void aes_ctr_xcrypt_buffer(aes_context *ctx, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out)
{
uint8_t bi, buffer[AES_BLOCKLEN], ivc[AES_BLOCKLEN];
size_t i;
uint8_t bi, ivc[AES_BLOCKLEN], buffer[AES_BLOCKLEN];
size_t i, l16 = length & ~0xF;
memcpy(ivc,iv,AES_BLOCKLEN);
memcpy(ivc, iv, AES_BLOCKLEN);
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
for (i = 0; i < l16; i += 16)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor complement in buffer */
{
memcpy(buffer, ivc, AES_BLOCKLEN);
aes_cipher(ctx, buffer, buffer);
aes_cipher(ctx, ivc, buffer);
ctr_increment(ivc);
*((uint64_t*)(out + i)) = *((uint64_t*)(in + i)) ^ ((uint64_t*)buffer)[0];
*((uint64_t*)(out + i + 8)) = *((uint64_t*)(in + i + 8)) ^ ((uint64_t*)buffer)[1];
}
/* Increment ivc and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will owerflow */
if (ivc[bi] == 255)
{
ivc[bi] = 0;
continue;
}
ivc[bi] += 1;
break;
}
bi = 0;
}
out[i] = in[i] ^ buffer[bi];
if (i<length)
{
memcpy(buffer, ivc, AES_BLOCKLEN);
aes_cipher(ctx, buffer, buffer);
for (bi=0 ; i < length; i++, bi++)
out[i] = in[i] ^ buffer[bi];
}
}
int aes_ctr_crypt(const uint8_t *key, const size_t key_len, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out)
int aes_ctr_crypt(const uint8_t *key, unsigned int key_len, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out)
{
int ret=0;
int ret = 0;
aes_context ctx;
aes_init_keygen_tables();
if (!(ret=aes_setkey(&ctx, AES_ENCRYPT, key, key_len)))
if (!(ret = aes_setkey(&ctx, AES_ENCRYPT, key, key_len)))
aes_ctr_xcrypt_buffer(&ctx, iv, in, length, out);
return ret;

View File

@@ -4,4 +4,5 @@
#include "aes.h"
void aes_ctr_xcrypt_buffer(aes_context *ctx, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out);
int aes_ctr_crypt(const uint8_t *key, const size_t key_len, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out);
int aes_ctr_crypt(const uint8_t *key, unsigned int key_len, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out);
void ctr_add(uint8_t *counter, uint64_t add);

View File

@@ -5,6 +5,8 @@ int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_
int ret = 0;
gcm_context ctx;
gcm_initialize();
if (!(ret = gcm_setkey(&ctx, key, (const uint)key_len)))
{
ret = gcm_crypt_and_tag(&ctx, mode, iv, iv_len, adata, adata_len, input, output, input_length, atag, atag_len);

View File

@@ -20,8 +20,14 @@
#include "nfqws.h"
#ifdef __CYGWIN__
#include <sys/cygwin.h>
#include <wlanapi.h>
#include <netlistmgr.h>
#include <aclapi.h>
#include <wchar.h>
#include <KnownFolders.h>
#include <shlobj.h>
#ifndef ERROR_INVALID_IMAGE_HASH
#define ERROR_INVALID_IMAGE_HASH __MSABI_LONG(577)
@@ -88,26 +94,22 @@ uint8_t tcp_find_scale_factor(const struct tcphdr *tcp)
if (scale && scale[1]==3) return scale[2];
return SCALE_NONE;
}
bool tcp_has_fastopen(const struct tcphdr *tcp)
{
uint8_t *opt;
// new style RFC7413
opt = tcp_find_option((struct tcphdr*)tcp, TCP_KIND_FASTOPEN);
if (opt) return true;
// old style RFC6994
opt = tcp_find_option((struct tcphdr*)tcp, 254);
return opt && opt[1]>=4 && opt[2]==0xF9 && opt[3]==0x89;
}
uint16_t tcp_find_mss(const struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option((struct tcphdr *)tcp, TCP_KIND_MSS);
return (t && t[1]==4) ? *(uint16_t*)(t+2) : 0;
}
bool tcp_has_sack(struct tcphdr *tcp)
bool tcp_synack_segment(const struct tcphdr *tcphdr)
{
uint8_t *t = tcp_find_option(tcp, TCP_KIND_SACK_PERM);
return !!t;
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN));
}
bool tcp_syn_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_SYN);
}
void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport)
{
@@ -549,67 +551,19 @@ void proto_dissect_l3l4(const uint8_t *data, size_t len, struct dissect *dis)
}
bool tcp_synack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN));
}
bool tcp_syn_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_SYN);
}
bool tcp_ack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_ACK);
}
void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor)
{
uint8_t *scale,scale_factor_old;
if (scale_factor!=SCALE_NONE)
{
scale = tcp_find_option(tcp,3); // tcp option 3 - scale factor
if (scale && scale[1]==3) // length should be 3
{
scale_factor_old=scale[2];
// do not allow increasing scale factor
if (scale_factor>=scale_factor_old)
DLOG("Scale factor %u unchanged\n", scale_factor_old);
else
{
scale[2]=scale_factor;
DLOG("Scale factor change %u => %u\n", scale_factor_old, scale_factor);
}
}
}
}
// scale_factor=SCALE_NONE - do not change
void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor)
{
uint16_t winsize_old;
winsize_old = htons(tcp->th_win); // << scale_factor;
tcp->th_win = htons(winsize);
DLOG("Window size change %u => %u\n", winsize_old, winsize);
tcp_rewrite_wscale(tcp, scale_factor);
}
uint8_t ttl46(const struct ip *ip, const struct ip6_hdr *ip6)
{
return ip ? ip->ip_ttl : ip6 ? ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim : 0;
}
#ifdef __CYGWIN__
uint32_t w_win32_error=0;
static BOOL RemoveTokenPrivs()
static BOOL RemoveTokenPrivs(void)
{
BOOL bRes = FALSE;
HANDLE hToken;
@@ -643,15 +597,153 @@ static BOOL RemoveTokenPrivs()
if (!bRes) w_win32_error = GetLastError();
return bRes;
}
static BOOL WinSandbox()
static SID_IDENTIFIER_AUTHORITY label_authority = SECURITY_MANDATORY_LABEL_AUTHORITY;
BOOL LowMandatoryLevel(void)
{
// unfortunately there's no way to remove or disable Administrators group in the current process's token
// only possible run child process with restricted token
// but at least it's possible to permanently remove privileges
// this is not much but better than nothing
return RemoveTokenPrivs();
BOOL bRes = FALSE;
HANDLE hToken;
char buf1[32];
TOKEN_MANDATORY_LABEL label_low;
label_low.Label.Sid = (PSID)buf1;
InitializeSid(label_low.Label.Sid, &label_authority, 1);
label_low.Label.Attributes = 0;
*GetSidSubAuthority(label_low.Label.Sid, 0) = SECURITY_MANDATORY_LOW_RID;
// S-1-16-12288 : Mandatory Label\High Mandatory Level
// S-1-16-8192 : Mandatory Label\Medium Mandatory Level
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, &hToken))
{
bRes = SetTokenInformation(hToken, TokenIntegrityLevel, &label_low, sizeof(label_low));
CloseHandle(hToken);
}
if (!bRes) w_win32_error = GetLastError();
return bRes;
}
BOOL SetMandatoryLabelFile(LPCSTR lpFileName, DWORD dwMandatoryLabelRID, DWORD dwAceFlags)
{
BOOL bRes=FALSE;
DWORD dwErr, dwFileAttributes;
char buf_label[16], buf_pacl[32];
PSID label = (PSID)buf_label;
PACL pacl = (PACL)buf_pacl;
LPWSTR lpFileNameW = NULL;
size_t szFileName;
szFileName = strlen(lpFileName);
if (!(lpFileNameW = (LPWSTR)LocalAlloc(LMEM_FIXED,(szFileName+1)*sizeof(WCHAR))))
goto err;
if (!MultiByteToWideChar(CP_UTF8, 0, lpFileName, -1, lpFileNameW, szFileName+1))
goto err;
if (!strncmp(lpFileName,"\\\\.\\",4))
dwFileAttributes = 0;
else
{
dwFileAttributes = GetFileAttributesW(lpFileNameW);
if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) goto err;
}
InitializeSid(label, &label_authority, 1);
*GetSidSubAuthority(label, 0) = dwMandatoryLabelRID;
if (InitializeAcl(pacl, sizeof(buf_pacl), ACL_REVISION) && AddMandatoryAce(pacl, (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? ACL_REVISION_DS : ACL_REVISION, dwAceFlags, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, label))
{
dwErr = SetNamedSecurityInfoW(lpFileNameW, SE_FILE_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pacl);
SetLastError(dwErr);
bRes = dwErr==ERROR_SUCCESS;
}
err:
if (!bRes) w_win32_error = GetLastError();
LocalFree(lpFileNameW);
return bRes;
}
BOOL SetMandatoryLabelFileW(LPCWSTR lpFileNameW, DWORD dwMandatoryLabelRID, DWORD dwAceFlags)
{
BOOL bRes=FALSE;
DWORD dwErr, dwFileAttributes;
char buf_label[16], buf_pacl[32];
PSID label = (PSID)buf_label;
PACL pacl = (PACL)buf_pacl;
if (!wcsncmp(lpFileNameW,L"\\\\.\\",4))
dwFileAttributes = 0;
else
{
dwFileAttributes = GetFileAttributesW(lpFileNameW);
if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) goto err;
}
InitializeSid(label, &label_authority, 1);
*GetSidSubAuthority(label, 0) = dwMandatoryLabelRID;
if (InitializeAcl(pacl, sizeof(buf_pacl), ACL_REVISION) && AddMandatoryAce(pacl, (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? ACL_REVISION_DS : ACL_REVISION, dwAceFlags, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, label))
{
dwErr = SetNamedSecurityInfoW((LPWSTR)lpFileNameW, SE_FILE_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pacl);
SetLastError(dwErr);
bRes = dwErr==ERROR_SUCCESS;
}
err:
if (!bRes) w_win32_error = GetLastError();
return bRes;
}
bool ensure_file_access(const char *filename)
{
return SetMandatoryLabelFile(filename, SECURITY_MANDATORY_LOW_RID, 0);
}
bool ensure_dir_access(const char *dir)
{
return SetMandatoryLabelFile(dir, SECURITY_MANDATORY_LOW_RID, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
}
bool prepare_low_appdata()
{
bool b = false;
PWSTR pszPath = NULL;
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_LocalAppDataLow, 0, NULL, &pszPath);
if (SUCCEEDED(hr))
{
size_t l = cygwin_conv_path(CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, pszPath, NULL, 0);
char *buf = (char*)malloc(l+8);
if (buf)
{
if (!cygwin_conv_path(CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, pszPath, buf, l))
{
b = true;
setenv("APPDATALOW", buf, 1);
}
free(buf);
}
CoTaskMemFree(pszPath);
}
return b;
}
#define WINDIVERT_DEVICE_NAME "WinDivert"
static bool b_isandbox_set = false;
bool win_sandbox(void)
{
// there's no way to return privs
if (!b_isandbox_set)
{
if (!RemoveTokenPrivs())
return FALSE;
// set low mandatory label on windivert device to allow administrators with low label access the driver
if (logical_net_filter_present() && !SetMandatoryLabelFile("\\\\.\\" WINDIVERT_DEVICE_NAME, SECURITY_MANDATORY_LOW_RID, 0))
return FALSE;
if (!LowMandatoryLevel())
return false;
// for LUA code to find where to store files
b_isandbox_set = true;
}
return true;
}
static HANDLE w_filter = NULL;
static OVERLAPPED ovl = { .hEvent = NULL };
@@ -745,8 +837,6 @@ bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_lis
if (LIST_EMPTY(ssid_filter)) ssid_filter=NULL;
if (LIST_EMPTY(nlm_filter)) nlm_filter=NULL;
if (!WinSandbox()) return false;
if (nlm_filter)
{
if (SUCCEEDED(w_win32_error = CoInitialize(NULL)))
@@ -1004,6 +1094,13 @@ bool logical_net_filter_match(void)
return wlan_filter_match(wlan_filter_ssid) && nlm_filter_match(nlm_filter_net);
}
bool logical_net_filter_present(void)
{
return (wlan_filter_ssid && !LIST_EMPTY(wlan_filter_ssid)) || (nlm_filter_net && !LIST_EMPTY(nlm_filter_net));
}
static bool logical_net_filter_match_rate_limited(void)
{
DWORD dwTick = GetTickCount() / 1000;
@@ -1093,14 +1190,17 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len,
return false;
}
usleep(0);
if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, NULL, &ovl))
{
*len = recv_len;
return true;
}
for(;;)
{
w_win32_error = GetLastError();
switch(w_win32_error)
{
case ERROR_IO_PENDING:
@@ -1156,6 +1256,8 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const
{
WINDIVERT_ADDRESS wa;
if (!ifout) return false;
memset(&wa,0,sizeof(wa));
// pseudo interface id IfIdx.SubIfIdx
if (sscanf(ifout,"%u.%u",&wa.Network.IfIdx,&wa.Network.SubIfIdx)!=2)
@@ -1168,17 +1270,22 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const
wa.TCPChecksum=1;
wa.UDPChecksum=1;
wa.IPv6 = (dst->sa_family==AF_INET6);
if (!windivert_send(data,len,&wa))
{
DLOG_ERR("windivert send error. win32 code %u\n",w_win32_error);
return false;
}
return true;
}
#else // *nix
bool ensure_file_access(const char *filename)
{
return !chown(filename, params.uid, -1);
}
static int rawsend_sock4=-1, rawsend_sock6=-1;
static bool b_bind_fix4=false, b_bind_fix6=false;
static void rawsend_clean_sock(int *sock)
@@ -1783,3 +1890,54 @@ bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
dbgprint_socket_buffers(fd);
return true;
}
bool make_writeable_dir()
{
char wdir[PATH_MAX], *wrdir;
if (*params.writeable_dir)
wrdir = params.writeable_dir;
else
{
#ifdef __CYGWIN__
char *env = getenv("APPDATALOW");
if (!env) return false;
#else
char *env = getenv("TMPDIR");
if (!env) env = "/tmp";
#endif
snprintf(wdir,sizeof(wdir),"%s/zapret2",env);
wrdir = wdir;
}
if (mkdir(wrdir,0755) && errno!=EEXIST)
return false;
bool b = false;
#ifdef __CYGWIN__
size_t l = cygwin_conv_path(CCP_POSIX_TO_WIN_W | CCP_ABSOLUTE, wrdir, NULL, 0);
WCHAR *wwrdir = (WCHAR*)malloc(l);
if (wwrdir)
{
if (!cygwin_conv_path(CCP_POSIX_TO_WIN_W | CCP_ABSOLUTE, wrdir, wwrdir, l))
b = SetMandatoryLabelFileW(wwrdir, SECURITY_MANDATORY_LOW_RID, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
free(wwrdir);
}
#else
if (ensure_dir_access(wrdir))
b = true;
else
{
// could not chown. may be still accessible ?
char testfile[PATH_MAX];
snprintf(testfile,sizeof(testfile),"%s/test_XXXXXX",wrdir);
int fd = mkstemp(testfile);
if (fd>0)
{
close(fd);
unlink(testfile);
b = true;
}
}
#endif
if (b) setenv("WRITEABLE",wrdir,1);
return b;
}

View File

@@ -83,23 +83,30 @@ uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind);
uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp);
uint16_t tcp_find_mss(const struct tcphdr *tcp);
bool tcp_has_sack(struct tcphdr *tcp);
bool tcp_has_fastopen(const struct tcphdr *tcp);
bool tcp_synack_segment(const struct tcphdr *tcphdr);
bool tcp_syn_segment(const struct tcphdr *tcphdr);
bool ip_has_df(const struct ip *ip);
bool make_writeable_dir();
bool ensure_file_access(const char *filename);
#ifdef __CYGWIN__
extern uint32_t w_win32_error;
bool ensure_dir_access(const char *filename);
bool prepare_low_appdata();
bool win_sandbox(void);
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter);
bool win_dark_deinit(void);
bool logical_net_filter_present(void);
bool logical_net_filter_match(void);
bool nlm_list(bool bAll);
bool windivert_init(const char *filter);
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa);
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa);
#else
#define ensure_dir_access(dir) ensure_file_access(dir)
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(bool bind_fix4, bool bind_fix6);
#endif
@@ -156,13 +163,6 @@ struct dissect
};
void proto_dissect_l3l4(const uint8_t *data, size_t len, struct dissect *dis);
bool tcp_synack_segment(const struct tcphdr *tcphdr);
bool tcp_syn_segment(const struct tcphdr *tcphdr);
bool tcp_ack_segment(const struct tcphdr *tcphdr);
// scale_factor=SCALE_NONE - do not change
void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor);
void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor);
uint8_t ttl46(const struct ip *ip, const struct ip6_hdr *ip6);
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, const struct ip *ip, const struct ip6_hdr *ip6hdr);

View File

@@ -659,7 +659,7 @@ static uint8_t desync(
struct func_list *func;
int ref_arg = LUA_NOREF, status;
bool b, b_cutoff_all, b_unwanted_payload;
t_lua_desync_context ctx = { .dp = dp,.ctrack = ctrack };
t_lua_desync_context ctx = { .dp = dp, .ctrack = ctrack, .dis = dis };
const char *sDirection = bIncoming ? "in" : "out";
struct packet_range *range;
size_t l;
@@ -787,7 +787,7 @@ static uint8_t desync(
}
lua_pushlightuserdata(params.L, &ctx);
lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref_arg);
lua_pushf_args(&func->args);
lua_pushf_args(&func->args, -1);
lua_pushf_str("func", func->func);
lua_pushf_int("func_n", func_n);
lua_pushf_str("func_instance", instance);
@@ -1169,7 +1169,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (l7proto == L7_UNKNOWN)
{
l7proto = L7_HTTP;
if (ctrack) ctrack->l7proto = l7proto;
if (ctrack && ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
}
// we do not reassemble http
@@ -1201,7 +1201,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (l7proto == L7_UNKNOWN)
{
l7proto = L7_TLS;
if (ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
if (ctrack && ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
}
if (bReqFull) TLSDebug(rdata_payload, rlen_payload);
@@ -1271,7 +1271,6 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
};
protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload);
}
if (ctrack && ctrack->req_seq_finalized)
{
uint32_t dseq = ctrack->seq_last - ctrack->req_seq_end;

View File

@@ -417,26 +417,9 @@ bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size)
}
return true;
}
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset)
char hex_digit(uint8_t v)
{
size_t size;
if (offset%=patsize)
{
size = patsize-offset;
size = bufsize>size ? size : bufsize;
memcpy(buf,pattern+offset,size);
buf += size;
bufsize -= size;
}
while (bufsize)
{
size = bufsize>patsize ? patsize : bufsize;
memcpy(buf,pattern,size);
buf += size;
bufsize -= size;
}
return v<=9 ? '0'+v : (v<=0xF) ? v+'A'-0xA : '?';
}
int fprint_localtime(FILE *F)

View File

@@ -65,7 +65,7 @@ uint64_t pntoh64(const uint8_t *p);
void phton64(uint8_t *p, uint64_t v);
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size);
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset);
char hex_digit(uint8_t v);
int fprint_localtime(FILE *F);

View File

@@ -1,8 +1,19 @@
#include <time.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <unistd.h>
#ifdef __FreeBSD__
#include <sys/thr.h>
#elif defined(__linux__)
#include <sys/syscall.h>
#elif defined(__CYGWIN__)
#include <processthreadsapi.h>
#endif
#include "lua.h"
#include "params.h"
#include "helpers.h"
#include "conntrack.h"
#include "crypto/sha.h"
#include "crypto/aes-gcm.h"
#include "crypto/aes-ctr.h"
@@ -185,12 +196,33 @@ static int luacall_u32(lua_State *L)
lua_pushinteger(L,pntoh32(p+offset));
return 1;
}
static int luacall_swap16(lua_State *L)
{
lua_check_argc(L,"swap16",1);
lua_Integer i = luaL_checkinteger(L,1);
if (i>0xFFFF || i<-(lua_Integer)0xFFFF) luaL_error(L, "out of range");
uint16_t u = (uint16_t)i;
// __builtin_bswap16 is absent in ancient lexra gcc 4.6
lua_pushinteger(L,(u>>8) | ((u&0xFF)<<8));
return 1;
}
static int luacall_swap32(lua_State *L)
{
lua_check_argc(L,"swap32",1);
lua_Integer i = luaL_checkinteger(L,1);
if (i>0xFFFFFFFF || i<-(lua_Integer)0xFFFFFFFF) luaL_error(L, "out of range");
uint32_t u = (uint32_t)i;
lua_pushinteger(L,__builtin_bswap32(u));
return 1;
}
static int luacall_bu8(lua_State *L)
{
lua_check_argc(L,"bu8",1);
lua_Integer i = luaL_checkinteger(L,1);
if (i & ~(uint64_t)0xFF) luaL_error(L, "out of range");
if (i>0xFF || i<-(lua_Integer)0xFF) luaL_error(L, "out of range");
uint8_t v=(uint8_t)i;
lua_pushlstring(L,(char*)&v,1);
return 1;
@@ -200,7 +232,7 @@ static int luacall_bu16(lua_State *L)
lua_check_argc(L,"bu16",1);
lua_Integer i = luaL_checkinteger(L,1);
if (i & ~(uint64_t)0xFFFF) luaL_error(L, "out of range");
if (i>0xFFFF || i<-(lua_Integer)0xFFFF) luaL_error(L, "out of range");
uint8_t v[2];
phton16(v,(uint16_t)i);
lua_pushlstring(L,(char*)v,2);
@@ -211,7 +243,7 @@ static int luacall_bu24(lua_State *L)
lua_check_argc(L,"bu24",1);
lua_Integer i = luaL_checkinteger(L,1);
if (i & ~(uint64_t)0xFFFFFF) luaL_error(L, "out of range");
if (i>0xFFFFFF || i<-(lua_Integer)0xFFFFFF) luaL_error(L, "out of range");
uint8_t v[3];
phton24(v,(uint32_t)i);
lua_pushlstring(L,(char*)v,3);
@@ -222,7 +254,7 @@ static int luacall_bu32(lua_State *L)
lua_check_argc(L,"bu32",1);
lua_Integer i = luaL_checkinteger(L,1);
if (i & ~(uint64_t)0xFFFFFFFF) luaL_error(L, "out of range");
if (i>0xFFFFFFFF || i<-(lua_Integer)0xFFFFFFFF) luaL_error(L, "out of range");
uint8_t v[4];
phton32(v,(uint32_t)i);
lua_pushlstring(L,(char*)v,4);
@@ -409,7 +441,6 @@ static int luacall_aes(lua_State *L)
if (input_len!=16)
luaL_error(L, "aes: wrong data length %u. should be 16.", (unsigned)input_len);
aes_init_keygen_tables();
aes_context ctx;
uint8_t output[16];
if (aes_setkey(&ctx, bEncrypt, key, key_len) || aes_cipher(&ctx, input, output))
@@ -525,7 +556,72 @@ static int luacall_hkdf(lua_State *L)
}
static int luacall_getpid(lua_State *L)
{
lua_check_argc(L,"getpid", 0);
lua_pushinteger(L, getpid());
return 1;
}
static int luacall_gettid(lua_State *L)
{
lua_check_argc(L,"gettid", 0);
#ifdef __OpenBSD__
lua_pushinteger(L, getthrid());
#elif defined(__FreeBSD__)
long tid;
if (thr_self(&tid))
lua_pushnil(L);
else
lua_pushinteger(L, tid);
#elif defined(__linux__)
lua_pushinteger(L, syscall(SYS_gettid));
#elif defined(__CYGWIN__)
lua_pushinteger(L, GetCurrentThreadId());
#else
// unsupported OS ?
lua_pushnil(L);
#endif
return 1;
}
static int luacall_uname(lua_State *L)
{
lua_check_argc(L,"uname", 0);
LUA_STACK_GUARD_ENTER(L)
struct utsname udata;
if (uname(&udata))
lua_pushnil(L);
else
{
lua_createtable(params.L, 0, 5);
lua_pushf_str("sysname", udata.sysname);
lua_pushf_str("nodename", udata.nodename);
lua_pushf_str("release", udata.release);
lua_pushf_str("version", udata.version);
lua_pushf_str("machine", udata.machine);
}
LUA_STACK_GUARD_RETURN(L,1)
}
static int luacall_clock_gettime(lua_State *L)
{
lua_check_argc(L,"uname", 0);
LUA_STACK_GUARD_ENTER(L)
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
{
lua_pushnil(L);
lua_pushnil(L);
}
else
{
lua_pushinteger(L, ts.tv_sec);
lua_pushinteger(L, ts.tv_nsec);
}
LUA_STACK_GUARD_RETURN(L,2)
}
static int luacall_instance_cutoff(lua_State *L)
{
// out : func_name.profile_number[0]
@@ -609,6 +705,23 @@ bool lua_instance_cutoff_check(const t_lua_desync_context *ctx, bool bIn)
return b;
}
static int luacall_raw_packet(lua_State *L)
{
lua_check_argc(L,"raw_packet",1);
LUA_STACK_GUARD_ENTER(L)
const t_lua_desync_context *ctx;
if (!lua_islightuserdata(L,1))
luaL_error(L, "raw_packet expect desync context in the first argument");
ctx = lua_touserdata(L,1);
lua_pushlstring(L, (const char*)ctx->dis->data_pkt, ctx->dis->len_pkt);
LUA_STACK_GUARD_RETURN(L,1)
}
void lua_pushf_nil(const char *field)
{
@@ -708,8 +821,27 @@ void lua_pushf_global(const char *field, const char *global)
lua_rawset(params.L,-3);
}
void lua_push_blob(int idx_desync, const char *blob)
{
lua_getfield(params.L, idx_desync, blob);
if (lua_type(params.L,-1)==LUA_TNIL)
{
lua_pop(params.L,1);
lua_getglobal(params.L, blob);
}
}
void lua_pushf_blob(int idx_desync, const char *field, const char *blob)
{
lua_pushstring(params.L, field);
lua_push_blob(idx_desync, blob);
lua_rawset(params.L,-3);
}
void lua_pushf_tcphdr_options(const struct tcphdr *tcp, size_t len)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushliteral(params.L,"options");
lua_newtable(params.L);
@@ -743,10 +875,14 @@ void lua_pushf_tcphdr_options(const struct tcphdr *tcp, size_t len)
}
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_tcphdr(const struct tcphdr *tcp, size_t len)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushliteral(params.L, "tcp");
if (tcp && len>=sizeof(struct tcphdr))
{
@@ -766,9 +902,13 @@ void lua_pushf_tcphdr(const struct tcphdr *tcp, size_t len)
else
lua_pushnil(params.L);
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_udphdr(const struct udphdr *udp, size_t len)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushliteral(params.L, "udp");
if (udp && len>=sizeof(struct udphdr))
{
@@ -781,9 +921,13 @@ void lua_pushf_udphdr(const struct udphdr *udp, size_t len)
else
lua_pushnil(params.L);
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_iphdr(const struct ip *ip, size_t len)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushliteral(params.L, "ip");
if (ip && len>=sizeof(struct ip))
{
@@ -807,9 +951,13 @@ void lua_pushf_iphdr(const struct ip *ip, size_t len)
else
lua_pushnil(params.L);
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_ip6exthdr(const struct ip6_hdr *ip6, size_t len)
{
LUA_STACK_GUARD_ENTER(params.L);
// assume ipv6 packet structure was already checked for validity
size_t hdrlen;
uint8_t HeaderType, *data;
@@ -866,9 +1014,13 @@ void lua_pushf_ip6exthdr(const struct ip6_hdr *ip6, size_t len)
end:
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushliteral(params.L, "ip6");
if (ip6)
{
@@ -884,9 +1036,13 @@ void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len)
else
lua_pushnil(params.L);
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_push_dissect(const struct dissect *dis)
{
LUA_STACK_GUARD_ENTER(params.L)
if (dis)
{
lua_createtable(params.L, 0, 7);
@@ -900,6 +1056,8 @@ void lua_push_dissect(const struct dissect *dis)
}
else
lua_pushnil(params.L);
LUA_STACK_GUARD_LEAVE(params.L, 1)
}
void lua_pushf_dissect(const struct dissect *dis)
{
@@ -910,6 +1068,8 @@ void lua_pushf_dissect(const struct dissect *dis)
void lua_pushf_ctrack(const t_ctrack *ctrack)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushliteral(params.L, "track");
if (ctrack)
{
@@ -956,18 +1116,49 @@ void lua_pushf_ctrack(const t_ctrack *ctrack)
else
lua_pushnil(params.L);
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_args(const struct ptr_list_head *args)
void lua_pushf_args(const struct ptr_list_head *args, int idx_desync)
{
// var=val - pass val string
// var=%val - subst 'val' blob
// var=#val - subst 'val' blob length
// var=\#val - no subst, skip '\'
// var=\%val - no subst, skip '\'
LUA_STACK_GUARD_ENTER(params.L)
struct ptr_list *arg;
const char *var, *val;
idx_desync = lua_absindex(params.L, idx_desync);
lua_pushliteral(params.L,"arg");
lua_newtable(params.L);
LIST_FOREACH(arg, args, next)
{
lua_pushf_str((char*)arg->ptr1,arg->ptr2 ? (char*)arg->ptr2 : "");
var = (char*)arg->ptr1;
val = arg->ptr2 ? (char*)arg->ptr2 : "";
if (val[0]=='\\' && (val[1]=='%' || val[1]=='#'))
// escape char
lua_pushf_str(var, val+1);
else if (val[0]=='%')
lua_pushf_blob(idx_desync, var, val+1);
else if (val[0]=='#')
{
lua_push_blob(idx_desync, val+1);
lua_Integer len = lua_rawlen(params.L, -1);
lua_pop(params.L,1);
lua_pushf_int(var, len);
}
else
lua_pushf_str(var, val);
}
lua_rawset(params.L,-3);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
@@ -1007,6 +1198,8 @@ static void lua_reconstruct_extract_options(lua_State *L, int idx, bool *badsum,
static bool lua_reconstruct_ip6exthdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t proto, bool preserve_next)
{
LUA_STACK_GUARD_ENTER(params.L)
// proto = last header type
if (*len<sizeof(struct tcphdr)) return false;
@@ -1062,16 +1255,21 @@ static bool lua_reconstruct_ip6exthdr(int idx, struct ip6_hdr *ip6, size_t *len,
*len = filled;
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return true;
err2:
lua_pop(params.L, 2);
return false;
goto err;
err3:
lua_pop(params.L, 3);
err:
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
bool lua_reconstruct_ip6hdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t last_proto, bool preserve_next)
{
LUA_STACK_GUARD_ENTER(params.L)
const char *p;
size_t l;
if (*len<sizeof(struct ip6_hdr) || lua_type(params.L,idx)!=LUA_TTABLE) return false;
@@ -1111,6 +1309,8 @@ bool lua_reconstruct_ip6hdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t l
return lua_reconstruct_ip6exthdr(idx, ip6, len, last_proto, preserve_next);
err:
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
@@ -1139,6 +1339,8 @@ bool lua_reconstruct_iphdr(int idx, struct ip *ip, size_t *len)
const char *p;
size_t l, lopt=0;
LUA_STACK_GUARD_ENTER(params.L)
if (*len<sizeof(struct ip) || lua_type(params.L,-1)!=LUA_TTABLE) return false;
ip->ip_v = IPVERSION;
@@ -1202,9 +1404,11 @@ bool lua_reconstruct_iphdr(int idx, struct ip *ip, size_t *len)
ip4_fix_checksum(ip);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return true;
err:
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
static int luacall_reconstruct_iphdr(lua_State *L)
@@ -1226,6 +1430,8 @@ static bool lua_reconstruct_tcphdr_options(int idx, struct tcphdr *tcp, size_t *
{
if (*len<sizeof(struct tcphdr)) return false;
LUA_STACK_GUARD_ENTER(params.L)
uint8_t filled = sizeof(struct tcphdr);
lua_getfield(params.L,idx,"options");
@@ -1297,21 +1503,26 @@ end:
*len = filled;
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return true;
err1:
lua_pop(params.L, 1);
return false;
goto err;
err2:
lua_pop(params.L, 2);
return false;
goto err;
err3:
lua_pop(params.L, 3);
err:
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
bool lua_reconstruct_tcphdr(int idx, struct tcphdr *tcp, size_t *len)
{
if (*len<sizeof(struct tcphdr) || lua_type(params.L,-1)!=LUA_TTABLE) return false;
LUA_STACK_GUARD_ENTER(params.L)
idx = lua_absindex(params.L, idx);
lua_getfield(params.L,idx,"th_sport");
@@ -1358,9 +1569,11 @@ bool lua_reconstruct_tcphdr(int idx, struct tcphdr *tcp, size_t *len)
tcp->th_off = 5;
LUA_STACK_GUARD_LEAVE(params.L, 0)
return lua_reconstruct_tcphdr_options(idx, tcp, len);
err:
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
static int luacall_reconstruct_tcphdr(lua_State *L)
@@ -1382,6 +1595,8 @@ bool lua_reconstruct_udphdr(int idx, struct udphdr *udp)
{
if (lua_type(params.L,-1)!=LUA_TTABLE) return false;
LUA_STACK_GUARD_ENTER(params.L)
lua_getfield(params.L,idx,"uh_sport");
if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err;
udp->uh_sport = htons((uint16_t)lua_tointeger(params.L,-1));
@@ -1400,9 +1615,11 @@ bool lua_reconstruct_udphdr(int idx, struct udphdr *udp)
udp->uh_sum = htons((uint16_t)lua_tointeger(params.L,-1));
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return true;
err:
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
static int luacall_reconstruct_udphdr(lua_State *L)
@@ -1443,6 +1660,8 @@ bool lua_reconstruct_dissect(int idx, uint8_t *buf, size_t *len, bool badsum, bo
struct udphdr *udp=NULL;
const char *p;
LUA_STACK_GUARD_ENTER(params.L)
idx = lua_absindex(params.L, idx);
lua_getfield(params.L,idx,"ip");
@@ -1578,9 +1797,11 @@ bool lua_reconstruct_dissect(int idx, uint8_t *buf, size_t *len, bool badsum, bo
}
*len = l;
LUA_STACK_GUARD_LEAVE(params.L, 0)
return true;
err:
lua_pop(params.L, 1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
return false;
}
static int luacall_reconstruct_dissect(lua_State *L)
@@ -1829,6 +2050,7 @@ static int luacall_rawsend(lua_State *L)
if (!extract_dst(data, len, (struct sockaddr*)&sa))
luaL_error(L, "bad ip4/ip6 header");
DLOG("rawsend repeats=%d size=%zu ifout=%s fwmark=%08X\n", repeats,len,ifout ? ifout : "",fwmark);
b = rawsend_rep(repeats, (struct sockaddr*)&sa, fwmark, ifout, data, len);
lua_pushboolean(L, b);
@@ -1853,7 +2075,7 @@ static int luacall_rawsend_dissect(lua_State *L)
luaL_checktype(L,1,LUA_TTABLE);
lua_rawsend_extract_options(L,2, &repeats, &fwmark, &ifout);
lua_reconstruct_extract_options(params.L, 3, &badsum, &ip6_preserve_next, NULL);
if (!lua_reconstruct_dissect(1, buf, &len, badsum, ip6_preserve_next))
luaL_error(L, "invalid dissect data");
@@ -2244,6 +2466,8 @@ void lua_shutdown()
if (params.L)
{
DLOG("LUA SHUTDOWN\n");
// conntrack holds lua state. must clear it before lua shoudown
ConntrackPoolDestroy(&params.conntrack);
lua_close(params.L);
params.L=NULL;
}
@@ -2328,6 +2552,20 @@ static bool lua_desync_functions_exist()
return true;
}
bool lua_test_init_script_files(void)
{
struct str_list *str;
LIST_FOREACH(str, &params.lua_init_scripts, next)
{
if (str->str[0]=='@' && !file_open_test(str->str+1, O_RDONLY))
{
DLOG_ERR("LUA file '%s' not accessible\n",str->str+1);
return false;
}
}
return true;
}
static bool lua_init_scripts(void)
{
struct str_list *str;
@@ -2380,13 +2618,16 @@ static void lua_sec_harden(void)
{
lua_getfield(params.L, -1, bad[i].field);
lua_pushstring(params.L, bad[i].field2);
DLOG(" %s.%s.%s", bad[i].global, bad[i].field, bad[i].field2);
}
else
{
lua_pushstring(params.L, bad[i].field);
DLOG(" %s.%s", bad[i].global, bad[i].field);
}
lua_pushnil(params.L);
lua_rawset(params.L, -3);
lua_pop(params.L,1 + !!bad[i].field2);
DLOG(" %s.%s", bad[i].global, bad[i].field);
}
else
{
@@ -2562,11 +2803,14 @@ static void lua_init_functions(void)
{"u16",luacall_u16},
{"u24",luacall_u24},
{"u32",luacall_u32},
// convert number to blob (string)
// convert number to blob (string) - big endian
{"bu8",luacall_bu8},
{"bu16",luacall_bu16},
{"bu24",luacall_bu24},
{"bu32",luacall_bu32},
// swap byte order
{"swap16",luacall_swap16},
{"swap32",luacall_swap32},
// integer division
{"divint",luacall_divint},
@@ -2592,6 +2836,14 @@ static void lua_init_functions(void)
// voluntarily stop receiving packets
{"instance_cutoff",luacall_instance_cutoff},
// get raw packet data
{"raw_packet",luacall_raw_packet},
// system functions
{"uname",luacall_uname},
{"clock_gettime",luacall_clock_gettime},
{"getpid",luacall_getpid},
{"gettid",luacall_gettid},
// convert table representation to blob or vise versa
{"reconstruct_tcphdr",luacall_reconstruct_tcphdr},

View File

@@ -28,6 +28,7 @@
#define LUA_STACK_GUARD_RETURN(L,N) LUA_STACK_GUARD_LEAVE(L,N); return N;
bool lua_test_init_script_files(void);
bool lua_init(void);
void lua_shutdown(void);
void lua_dlog_error(void);
@@ -35,6 +36,7 @@ void lua_do_gc(void);
#if LUA_VERSION_NUM < 502
int lua_absindex(lua_State *L, int idx);
#define lua_rawlen lua_objlen
#endif
// push - create object and push to the stack
@@ -56,6 +58,9 @@ void lua_pushf_lud(const char *field, void *p);
void lua_pushf_table(const char *field);
void lua_pushi_table(lua_Integer idx);
void lua_push_blob(int idx_desync, const char *blob);
void lua_pushf_blob(int idx_desync, const char *field, const char *blob);
void lua_pushf_tcphdr_options(const struct tcphdr *tcp, size_t len);
void lua_pushf_tcphdr(const struct tcphdr *tcp, size_t len);
void lua_pushf_udphdr(const struct udphdr *udp, size_t len);
@@ -64,7 +69,7 @@ void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len);
void lua_push_dissect(const struct dissect *dis);
void lua_pushf_dissect(const struct dissect *dis);
void lua_pushf_ctrack(const t_ctrack *ctrack);
void lua_pushf_args(const struct ptr_list_head *args);
void lua_pushf_args(const struct ptr_list_head *args, int idx_desync);
void lua_pushf_global(const char *field, const char *global);
bool lua_reconstruct_ip6hdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t last_proto, bool preserve_next);
@@ -77,6 +82,7 @@ typedef struct {
const char *func, *instance;
const struct desync_profile *dp;
const t_ctrack *ctrack;
const struct dissect *dis;
} t_lua_desync_context;
bool lua_instance_cutoff_check(const t_lua_desync_context *ctx, bool bIn);

View File

@@ -12,6 +12,7 @@
#include "gzip.h"
#include "pools.h"
#include "lua.h"
#include "crypto/aes.h"
#include <stdio.h>
#include <stdlib.h>
@@ -303,6 +304,8 @@ static int nfq_main(void)
print_id();
if (params.droproot && !test_list_files())
goto err;
if (!lua_test_init_script_files())
goto err;
sec_harden();
@@ -461,6 +464,8 @@ static int dvt_main(void)
print_id();
if (params.droproot && !test_list_files())
goto exiterr;
if (!lua_test_init_script_files())
goto exiterr;
if (!lua_init())
goto exiterr;
@@ -601,11 +606,6 @@ static int win_main()
return ERROR_TOO_MANY_OPEN_FILES; // code 4 = The system cannot open the file
}
if (!lua_init())
{
res=ERROR_INVALID_PARAMETER; goto ex;
}
if (!win_dark_init(&params.ssid_filter, &params.nlm_filter))
{
DLOG_ERR("win_dark_init failed. win32 error %u (0x%08X)\n", w_win32_error, w_win32_error);
@@ -635,6 +635,19 @@ static int win_main()
{
res=w_win32_error; goto ex;
}
if (!win_sandbox())
{
res=w_win32_error;
DLOG_ERR("Cannot init Windows sandbox\n");
goto ex;
}
// init LUA only here because of possible sandbox. no LUA code with high privs
if (!params.L && !lua_init())
{
res=ERROR_INVALID_PARAMETER; goto ex;
}
DLOG_CONDUP("windivert initialized. capture is started.\n");
@@ -1020,7 +1033,7 @@ bool lua_call_param_add(char *opt, struct ptr_list_head *args)
struct func_list *parse_lua_call(char *opt, struct func_list_head *flist)
{
char *name, *e, *p, c;
bool b;
bool b,last;
struct func_list *f = NULL;
if (!(name = item_name(&opt)))
@@ -1031,14 +1044,18 @@ struct func_list *parse_lua_call(char *opt, struct func_list_head *flist)
for (p = opt; p && *p; )
{
if ((e = strchr(p, ':')))
for(e=p; *e && *e!=':'; e++)
{
c = *e;
*e = 0;
if (e[0]=='\\' && e[1]==':')
memmove(e,e+1,strlen(e)); // swallow escape symbol
}
last = !*e;
c = *e;
*e = 0;
b = lua_call_param_add(p, &f->args);
if (e) *e++ = c;
if (!last) *e++ = c;
if (!b) goto err;
p = e;
@@ -1153,7 +1170,7 @@ static void LuaDesyncDebug(struct desync_profile *dp)
LIST_FOREACH(arg, &func->args, next)
{
if (n) DLOG(",");
DLOG(arg->ptr2 ? "%s=\"%s\"" : "%s=nil", (char*)arg->ptr1, (char*)arg->ptr2);
DLOG(arg->ptr2 ? "%s=\"%s\"" : "%s=\"\"", (char*)arg->ptr1, (char*)arg->ptr2);
n++;
}
DLOG(" range_in=%c%u%c%c%u range_out=%c%u%c%c%u payload_type=",
@@ -1390,6 +1407,7 @@ static void exithelp(void)
" --nlm-list[=all]\t\t\t\t\t; list Network List Manager (NLM) networks. connected only or all.\n"
#endif
"\nDESYNC ENGINE INIT:\n"
" --writeable[=<dir_name>]\t\t\t\t; create writeable dir for LUA scripts and pass it in WRITEABLE env variable (only one dir possible)\n"
" --blob=<item_name>:[+ofs]@<filename>|0xHEX\t\t; load blob to LUA var <item_name>\n"
" --lua-init=@<filename>|<lua_text>\t\t\t; load LUA program from a file or string. if multiple parameters present order of execution is preserved.\n"
" --lua-gc=<int>\t\t\t\t\t\t; forced garbage collection every N sec. default %u sec. triggers only when a packet arrives. 0 = disable.\n"
@@ -1528,6 +1546,8 @@ enum opt_indices {
IDX_SOCKARG,
#endif
IDX_WRITEABLE,
IDX_BLOB,
IDX_LUA_INIT,
IDX_LUA_GC,
@@ -1608,6 +1628,7 @@ static const struct option long_options[] = {
#elif defined(SO_USER_COOKIE)
[IDX_SOCKARG] = {"sockarg", required_argument, 0, 0},
#endif
[IDX_WRITEABLE] = {"writeable", optional_argument, 0, 0},
[IDX_BLOB] = {"blob", required_argument, 0, 0},
[IDX_LUA_INIT] = {"lua-init", required_argument, 0, 0},
[IDX_LUA_GC] = {"lua-gc", required_argument, 0, 0},
@@ -1662,10 +1683,12 @@ int main(int argc, char **argv)
{
if (argc < 2) exithelp();
aes_init_keygen_tables(); // required for aes
set_console_io_buffering();
set_env_exedir(argv[0]);
#ifdef __CYGWIN__
prepare_low_appdata();
if (service_run(argc, argv))
{
// we were running as service. now exit.
@@ -1680,11 +1703,10 @@ int main(int argc, char **argv)
uint64_t payload_type=0;
struct packet_range range_in = PACKET_RANGE_NEVER, range_out = PACKET_RANGE_ALWAYS;
#ifdef __CYGWIN__
char wf_save_file[256];
char wf_save_file[256]="";
bool wf_ipv4 = true, wf_ipv6 = true, wf_filter_lan = true, wf_tcp_empty = false;
unsigned int IfIdx = 0, SubIfIdx = 0;
unsigned int hash_wf_tcp_in = 0, hash_wf_udp_in = 0, hash_wf_tcp_out = 0, hash_wf_udp_out = 0, hash_wf_raw = 0, hash_wf_raw_part = 0, hash_ssid_filter = 0, hash_nlm_filter = 0;
*wf_save_file = 0;
#endif
srandom(time(NULL));
@@ -1954,6 +1976,17 @@ int main(int argc, char **argv)
}
break;
#endif
case IDX_WRITEABLE:
params.writeable_dir_enable = true;
if (optarg)
{
strncpy(params.writeable_dir, optarg, sizeof(params.writeable_dir));
params.writeable_dir[sizeof(params.writeable_dir) - 1] = 0;
}
else
*params.writeable_dir = 0;
break;
case IDX_BLOB:
load_blob_to_collection(optarg, &params.blobs, MAX_BLOB_SIZE, BLOB_EXTRA_BYTES);
break;
@@ -2398,12 +2431,24 @@ int main(int argc, char **argv)
DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n", desync_profile_count);
if (params.writeable_dir_enable)
{
if (!make_writeable_dir())
{
DLOG_ERR("could not make writeable dir for LUA\n");
exit_clean(1);
}
DLOG("LUA writeable dir : %s\n", getenv("WRITEABLE"));
}
#ifndef __CYGWIN__
if (params.debug_target == LOG_TARGET_FILE && params.droproot && chown(params.debug_logfile, params.uid, -1))
fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile);
if (params.droproot && *params.hostlist_auto_debuglog && chown(params.hostlist_auto_debuglog, params.uid, -1))
DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog);
if (params.droproot)
#endif
{
if (params.debug_target == LOG_TARGET_FILE && !ensure_file_access(params.debug_logfile))
DLOG_ERR("could not make '%s' accessible. log file may not be writable after privilege drop\n", params.debug_logfile);
if (*params.hostlist_auto_debuglog && !ensure_file_access(params.hostlist_auto_debuglog))
DLOG_ERR("could not make '%s' accessible. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog);
}
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
dp = &dpl->dp;
@@ -2415,14 +2460,20 @@ int main(int argc, char **argv)
}
#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);
if (params.droproot)
#endif
{
if (dp->hostlist_auto && ensure_file_access(dp->hostlist_auto->filename))
DLOG_ERR("could not make '%s' accessible. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
}
LuaDesyncDebug(dp);
}
if (!test_list_files())
exit_clean(1);
if (!lua_test_init_script_files())
exit_clean(1);
if (!LoadAllHostLists())
{

View File

@@ -88,21 +88,21 @@ const uint8_t fake_tls_clienthello_default[680] = {
const char * tld[6] = { "com","org","net","edu","gov","biz" };
int DLOG_FILE(FILE *F, const char *format, va_list args)
int DLOG_FILE_VA(FILE *F, const char *format, va_list args)
{
return vfprintf(F, format, args);
}
int DLOG_CON(const char *format, int syslog_priority, va_list args)
int DLOG_CON_VA(const char *format, int syslog_priority, va_list args)
{
return DLOG_FILE(syslog_priority==LOG_ERR ? stderr : stdout, format, args);
return DLOG_FILE_VA(syslog_priority==LOG_ERR ? stderr : stdout, format, args);
}
int DLOG_FILENAME(const char *filename, const char *format, va_list args)
int DLOG_FILENAME_VA(const char *filename, const char *format, va_list args)
{
int r;
FILE *F = fopen(filename,"at");
if (F)
{
r = DLOG_FILE(F, format, args);
r = DLOG_FILE_VA(F, format, args);
fclose(F);
}
else
@@ -118,6 +118,21 @@ static void syslog_log_function(int priority, const char *line)
{
syslog(priority,"%s",log_buf);
}
static int DLOG_FILENAME(const char *filename, const char *format, ...)
{
int r;
va_list args;
va_start(args, format);
r = DLOG_FILENAME_VA(filename, format, args);
va_end(args);
return r;
}
static void file_log_function(int priority, const char *line)
{
DLOG_FILENAME(params.debug_logfile,"%s",log_buf);
}
#ifdef __ANDROID__
static enum android_LogPriority syslog_priority_to_android(int priority)
{
@@ -163,7 +178,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
if (condup && !(params.debug && params.debug_target==LOG_TARGET_CONSOLE))
{
va_copy(args2,args);
DLOG_CON(format,syslog_priority,args2);
DLOG_CON_VA(format,syslog_priority,args2);
va_end(args2);
}
if (params.debug)
@@ -171,10 +186,11 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
switch(params.debug_target)
{
case LOG_TARGET_CONSOLE:
r = DLOG_CON(format,syslog_priority,args);
r = DLOG_CON_VA(format,syslog_priority,args);
break;
case LOG_TARGET_FILE:
r = DLOG_FILENAME(params.debug_logfile,format,args);
log_buffered(file_log_function,syslog_priority,format,args);
r = 1;
break;
case LOG_TARGET_SYSLOG:
// skip newlines
@@ -271,10 +287,39 @@ void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
bcut = true;
}
if (!size) return;
for (k = 0; k < size; k++) DLOG("%02X ", data[k]);
DLOG(bcut ? "... : " : ": ");
for (k = 0; k < size; k++) DLOG("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.');
if (bcut) DLOG(" ...");
char *p, *buf = malloc(size*4 + 16);
if (buf)
{
p=buf;
for (k = 0; k < size; k++)
{
*p++ = hex_digit(data[k] >> 4);
*p++ = hex_digit(data[k] & 0xF);
*p++ = ' ';
}
if (bcut)
{
*p++='.';
*p++='.';
*p++='.';
*p++=' ';
}
*p++=':';
*p++=' ';
for (k = 0; k < size; k++)
*p++ = data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.';
if (bcut)
{
*p++=' ';
*p++='.';
*p++='.';
*p++='.';
}
*p = 0;
DLOG("%s", buf);
free(buf);
}
}
void dp_init(struct desync_profile *dp)
@@ -381,9 +426,7 @@ void cleanup_params(struct params_s *params)
#endif
ConntrackPoolDestroy(&params->conntrack);
dp_list_destroy(&params->desync_profiles);
hostlist_files_destroy(&params->hostlists);
ipset_files_destroy(&params->ipsets);
ipcacheDestroy(&params->ipcache);

View File

@@ -161,6 +161,8 @@ struct params_s
uint64_t reasm_payload_disable;
struct str_list_head lua_init_scripts;
bool writeable_dir_enable;
char writeable_dir[PATH_MAX];
int lua_gc;
lua_State *L;

View File

@@ -748,7 +748,7 @@ bool TLSMod(const struct fake_tls_mod *tls_mod, const uint8_t *payload, size_t p
char *s1 = NULL;
if (params.debug)
if ((s1 = malloc(slen + 1)))
memcpy(s1, sni, slen); s1[slen] = 0;
{memcpy(s1, sni, slen); s1[slen] = 0;}
if (slen_delta)
{
if ((*fake_tls_size + slen_delta) > fake_tls_buf_size)
@@ -1151,8 +1151,6 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
pn_offset += tvb_get_varint(data + pn_offset, &payload_len);
if (payload_len<20 || (pn_offset + payload_len)>data_len) return false;
aes_init_keygen_tables();
uint8_t sample_enc[16];
aes_context ctx;
if (aes_setkey(&ctx, 1, aeshp, sizeof(aeshp)) || aes_cipher(&ctx, data + pn_offset + 4, sample_enc)) return false;
@@ -1390,12 +1388,29 @@ bool IsStunBindingRequest(const uint8_t *data, size_t len)
ntohl(*(uint32_t*)(&data[4]))==0x2112A442 && // magic cookie
ntohs(*(uint16_t*)(&data[2]))==len-20;
}
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
bool IsMTProto(const uint8_t *data, size_t len)
{
if (len>=64)
{
/*
uint8_t decrypt[64];
aes_ctr_crypt(data+8, 32, data+40, data, 64, decrypt);
return !memcmp(decrypt+56,"\xEF\xEF\xEF\xEF",4);
*/
// this way requires only one AES instead of 4
uint8_t decrypt[16], iv[16];
aes_context ctx;
memcpy(iv, data+40, 16);
ctr_add(iv,3);
if (!aes_setkey(&ctx, AES_ENCRYPT, data+8, 32) && !aes_cipher(&ctx, iv, decrypt))
{
*((uint32_t*)(decrypt+8)) ^= *((uint32_t*)(data+56));
return !memcmp(decrypt+8,"\xEF\xEF\xEF\xEF",4);
}
}
return false;
}

View File

@@ -37,9 +37,6 @@ SYS_exec_with_loader,
#ifdef SYS_osf_execve
SYS_osf_execve,
#endif
#ifdef SYS_uselib
SYS_uselib,
#endif
#ifdef SYS_chmod
SYS_chmod,
#endif
@@ -69,14 +66,6 @@ SYS_symlinkat,
SYS_link,
#endif
SYS_linkat,
SYS_truncate,
#ifdef SYS_truncate64
SYS_truncate64,
#endif
SYS_ftruncate,
#ifdef SYS_ftruncate64
SYS_ftruncate64,
#endif
#ifdef SYS_mknod
SYS_mknod,
#endif