mirror of
https://github.com/bol-van/zapret-win-bundle.git
synced 2025-12-17 04:14:37 +03:00
bundle
This commit is contained in:
BIN
zapret-winws/WinDivert.dll
Normal file
BIN
zapret-winws/WinDivert.dll
Normal file
Binary file not shown.
BIN
zapret-winws/WinDivert64.sys
Normal file
BIN
zapret-winws/WinDivert64.sys
Normal file
Binary file not shown.
3
zapret-winws/_CMD_ADMIN.cmd
Normal file
3
zapret-winws/_CMD_ADMIN.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
|
||||
"%~dp0elevator" cmd /k cd /d "%~dp0"
|
||||
BIN
zapret-winws/cygwin1.dll
Normal file
BIN
zapret-winws/cygwin1.dll
Normal file
Binary file not shown.
BIN
zapret-winws/elevator.exe
Normal file
BIN
zapret-winws/elevator.exe
Normal file
Binary file not shown.
14
zapret-winws/enable_timestamps.cmd
Normal file
14
zapret-winws/enable_timestamps.cmd
Normal file
@@ -0,0 +1,14 @@
|
||||
@echo off
|
||||
|
||||
if "%1%" == "doit" (
|
||||
echo enable tcp timestamps
|
||||
netsh interface tcp set global timestamps=enabled
|
||||
goto :end
|
||||
)
|
||||
|
||||
"%~dp0elevator" %0 doit
|
||||
goto :eof
|
||||
|
||||
:end
|
||||
pause
|
||||
|
||||
14
zapret-winws/files/list-youtube.txt
Normal file
14
zapret-winws/files/list-youtube.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
googlevideo.com
|
||||
youtubei.googleapis.com
|
||||
ytimg.com
|
||||
yt3.ggpht.com
|
||||
yt4.ggpht.com
|
||||
youtube.com
|
||||
youtubeembeddedplayer.googleapis.com
|
||||
ytimg.l.google.com
|
||||
jnn-pa.googleapis.com
|
||||
youtube-nocookie.com
|
||||
youtube-ui.l.google.com
|
||||
yt-video-upload.l.google.com
|
||||
wide-youtube.l.google.com
|
||||
youtu.be
|
||||
BIN
zapret-winws/files/quic_initial_www_google_com.bin
Normal file
BIN
zapret-winws/files/quic_initial_www_google_com.bin
Normal file
Binary file not shown.
BIN
zapret-winws/killall.exe
Normal file
BIN
zapret-winws/killall.exe
Normal file
Binary file not shown.
892
zapret-winws/lua/zapret-antidpi.lua
Normal file
892
zapret-winws/lua/zapret-antidpi.lua
Normal file
@@ -0,0 +1,892 @@
|
||||
--[[
|
||||
|
||||
NFQWS2 ANTIDPI LIBRARY
|
||||
|
||||
--lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua
|
||||
--lua-desync=func1:arg1[=val1]:arg2[=val2] --lua-desync=func2:arg1[=val1]:arg2[=val2] .... --lua-desync=funcN:arg1[=val1]:arg2[=val2]
|
||||
|
||||
BLOBS
|
||||
|
||||
blobs can be 0xHEX, field name in desync or global var
|
||||
standard way to bring binary data to lua code is using the "--blob" parameter of nfqws2
|
||||
dynamic blobs can be inside desync table. one function can prepare data for next functions.
|
||||
|
||||
STANDARD FUNCTION ARGS
|
||||
|
||||
standard direction :
|
||||
|
||||
* dir = in|out|any
|
||||
|
||||
standard fooling :
|
||||
|
||||
* ip_ttl=N - set ipv.ip_ttl to N
|
||||
* ip6_ttl=N - set ip6.ip6_hlim to N
|
||||
* ip_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
|
||||
* 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 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.
|
||||
|
||||
* tcp_seq=N - add N to tcp.th_seq
|
||||
* tcp_ack=N - add N to tcp.th_ack
|
||||
* 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_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)
|
||||
|
||||
standard reconstruct :
|
||||
|
||||
* badsum - make L4 checksum invalid
|
||||
|
||||
standard rawsend :
|
||||
|
||||
* repeats - how many time send the packet
|
||||
* ifout - override outbound interface (if --bind_fix4, --bind-fix6 enabled)
|
||||
* fwmark - override fwmark. desync mark bit(s) will be set unconditionally
|
||||
|
||||
standard payload :
|
||||
|
||||
* payload - comma separarated list of allowed payload types. if not present - allow non-empty known payloads.
|
||||
|
||||
standard ip_id :
|
||||
|
||||
* ip_id - seq|rnd|zero|none
|
||||
* ip_id_conn - in 'seq' mode save current ip_id in track.lua_state to use it between packets
|
||||
|
||||
standard ipfrag :
|
||||
|
||||
* ipfrag[=frag_function] - ipfrag function name. "ipfrag2" by default if empty
|
||||
* ipfrag_disorder - send fragments from last to first
|
||||
* ipfrag2 : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8
|
||||
* ipfrag2 : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
|
||||
* ipfrag2 : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
|
||||
|
||||
]]
|
||||
|
||||
|
||||
-- drop packet
|
||||
-- standard args : direction, payload
|
||||
function drop(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync, "any")
|
||||
if direction_check(desync, "any") and payload_check(desync,"all") then
|
||||
DLOG("drop")
|
||||
return VERDICT_DROP
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dup"
|
||||
-- standard args : direction, fooling, ip_id, ipfrag, rawsend, reconstruct
|
||||
function send(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync, "any")
|
||||
if direction_check(desync, "any") then
|
||||
DLOG("send")
|
||||
local dis = deepcopy(desync.dis)
|
||||
apply_fooling(desync, dis)
|
||||
apply_ip_id(desync, dis, nil, "none")
|
||||
-- it uses rawsend, reconstruct and ipfrag options
|
||||
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--orig"
|
||||
-- apply modification to current packet
|
||||
-- standard args : direction, fooling, ip_id
|
||||
function pktmod(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync, "any")
|
||||
if direction_check(desync, "any") then
|
||||
-- apply to current packet
|
||||
apply_fooling(desync)
|
||||
apply_ip_id(desync, nil, nil, "none")
|
||||
DLOG("pktmod: applied")
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--domcase"
|
||||
-- standard args : direction
|
||||
function http_domcase(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if desync.l7payload=="http_req" and direction_check(desync) then
|
||||
local host_range = resolve_multi_pos(desync.dis.payload,desync.l7payload,"host,endhost")
|
||||
if #host_range == 2 then
|
||||
local host = string.sub(desync.dis.payload,host_range[1],host_range[2]-1)
|
||||
local newhost="", i
|
||||
for i = 1, #host do
|
||||
newhost=newhost..((i%2)==0 and string.lower(string.sub(host,i,i)) or string.upper(string.sub(host,i,i)))
|
||||
end
|
||||
DLOG("http_domcase: "..host.." => "..newhost)
|
||||
desync.dis.payload = string.sub(desync.dis.payload, 1, host_range[1]-1)..newhost..string.sub(desync.dis.payload, host_range[2])
|
||||
return VERDICT_MODIFY
|
||||
else
|
||||
DLOG("http_domcase: cannot find host range")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--hostcase"
|
||||
-- standard args : direction
|
||||
-- arg : spell=<str> . spelling of the "Host" header. must be exactly 4 chars long
|
||||
function http_hostcase(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if desync.l7payload=="http_req" and direction_check(desync) then
|
||||
local spell = desync.arg.spell or "host"
|
||||
if #spell ~= 4 then
|
||||
error("http_hostcase: invalid host spelling '"..spell.."'")
|
||||
else
|
||||
local hdis = http_dissect_req(desync.dis.payload)
|
||||
if hdis.headers.host then
|
||||
DLOG("http_hostcase: 'Host:' => '"..spell.."'")
|
||||
desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers.host.pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers.host.pos_header_end+1)
|
||||
return VERDICT_MODIFY
|
||||
else
|
||||
DLOG("http_hostcase: 'Host:' header not found")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--methodeol"
|
||||
-- standard args : direction
|
||||
function http_methodeol(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if desync.l7payload=="http_req" and direction_check(desync) then
|
||||
local hdis = http_dissect_req(desync.dis.payload)
|
||||
local ua = hdis.headers["user-agent"]
|
||||
if ua then
|
||||
if (ua.pos_end - ua.pos_value_start) < 2 then
|
||||
DLOG("http_methodeol: 'User-Agent:' header is too short")
|
||||
else
|
||||
DLOG("http_methodeol: applied")
|
||||
desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or "");
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
else
|
||||
DLOG("http_methodeol: 'User-Agent:' header not found")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--synack-split"
|
||||
-- standard args : rawsend, reconstruct, ipfrag
|
||||
-- arg : mode=syn|synack|acksyn . "synack" by default
|
||||
function synack_split(ctx, desync)
|
||||
if desync.dis.tcp then
|
||||
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK) == (TH_SYN + TH_ACK) then
|
||||
local mode = desync.arg.mode or "synack"
|
||||
local options = desync_opts(desync)
|
||||
if mode=="syn" then
|
||||
local dis = deepcopy(desync.dis)
|
||||
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
|
||||
DLOG("synack_split: sending SYN")
|
||||
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
||||
return VERDICT_DROP
|
||||
elseif mode=="synack" then
|
||||
local dis = deepcopy(desync.dis)
|
||||
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
|
||||
DLOG("synack_split: sending SYN")
|
||||
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
||||
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN))
|
||||
DLOG("synack_split: sending ACK")
|
||||
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
||||
return VERDICT_DROP
|
||||
elseif mode=="acksyn" then
|
||||
local dis = deepcopy(desync.dis)
|
||||
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN))
|
||||
DLOG("synack_split: sending ACK")
|
||||
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
||||
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
|
||||
DLOG("synack_split: sending SYN")
|
||||
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
||||
return VERDICT_DROP
|
||||
else
|
||||
error("synack_split: bad mode '"..mode.."'")
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync) -- mission complete
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=synack"
|
||||
-- standard args : rawsend, reconstruct, ipfrag
|
||||
function synack(ctx, desync)
|
||||
if desync.dis.tcp then
|
||||
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
|
||||
local dis = deepcopy(desync.dis)
|
||||
dis.tcp.th_flags = bitor(dis.tcp.th_flags, TH_ACK)
|
||||
DLOG("synack: sending")
|
||||
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync) -- mission complete
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- nfqws1 : "--wsize"
|
||||
-- arg : wsize=N . tcp window size
|
||||
-- arg : scale=N . tcp option scale factor
|
||||
function wsize(ctx, desync)
|
||||
if desync.dis.tcp then
|
||||
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK) == (TH_SYN + TH_ACK) then
|
||||
if wsize_rewrite(desync.dis, desync.arg) then
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync) -- mission complete
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--wssize"
|
||||
-- standard args : direction
|
||||
-- arg : wsize=N . tcp window size
|
||||
-- arg : scale=N . tcp option scale factor
|
||||
-- arg : forced_cutoff=<list> - comma separated list of payloads that trigger forced wssize cutoff. by default - any non-empty payload
|
||||
function wssize(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
local verdict = VERDICT_PASS
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if direction_check(desync) then
|
||||
if wsize_rewrite(desync.dis, desync.arg) then
|
||||
verdict = VERDICT_MODIFY
|
||||
end
|
||||
if #desync.dis.payload>0 and (not desync.arg.forced_cutoff or in_list(desync.arg.forced_cutoff, desync.l7payload)) then
|
||||
DLOG("wssize: forced cutoff")
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
end
|
||||
end
|
||||
return verdict
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=syndata"
|
||||
-- standard args : fooling, rawsend, reconstruct, ipfrag
|
||||
-- arg : blob=<blob> - fake payload. must fit to single packet. no segmentation possible. default - 16 zero bytes.
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>. sni=%var is supported
|
||||
function syndata(ctx, desync)
|
||||
if desync.dis.tcp then
|
||||
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
|
||||
local dis = deepcopy(desync.dis)
|
||||
dis.payload = blob(desync, desync.arg.blob, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
apply_fooling(desync, dis)
|
||||
if desync.arg.tls_mod then
|
||||
dis.payload = tls_mod_shim(desync, dis.payload, desync.arg.tls_mod, nil)
|
||||
end
|
||||
if b_debug then DLOG("syndata: "..hexdump_dlog(dis.payload)) end
|
||||
if rawsend_dissect_ipfrag(dis, desync_opts(desync)) then
|
||||
return VERDICT_DROP
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync) -- mission complete
|
||||
end
|
||||
else
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=rst"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- arg : rstack - send RST,ACK instead of RST
|
||||
function rst(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if direction_check(desync, "any") and payload_check(desync) then
|
||||
if replay_first(desync) then
|
||||
local dis = deepcopy(desync.dis)
|
||||
dis.payload = ""
|
||||
dis.tcp.th_flags = TH_RST + (desync.arg.rstack and TH_ACK or 0)
|
||||
apply_fooling(desync, dis)
|
||||
apply_ip_id(desync, dis, nil, "none")
|
||||
DLOG("rst")
|
||||
-- it uses rawsend, reconstruct and ipfrag options
|
||||
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
||||
else
|
||||
DLOG("rst: not acting on further replay pieces")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=fake"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- arg : blob=<blob> - fake payload
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap . sni=%var is supported
|
||||
function fake(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
if direction_check(desync) and payload_check(desync) then
|
||||
if replay_first(desync) then
|
||||
if not desync.arg.blob then
|
||||
error("fake: 'blob' arg required")
|
||||
end
|
||||
local fake_payload = blob(desync, desync.arg.blob)
|
||||
if desync.reasm_data and desync.arg.tls_mod then
|
||||
fake_payload = tls_mod_shim(desync, fake_payload, desync.arg.tls_mod, desync.reasm_data)
|
||||
end
|
||||
-- check debug to save CPU
|
||||
if b_debug then DLOG("fake: "..hexdump_dlog(fake_payload)) end
|
||||
rawsend_payload_segmented(desync,fake_payload)
|
||||
else
|
||||
DLOG("fake: not acting on further replay pieces")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=multisplit"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- 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_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
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"
|
||||
-- check debug to save CPU
|
||||
if b_debug then DLOG("multisplit: split pos: "..spos) end
|
||||
local pos = resolve_multi_pos(data, desync.l7payload, spos)
|
||||
if b_debug then DLOG("multisplit: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end
|
||||
delete_pos_1(pos) -- cannot split at the first byte
|
||||
if #pos>0 then
|
||||
for i=0,#pos do
|
||||
local pos_start = pos[i] or 1
|
||||
local pos_end = i<#pos and pos[i+1]-1 or #data
|
||||
local part = string.sub(data,pos_start,pos_end)
|
||||
local seqovl=0
|
||||
if i==0 and desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
|
||||
seqovl = tonumber(desync.arg.seqovl)
|
||||
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
||||
part = pattern(pat,1,seqovl)..part
|
||||
end
|
||||
if b_debug then DLOG("multisplit: sending part "..(i+1).." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,pos_start-1-seqovl) then
|
||||
return VERDICT_PASS
|
||||
end
|
||||
end
|
||||
replay_drop_set(desync)
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
else
|
||||
DLOG("multisplit: no valid split positions")
|
||||
end
|
||||
else
|
||||
DLOG("multisplit: not acting on further replay pieces")
|
||||
end
|
||||
-- drop replayed packets if reasm was sent successfully in splitted form
|
||||
if replay_drop(desync) then
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=multidisorder"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- 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 reasm_data
|
||||
-- arg : nodrop - do not drop current dissect
|
||||
function multidisorder(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
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"
|
||||
-- check debug to save CPU
|
||||
if b_debug then DLOG("multidisorder: split pos: "..spos) end
|
||||
local pos = resolve_multi_pos(data, desync.l7payload, spos)
|
||||
if b_debug then DLOG("multidisorder: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end
|
||||
delete_pos_1(pos) -- cannot split at the first byte
|
||||
if #pos>0 then
|
||||
for i=#pos,0,-1 do
|
||||
local pos_start = pos[i] or 1
|
||||
local pos_end = i<#pos and pos[i+1]-1 or #data
|
||||
local part = string.sub(data,pos_start,pos_end)
|
||||
local seqovl=0
|
||||
if i==1 and desync.arg.seqovl then
|
||||
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
|
||||
if not seqovl then
|
||||
DLOG("multidisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
|
||||
seqovl = 0
|
||||
else
|
||||
seqovl = seqovl - 1
|
||||
if seqovl>=(pos[1]-1) then
|
||||
DLOG("multidisorder: seqovl cancelled because seqovl "..seqovl.." is not less than the first split pos "..(pos[1]-1))
|
||||
seqovl = 0
|
||||
else
|
||||
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
||||
part = pattern(pat,1,seqovl)..part
|
||||
end
|
||||
end
|
||||
end
|
||||
if b_debug then DLOG("multidisorder: sending part "..(i+1).." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,pos_start-1-seqovl) then
|
||||
return VERDICT_PASS
|
||||
end
|
||||
end
|
||||
replay_drop_set(desync)
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
else
|
||||
DLOG("multidisorder: no valid split positions")
|
||||
end
|
||||
else
|
||||
DLOG("multidisorder: not acting on further replay pieces")
|
||||
end
|
||||
-- drop replayed packets if reasm was sent successfully in splitted form
|
||||
if replay_drop(desync) then
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=hostfakesplit"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
|
||||
-- arg : host=<str> - hostname template. generate hosts like "random.template". example : e8nzn.vk.com
|
||||
-- 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_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
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)
|
||||
if pos then
|
||||
if b_debug then DLOG("hostfakesplit: resolved host range: "..table.concat(zero_based_pos(pos)," ")) end
|
||||
|
||||
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
|
||||
local part, fakehost
|
||||
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
|
||||
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
|
||||
|
||||
part = string.sub(data,1,pos[1]-1)
|
||||
if b_debug then DLOG("hostfakesplit: sending before_host part 0-"..(pos[1]-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,0, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
fakehost = genhost(pos[2]-pos[1]+1, desync.arg.host)
|
||||
|
||||
if not desync.arg.nofake1 then
|
||||
if b_debug then DLOG("hostfakesplit: sending fake host part (1) "..(pos[1]-1).."-"..(pos[2]-1).." len="..#fakehost.." : "..hexdump_dlog(fakehost)) end
|
||||
if not rawsend_payload_segmented(desync,fakehost,pos[1]-1, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
local midhost
|
||||
if desync.arg.midhost then
|
||||
midhost = resolve_pos(data,desync.l7payload,desync.arg.midhost)
|
||||
if not midhost then
|
||||
DLOG("hostfakesplit: cannot resolve midhost marker '"..desync.arg.midhost.."'")
|
||||
end
|
||||
DLOG("hosfakesplit: midhost marker resolved to "..midhost)
|
||||
if midhost<=pos[1] or midhost>pos[2] then
|
||||
DLOG("hostfakesplit: midhost is not inside the host range")
|
||||
midhost = nil
|
||||
end
|
||||
end
|
||||
-- if present apply ipfrag only to real host parts. fakes and parts outside of the host must be visible to DPI.
|
||||
if midhost then
|
||||
part = string.sub(data,pos[1],midhost-1)
|
||||
if b_debug then DLOG("hostfakesplit: sending real host part 1 "..(pos[1]-1).."-"..(midhost-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,pos[1]-1, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
part = string.sub(data,midhost,pos[2])
|
||||
if b_debug then DLOG("hostfakesplit: sending real host part 2 "..(midhost-1).."-"..(pos[2]-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,midhost-1, opts_orig) then return VERDICT_PASS end
|
||||
else
|
||||
part = string.sub(data,pos[1],pos[2])
|
||||
if b_debug then DLOG("hostfakesplit: sending real host part "..(pos[1]-1).."-"..(pos[2]-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,pos[1]-1, opts_orig) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
if not desync.arg.nofake2 then
|
||||
if b_debug then DLOG("hostfakesplit: sending fake host part (2) "..(pos[1]-1).."-"..(pos[2]-1).." len="..#fakehost.." : "..hexdump_dlog(fakehost)) end
|
||||
if not rawsend_payload_segmented(desync,fakehost,pos[1]-1, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
local disorder_after_pos
|
||||
if desync.arg.disorder_after then
|
||||
disorder_after_pos = resolve_pos(data, desync.l7payload, desync.arg.disorder_after=="" and "-1" or desync.arg.disorder_after)
|
||||
if disorder_after_pos then
|
||||
-- pos[2] points to the last letter of the host starting from 1
|
||||
if disorder_after_pos<=(pos[2]+1) then
|
||||
DLOG("hostfakesplit: disorder_after marker '"..(disorder_after_pos-1).."' resolved to pos not after after_host pos "..pos[2])
|
||||
disorder_after_pos = nil
|
||||
end
|
||||
|
||||
else
|
||||
DLOG("hostfakesplit: could not resolve disorder_after marker '"..desync.arg.disorder_after.."'")
|
||||
end
|
||||
end
|
||||
if disorder_after_pos then
|
||||
part = string.sub(data,disorder_after_pos)
|
||||
if b_debug then DLOG("hostfakesplit: sending after_host part (2) "..(disorder_after_pos-1).."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,disorder_after_pos-1, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
part = string.sub(data,pos[2]+1,disorder_after_pos-1)
|
||||
if b_debug then DLOG("hostfakesplit: sending after_host part (1) "..pos[2].."-"..(disorder_after_pos-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
else
|
||||
part = string.sub(data,pos[2]+1)
|
||||
if b_debug then DLOG("hostfakesplit: sending after_host part "..pos[2].."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
end
|
||||
if not rawsend_payload_segmented(desync,part,pos[2], opts_orig) then return VERDICT_PASS end
|
||||
|
||||
replay_drop_set(desync)
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
else
|
||||
DLOG("hostfakesplit: host range cannot be resolved")
|
||||
end
|
||||
else
|
||||
DLOG("hostfakesplit: not acting on further replay pieces")
|
||||
end
|
||||
-- drop replayed packets if reasm was sent successfully in splitted form
|
||||
if replay_drop(desync) then
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=fakedsplit"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
|
||||
-- arg : pos=<posmarker> - split position marker
|
||||
-- arg : nofake1, nofake2, nofake3, nofake4 - do not send individual fakes
|
||||
-- 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 reasm_data
|
||||
-- arg : nodrop - do not drop current dissect
|
||||
function fakedsplit(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
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"
|
||||
local pos = resolve_pos(data, desync.l7payload, spos)
|
||||
if pos then
|
||||
if pos == 1 then
|
||||
DLOG("multidisorder: split pos resolved to 0. cannot split.")
|
||||
else
|
||||
if b_debug then DLOG("fakedsplit: resolved split pos: "..tostring(pos-1)) end
|
||||
|
||||
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
|
||||
local fake, fakepat, part, pat
|
||||
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
|
||||
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
|
||||
|
||||
fakepat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
|
||||
|
||||
-- first fake
|
||||
fake = pattern(fakepat,1,pos-1)
|
||||
|
||||
if not desync.arg.nofake1 then
|
||||
if b_debug then DLOG("fakedsplit: sending fake part 1 (1) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
-- first real
|
||||
part = string.sub(data,1,pos-1)
|
||||
local seqovl=0
|
||||
if desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
|
||||
seqovl = tonumber(desync.arg.seqovl)
|
||||
pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
||||
part = pattern(pat,1,seqovl)..part
|
||||
end
|
||||
if b_debug then DLOG("fakedsplit: sending real part 1 : 0-"..(pos-2).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,-seqovl, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
-- first fake again
|
||||
if not desync.arg.nofake2 then
|
||||
if b_debug then DLOG("fakedsplit: sending fake part 1 (2) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
-- second fake
|
||||
fake = pattern(fakepat,pos,#data-pos+1)
|
||||
if not desync.arg.nofake3 then
|
||||
if b_debug then DLOG("fakedsplit: sending fake part 2 (1) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
-- second real
|
||||
part = string.sub(data,pos)
|
||||
if b_debug then DLOG("fakedsplit: sending real part 2 : "..(pos-1).."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,pos-1, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
-- second fake again
|
||||
if not desync.arg.nofake4 then
|
||||
if b_debug then DLOG("fakedsplit: sending fake part 2 (2) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
replay_drop_set(desync)
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
else
|
||||
DLOG("fakedsplit: cannot resolve pos '"..desync.arg.pos.."'")
|
||||
end
|
||||
else
|
||||
DLOG("fakedsplit: not acting on further replay pieces")
|
||||
end
|
||||
-- drop replayed packets if reasm was sent successfully in splitted form
|
||||
if replay_drop(desync) then
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=fakeddisorder"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
|
||||
-- arg : pos=<posmarker> - split position marker
|
||||
-- arg : nofake1, nofake2, nofake3, nofake4 - do not send individual fakes
|
||||
-- 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_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
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"
|
||||
local pos = resolve_pos(data, desync.l7payload, spos)
|
||||
if pos then
|
||||
if pos == 1 then
|
||||
DLOG("multidisorder: split pos resolved to 0. cannot split.")
|
||||
else
|
||||
if b_debug then DLOG("fakeddisorder: resolved split pos: "..tostring(pos-1)) end
|
||||
|
||||
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
|
||||
local fake, part, pat
|
||||
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
|
||||
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
|
||||
|
||||
fakepat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
|
||||
|
||||
-- second fake
|
||||
fake = pattern(fakepat,pos,#data-pos+1)
|
||||
if not desync.arg.nofake1 then
|
||||
if b_debug then DLOG("fakeddisorder: sending fake part 2 (1) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
-- second real
|
||||
part = string.sub(data,pos)
|
||||
local seqovl = 0
|
||||
if desync.arg.seqovl then
|
||||
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
|
||||
if seqovl then
|
||||
seqovl = seqovl - 1
|
||||
if seqovl>=(pos-1) then
|
||||
DLOG("fakeddisorder: seqovl cancelled because seqovl "..seqovl.." is not less than the split pos "..(pos-1))
|
||||
seqovl = 0
|
||||
else
|
||||
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
||||
part = pattern(pat,1,seqovl)..part
|
||||
end
|
||||
else
|
||||
DLOG("fakeddisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
|
||||
seqovl = 0
|
||||
end
|
||||
end
|
||||
if b_debug then DLOG("fakeddisorder: sending real part 2 : "..(pos-1).."-"..(#data-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,pos-1-seqovl, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
-- second fake again
|
||||
if not desync.arg.nofake2 then
|
||||
if b_debug then DLOG("fakeddisorder: sending fake part 2 (2) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
-- first fake
|
||||
fake = pattern(fakepat,1,pos-1)
|
||||
if not desync.arg.nofake3 then
|
||||
if b_debug then DLOG("fakeddisorder: sending fake part 1 (1) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
-- first real
|
||||
part = string.sub(data,1,pos-1)
|
||||
if b_debug then DLOG("fakeddisorder: sending real part 1 : 0-"..(pos-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
||||
if not rawsend_payload_segmented(desync,part,0, opts_orig) then return VERDICT_PASS end
|
||||
|
||||
-- first fake again
|
||||
if not desync.arg.nofake4 then
|
||||
if b_debug then DLOG("fakeddisorder: sending fake part 1 (2) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
||||
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
||||
end
|
||||
|
||||
replay_drop_set(desync)
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
else
|
||||
DLOG("fakeddisorder: cannot resolve pos '"..desync.arg.pos.."'")
|
||||
end
|
||||
else
|
||||
DLOG("fakeddisorder: not acting on further replay pieces")
|
||||
end
|
||||
-- drop replayed packets if reasm was sent successfully in splitted form
|
||||
if replay_drop(desync) then
|
||||
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : not available
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- 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_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if not desync.arg.pos then
|
||||
error("tcpseg: no pos specified")
|
||||
end
|
||||
-- by default process only outgoing known payloads
|
||||
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
|
||||
-- always returns 2 positions or nil or causes error
|
||||
local pos = resolve_range(data, desync.l7payload, desync.arg.pos)
|
||||
if pos then
|
||||
-- check debug to save CPU
|
||||
if b_debug then DLOG("tcpseg: resolved range: "..table.concat(zero_based_pos(pos)," ")) end
|
||||
local part = string.sub(data,pos[1],pos[2])
|
||||
local seqovl=0
|
||||
if desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
|
||||
seqovl = tonumber(desync.arg.seqovl)
|
||||
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
||||
part = pattern(pat,1,seqovl)..part
|
||||
end
|
||||
if b_debug then DLOG("tcpseg: sending "..(pos[1]-1).."-"..(pos[2]-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
||||
rawsend_payload_segmented(desync,part,pos[1]-1-seqovl)
|
||||
else
|
||||
DLOG("tcpseg: range cannot be resolved")
|
||||
end
|
||||
else
|
||||
DLOG("tcpseg: not acting on further replay pieces")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=udplen"
|
||||
-- standard args : direction, payload
|
||||
-- arg : min=N . do not act on payloads smaller than N bytes
|
||||
-- arg : max=N . do not act on payloads larger than N bytes
|
||||
-- arg : increment=N . 2 by default. negative increment shrinks the packet, positive grows it.
|
||||
-- arg : pattern=<blob> . used to fill extra bytes when length increases
|
||||
-- arg : pattern_offset=N . offset in the pattern. 0 by default
|
||||
function udplen(ctx, desync)
|
||||
if not desync.dis.udp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if direction_check(desync) and payload_check(desync) then
|
||||
local len = #desync.dis.payload
|
||||
if (desync.arg.min and #desync.dis.payload < tonumber(desync.arg.min)) then
|
||||
DLOG("udplen: payload size "..len.." is less than the minimum size "..desync.arg.min)
|
||||
elseif (desync.arg.max and #desync.dis.payload > tonumber(desync.arg.max)) then
|
||||
DLOG("udplen: payload size "..len.." is more than the maximum size "..desync.arg.max)
|
||||
else
|
||||
local inc = desync.arg.increment and tonumber(desync.arg.increment) or 2
|
||||
if inc>0 then
|
||||
local pat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
|
||||
local pat_offset = desync.arg.pattern_offset and (tonumber(desync.arg.pattern_offset)+1) or 1
|
||||
desync.dis.payload = desync.dis.payload .. pattern(pat, pat_offset, inc)
|
||||
DLOG("udplen: "..len.." => "..#desync.dis.payload)
|
||||
return VERDICT_MODIFY
|
||||
elseif inc<0 then
|
||||
if (len+inc)<1 then
|
||||
DLOG("udplen: will not shrink to zero length")
|
||||
else
|
||||
desync.dis.payload = string.sub(desync.dis.payload,1,len+inc)
|
||||
DLOG("udplen: "..len.." => "..#desync.dis.payload)
|
||||
end
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nfqws1 : "--dpi-desync=tamper" for dht proto
|
||||
-- standard args : direction
|
||||
-- arg : dn=N - message starts from "dN". 2 by default
|
||||
function dht_dn(ctx, desync)
|
||||
if not desync.dis.udp then
|
||||
instance_cutoff_shim(ctx, desync)
|
||||
return
|
||||
end
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
if desync.l7payload=="dht" and direction_check(desync) then
|
||||
local N = tonumber(desync.arg.dn) or 2
|
||||
-- remove "d1" from the start not breaking bencode
|
||||
local prefix = "d"..tostring(N)..":"..string.rep("0",N).."1:x"
|
||||
desync.dis.payload = prefix..string.sub(desync.dis.payload,2)
|
||||
DLOG("dht_dn: tampered dht to start with '"..prefix.."' instead of 'd1:'")
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
end
|
||||
429
zapret-winws/lua/zapret-auto.lua
Normal file
429
zapret-winws/lua/zapret-auto.lua
Normal file
@@ -0,0 +1,429 @@
|
||||
-- standard automation/orchestration code
|
||||
-- this is related to making dynamic strategy decisions without rewriting or altering strategy function code
|
||||
-- orchestrators can decide which instances to call or not to call or pass them dynamic arguments
|
||||
-- failure and success detectors test potential block conditions for orchestrators
|
||||
|
||||
-- standard host key generator for per-host storage
|
||||
-- arg: reqhost - require hostname, do not work with ip
|
||||
-- arg: nld=N - cut hostname to N level domain. NLD=2 static.intranet.microsoft.com => microsoft.com
|
||||
function standard_hostkey(desync)
|
||||
local hostkey = desync.track and desync.track.hostname
|
||||
if hostkey then
|
||||
if desync.arg.nld and tonumber(desync.arg.nld)>0 then
|
||||
-- dissect_nld returns nil if domain is invalid or does not have this NLD
|
||||
-- fall back to original hostkey if it fails
|
||||
local hktemp = dissect_nld(hostkey, tonumber(desync.arg.nld))
|
||||
if hktemp then
|
||||
hostkey = hktemp
|
||||
end
|
||||
end
|
||||
elseif not desync.arg.reqhost then
|
||||
hostkey = host_ip(desync)
|
||||
end
|
||||
return hostkey
|
||||
end
|
||||
|
||||
-- per-host storage
|
||||
-- arg: key - a string - table name inside autostate table. to allow multiple orchestrator instances to use single host storage
|
||||
-- arg: hostkey - hostkey generator function name
|
||||
function automate_host_record(desync)
|
||||
local hostkey, hkf, askey
|
||||
|
||||
if desync.arg.hostkey then
|
||||
if type(_G[desync.arg.hostkey])~="function" then
|
||||
error("automate: invalid hostkey function '"..desync.arg.hostkey.."'")
|
||||
end
|
||||
hkf = _G[desync.arg.hostkey]
|
||||
else
|
||||
hkf = standard_hostkey
|
||||
end
|
||||
hostkey = hkf(desync)
|
||||
if not hostkey then
|
||||
DLOG("automate: host record key unavailable")
|
||||
return nil
|
||||
end
|
||||
|
||||
askey = (desync.arg.key and #desync.arg.key>0) and desync.arg.key or desync.func_instance
|
||||
DLOG("automate: host record key 'autostate."..askey.."."..hostkey.."'")
|
||||
if not autostate then
|
||||
autostate = {}
|
||||
end
|
||||
if not autostate[askey] then
|
||||
autostate[askey] = {}
|
||||
end
|
||||
if not autostate[askey][hostkey] then
|
||||
autostate[askey][hostkey] = {}
|
||||
end
|
||||
return autostate[askey][hostkey]
|
||||
end
|
||||
-- per-connection storage
|
||||
function automate_conn_record(desync)
|
||||
if not desync.track.lua_state.automate then
|
||||
desync.track.lua_state.automate = {}
|
||||
end
|
||||
return desync.track.lua_state.automate
|
||||
end
|
||||
|
||||
-- counts failure, optionally (if crec is given) prevents dup failure counts in a single connection
|
||||
-- if 'maxtime' between failures is exceeded then failure count is reset
|
||||
-- return true if threshold ('fails') is reached
|
||||
-- hres is host record. host or ip bound table
|
||||
-- cres is connection record. connection bound table
|
||||
function automate_failure_counter(hrec, crec, fails, maxtime)
|
||||
if crec and crec.failure then
|
||||
DLOG("automate: duplicate failure in the same connection. not counted")
|
||||
else
|
||||
if crec then crec.failure = true end
|
||||
local tnow=os.time()
|
||||
if not hrec.failure_time_last then
|
||||
hrec.failure_time_last = tnow
|
||||
end
|
||||
if not hrec.failure_counter then
|
||||
hrec.failure_counter = 0
|
||||
elseif tnow>(hrec.failure_time_last + maxtime) then
|
||||
DLOG("automate: failure counter reset because last failure was "..(tnow - hrec.failure_time_last).." seconds ago")
|
||||
hrec.failure_counter = 0
|
||||
end
|
||||
hrec.failure_counter = hrec.failure_counter + 1
|
||||
hrec.failure_time_last = tnow
|
||||
if b_debug then DLOG("automate: failure counter "..hrec.failure_counter..(fails and ('/'..fails) or '')) end
|
||||
if fails and hrec.failure_counter>=fails then
|
||||
hrec.failure_counter = nil -- reset counter
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
-- resets failure counter if it has started counting
|
||||
function automate_failure_counter_reset(hrec)
|
||||
if hrec.failure_counter then
|
||||
DLOG("automate: failure counter reset")
|
||||
hrec.failure_counter = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- location is url compatible with Location: header
|
||||
-- hostname is original hostname
|
||||
function is_dpi_redirect(hostname, location)
|
||||
local ds = dissect_url(location)
|
||||
if ds.domain then
|
||||
local sld1 = dissect_nld(hostname,2)
|
||||
local sld2 = dissect_nld(ds.domain,2)
|
||||
return sld2 and sld1~=sld2
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function standard_detector_defaults(arg)
|
||||
return {
|
||||
inseq = tonumber(arg.inseq) or 4096,
|
||||
retrans = tonumber(arg.retrans) or 3,
|
||||
maxseq = tonumber(arg.maxseq) or 32768,
|
||||
udp_in = tonumber(arg.udp_in) or 1,
|
||||
udp_out = tonumber(arg.udp_out) or 4,
|
||||
no_http_redirect = arg.no_http_redirect,
|
||||
no_rst = arg.no_rst
|
||||
}
|
||||
end
|
||||
|
||||
-- standard failure detector
|
||||
-- works with tcp and udp
|
||||
-- detected failures:
|
||||
-- incoming RST
|
||||
-- incoming http redirection
|
||||
-- outgoing retransmissions
|
||||
-- udp too much out with too few in
|
||||
-- arg: maxseq=<rseq> - tcp: test retransmissions only within this relative sequence. default is 32K
|
||||
-- arg: retrans=N - tcp: retrans count threshold. default is 3
|
||||
-- arg: inseq=<rseq> - tcp: maximum relative sequence number to treat incoming RST as DPI reset. default is 4K
|
||||
-- arg: no_http_redirect - tcp: disable http_reply dpi redirect trigger
|
||||
-- arg: no_rst - tcp: disable incoming RST trigger
|
||||
-- arg: udp_out - udp: >= outgoing udp packets. default is 4
|
||||
-- arg: udp_in - udp: with <= incoming udp packets. default is 1
|
||||
function standard_failure_detector(desync, crec)
|
||||
local arg = standard_detector_defaults(desync.arg)
|
||||
local trigger = false
|
||||
if desync.dis.tcp then
|
||||
local seq = pos_get(desync,'s')
|
||||
if desync.outgoing then
|
||||
if #desync.dis.payload>0 and arg.retrans and arg.maxseq>0 and seq<=arg.maxseq and (crec.retrans or 0)<arg.retrans then
|
||||
if is_retransmission(desync) then
|
||||
crec.retrans = crec.retrans and (crec.retrans+1) or 1
|
||||
DLOG("standard_failure_detector: retransmission "..crec.retrans.."/"..arg.retrans)
|
||||
trigger = crec.retrans>=arg.retrans
|
||||
end
|
||||
end
|
||||
else
|
||||
if not arg.no_rst and arg.inseq>0 and bitand(desync.dis.tcp.th_flags, TH_RST)~=0 and seq>=1 then
|
||||
trigger = seq<=arg.inseq
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: incoming RST s"..seq.." in range s"..arg.inseq)
|
||||
else
|
||||
DLOG("standard_failure_detector: not counting incoming RST s"..seq.." beyond s"..arg.inseq)
|
||||
end
|
||||
end
|
||||
elseif not arg.no_http_redirect and desync.l7payload=="http_reply" and desync.track.hostname then
|
||||
local hdis = http_dissect_reply(desync.dis.payload)
|
||||
if hdis and (hdis.code==302 or hdis.code==307) and hdis.headers.location and hdis.headers.location then
|
||||
trigger = is_dpi_redirect(desync.track.hostname, hdis.headers.location.value)
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. looks like DPI redirect.")
|
||||
else
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. NOT a DPI redirect.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif desync.dis.udp then
|
||||
if desync.outgoing then
|
||||
if arg.udp_out>0 then
|
||||
local pos_out = pos_get(desync,'n',false)
|
||||
local pos_in = pos_get(desync,'n',true)
|
||||
trigger = pos_out>=arg.udp_out and pos_in<=arg.udp_in
|
||||
if trigger then
|
||||
if b_debug then
|
||||
DLOG("standard_failure_detector: arg.udp_out "..pos_out..">="..arg.udp_out.." arg.udp_in "..pos_in.."<="..arg.udp_in)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return trigger
|
||||
end
|
||||
|
||||
-- standard success detector
|
||||
-- success means previous failures were temporary and counter should be reset
|
||||
-- detected successes:
|
||||
-- tcp: outgoing seq is beyond 'maxseq' and maxseq>0
|
||||
-- tcp: incoming seq is beyond 'inseq' and inseq>0
|
||||
-- udp: incoming packets count > `udp_in` and `udp_out`>0
|
||||
-- arg: maxseq=<rseq> - tcp: success if outgoing relative sequence is beyond this value. default is 32K
|
||||
-- arg: inseq=<rseq> - tcp: success if incoming relative sequence is beyond this value. default is 4K
|
||||
-- arg: udp_out - udp : must be nil or >0 to test udp_in
|
||||
-- arg: udp_in - udp: if number if incoming packets > udp_in it means success
|
||||
function standard_success_detector(desync, crec)
|
||||
local arg = standard_detector_defaults(desync.arg)
|
||||
if desync.dis.tcp then
|
||||
local seq = pos_get(desync,'s')
|
||||
if desync.outgoing then
|
||||
if arg.maxseq>0 and seq>arg.maxseq then
|
||||
DLOG("standard_success_detector: outgoing s"..seq.." is beyond s"..arg.maxseq..". treating connection as successful")
|
||||
return true
|
||||
end
|
||||
else
|
||||
if arg.inseq>0 and seq>arg.inseq then
|
||||
DLOG("standard_success_detector: incoming s"..seq.." is beyond s"..arg.inseq..". treating connection as successful")
|
||||
return true
|
||||
end
|
||||
end
|
||||
elseif desync.dis.udp then
|
||||
if not desync.outgoing then
|
||||
local pos = pos_get(desync,'n')
|
||||
if arg.udp_out>0 and pos>arg.udp_in then
|
||||
if b_debug then
|
||||
DLOG("standard_success_detector: arg.udp_in "..pos..">"..arg.udp_in)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- calls success and failure detectors
|
||||
-- resets counter if success is detected
|
||||
-- increases counter if failure is detected
|
||||
-- returns true if failure counter exceeds threshold
|
||||
function automate_failure_check(desync, hrec, crec)
|
||||
if crec.nocheck then return false end
|
||||
|
||||
local failure_detector, success_detector
|
||||
if desync.arg.failure_detector then
|
||||
if type(_G[desync.arg.failure_detector])~="function" then
|
||||
error("automate: invalid failure detector function '"..desync.arg.failure_detector.."'")
|
||||
end
|
||||
failure_detector = _G[desync.arg.failure_detector]
|
||||
else
|
||||
failure_detector = standard_failure_detector
|
||||
end
|
||||
if desync.arg.success_detector then
|
||||
if type(_G[desync.arg.success_detector])~="function" then
|
||||
error("automate: invalid success detector function '"..desync.arg.success_detector.."'")
|
||||
end
|
||||
success_detector = _G[desync.arg.success_detector]
|
||||
else
|
||||
success_detector = standard_success_detector
|
||||
end
|
||||
|
||||
if success_detector(desync, crec) then
|
||||
crec.nocheck = true
|
||||
DLOG("automate: success detected")
|
||||
automate_failure_counter_reset(hrec)
|
||||
return false
|
||||
end
|
||||
if failure_detector(desync, crec) then
|
||||
crec.nocheck = true
|
||||
DLOG("automate: failure detected")
|
||||
local fails = tonumber(desync.arg.fails) or 3
|
||||
local maxtime = tonumber(desync.arg.time) or 60
|
||||
return automate_failure_counter(hrec, crec, fails, maxtime)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- circularily change strategy numbers when failure count reaches threshold ('fails')
|
||||
-- this orchestrator requires redirection of incoming traffic to cache RST and http replies !
|
||||
-- each orchestrated instance must have strategy=N arg, where N starts from 1 and increment without gaps
|
||||
-- if 'final' arg is present in an orchestrated instance it stops rotation
|
||||
-- arg: fails=N - failture count threshold. default is 3
|
||||
-- arg: time=<sec> - if last failure happened earlier than `maxtime` seconds ago - reset failure counter. default is 60.
|
||||
-- arg: success_detector - success detector function name
|
||||
-- arg: failure_detector - failure detector function name
|
||||
-- arg: hostkey - hostkey generator function name
|
||||
-- args for failure detector - see standard_failure_detector or your own detector
|
||||
-- args for success detector - see standard_success_detector or your own detector
|
||||
-- args for hostkey generator - see standard_hostkey or your own generator
|
||||
-- test case: nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s34228 --lua-desync=circular --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2
|
||||
function circular(ctx, desync)
|
||||
local function count_strategies(hrec)
|
||||
if not hrec.ctstrategy then
|
||||
local uniq={}
|
||||
local n=0
|
||||
for i,instance in pairs(desync.plan) do
|
||||
if instance.arg.strategy then
|
||||
n = tonumber(instance.arg.strategy)
|
||||
if not n or n<1 then
|
||||
error("circular: strategy number '"..tostring(instance.arg.strategy).."' is invalid")
|
||||
end
|
||||
uniq[tonumber(instance.arg.strategy)] = true
|
||||
if instance.arg.final then
|
||||
hrec.final = n
|
||||
end
|
||||
end
|
||||
end
|
||||
n=0
|
||||
for i,v in pairs(uniq) do
|
||||
n=n+1
|
||||
end
|
||||
if n~=#uniq then
|
||||
error("circular: strategies numbers must start from 1 and increment. gaps are not allowed.")
|
||||
end
|
||||
hrec.ctstrategy = n
|
||||
end
|
||||
end
|
||||
|
||||
-- take over execution. prevent further instance execution in case of error
|
||||
orchestrate(ctx, desync)
|
||||
|
||||
if not desync.track then
|
||||
DLOG_ERR("circular: conntrack is missing but required")
|
||||
return
|
||||
end
|
||||
|
||||
local hrec = automate_host_record(desync)
|
||||
if not hrec then
|
||||
DLOG("circular: passing with no tampering")
|
||||
return
|
||||
end
|
||||
|
||||
count_strategies(hrec)
|
||||
if hrec.ctstrategy==0 then
|
||||
error("circular: add strategy=N tag argument to each following instance ! N must start from 1 and increment")
|
||||
end
|
||||
if not hrec.nstrategy then
|
||||
DLOG("circular: start from strategy 1")
|
||||
hrec.nstrategy = 1
|
||||
end
|
||||
|
||||
local verdict = VERDICT_PASS
|
||||
if hrec.final~=hrec.nstrategy then
|
||||
local crec = automate_conn_record(desync)
|
||||
if automate_failure_check(desync, hrec, crec) then
|
||||
hrec.nstrategy = (hrec.nstrategy % hrec.ctstrategy) + 1
|
||||
DLOG("circular: rotate strategy to "..hrec.nstrategy)
|
||||
if hrec.nstrategy == hrec.final then
|
||||
DLOG("circular: final strategy "..hrec.final.." reached. will rotate no more.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DLOG("circular: current strategy "..hrec.nstrategy)
|
||||
while true do
|
||||
local instance = plan_instance_pop(desync)
|
||||
if not instance then break end
|
||||
if instance.arg.strategy and tonumber(instance.arg.strategy)==hrec.nstrategy then
|
||||
verdict = plan_instance_execute(desync, verdict, instance)
|
||||
end
|
||||
end
|
||||
|
||||
return verdict
|
||||
end
|
||||
|
||||
-- test iff functions
|
||||
function cond_true(desync)
|
||||
return true
|
||||
end
|
||||
function cond_false(desync)
|
||||
return false
|
||||
end
|
||||
-- arg: percent - of true . 50 by default
|
||||
function cond_random(desync)
|
||||
return math.random(0,99)<(tonumber(desync.arg.percent) or 50)
|
||||
end
|
||||
-- this iif function detects packets having 'arg.pattern' string in their payload
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --lua-desync=condition:iff=cond_payload_str:pattern=1234 --lua-desync=argdebug:testarg=1 --lua-desync=argdebug:testarg=2:morearg=xyz
|
||||
-- test case (true) : echo aaz1234zzz | ncat -4u 1.1.1.1 443
|
||||
-- test case (false) : echo aaze124zzz | ncat -4u 1.1.1.1 443
|
||||
function cond_payload_str(desync)
|
||||
if not desync.arg.pattern then
|
||||
error("cond_payload_str: missing 'pattern'")
|
||||
end
|
||||
return string.find(desync.dis.payload,desync.arg.pattern,1,true)
|
||||
end
|
||||
-- check iff function available. error if not
|
||||
function require_iff(desync, name)
|
||||
if not desync.arg.iff then
|
||||
error(name..": missing 'iff' function")
|
||||
end
|
||||
if type(_G[desync.arg.iff])~="function" then
|
||||
error(name..": invalid 'iff' function '"..desync.arg.iff.."'")
|
||||
end
|
||||
end
|
||||
-- execute further desync instances only if user-provided 'iff' function returns true
|
||||
-- for example, this can be used by custom protocol detectors
|
||||
-- arg: iff - condition function. takes desync as arg and returns bool. (cant use 'if' because of reserved word)
|
||||
-- arg: neg - invert condition function result
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --lua-desync=condition:iff=cond_random --lua-desync=argdebug:testarg=1 --lua-desync=argdebug:testarg=2:morearg=xyz
|
||||
function condition(ctx, desync)
|
||||
require_iff(desync, "condition")
|
||||
orchestrate(ctx, desync)
|
||||
if logical_xor(_G[desync.arg.iff](desync), desync.arg.neg) then
|
||||
DLOG("condition: true")
|
||||
return replay_execution_plan(desync)
|
||||
else
|
||||
DLOG("condition: false")
|
||||
plan_clear(desync)
|
||||
end
|
||||
end
|
||||
-- clear execution plan if user provided 'iff' functions returns true
|
||||
-- can be used with other orchestrators to stop execution conditionally
|
||||
-- arg: iff - condition function. takes desync as arg and returns bool. (cant use 'if' because of reserved word)
|
||||
-- arg: neg - invert condition function result
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s1 --lua-desync=circular --lua-desync=stopif:iff=cond_random:strategy=1 --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2
|
||||
function stopif(ctx, desync)
|
||||
require_iff(desync, "stopif")
|
||||
orchestrate(ctx, desync)
|
||||
if logical_xor(_G[desync.arg.iff](desync), desync.arg.neg) then
|
||||
DLOG("stopif: true")
|
||||
plan_clear(desync)
|
||||
else
|
||||
-- do not do anything. allow other orchestrator to finish the plan
|
||||
DLOG("stopif: false")
|
||||
end
|
||||
end
|
||||
1398
zapret-winws/lua/zapret-lib.lua
Normal file
1398
zapret-winws/lua/zapret-lib.lua
Normal file
File diff suppressed because it is too large
Load Diff
39
zapret-winws/lua/zapret-pcap.lua
Normal file
39
zapret-winws/lua/zapret-pcap.lua
Normal 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
|
||||
704
zapret-winws/lua/zapret-tests.lua
Normal file
704
zapret-winws/lua/zapret-tests.lua
Normal file
@@ -0,0 +1,704 @@
|
||||
-- nfqws2 C functions tests
|
||||
-- to run : --lua-init=@zapret-lib.lua --lua-init=@zapret-tests.lua --lua-init="test_all()"
|
||||
|
||||
function test_assert(b)
|
||||
assert(b, "test failed")
|
||||
end
|
||||
|
||||
function test_run(tests,...)
|
||||
for k,f in pairs(tests) do
|
||||
f(...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
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},...)
|
||||
end
|
||||
|
||||
function test_random()
|
||||
local rnds={}
|
||||
for i=1,20 do
|
||||
local rnd = bcryptorandom(math.random(10,20))
|
||||
print("random: "..string2hex(rnd))
|
||||
test_assert(not rnds[rnd]) -- should not be repeats
|
||||
rnds[rnd] = true
|
||||
end
|
||||
end
|
||||
|
||||
function test_hash()
|
||||
local hashes={}
|
||||
for i=1,5 do
|
||||
local rnd = brandom(math.random(5,64))
|
||||
print("data: "..string2hex(rnd))
|
||||
for k,sha in pairs({"sha256","sha224"}) do
|
||||
local hsh = hash(sha, rnd)
|
||||
print(sha..": "..string2hex(hsh))
|
||||
local hsh2 = hash(sha, rnd)
|
||||
test_assert(hsh==hsh2)
|
||||
test_assert(not hashes[hsh])
|
||||
hashes[hsh] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function test_hkdf()
|
||||
local nblob = 2
|
||||
local okms = {}
|
||||
for nsalt=1,nblob do
|
||||
local salt = brandom(math.random(10,20))
|
||||
for nikm=1,nblob do
|
||||
local ikm = brandom(math.random(5,10))
|
||||
for ninfo=1,nblob do
|
||||
local info = brandom(math.random(5,10))
|
||||
local okm_prev
|
||||
for k,sha in pairs({"sha256","sha224"}) do
|
||||
for k,okml in pairs({8, 16, 50}) do
|
||||
local okm_prev
|
||||
local okm
|
||||
print("* hkdf "..sha)
|
||||
print("salt: "..string2hex(salt))
|
||||
print("ikm : "..string2hex(ikm))
|
||||
print("info: "..string2hex(info))
|
||||
print("okml: "..tostring(okml))
|
||||
okm = hkdf(sha, salt, ikm, info, okml)
|
||||
test_assert(okm)
|
||||
print("okm: "..string2hex(okm))
|
||||
if okms[okm] then
|
||||
print("duplicate okm !")
|
||||
end
|
||||
okms[okm] = true
|
||||
|
||||
test_assert(not okm_prev or okm_prev==string.sub(okm, 1, #okm_prev))
|
||||
okm_prev = okm
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function test_aes()
|
||||
local clear_text="test "..brandom_az09(11)
|
||||
local iv, key, encrypted, decrypted
|
||||
|
||||
for key_size=16,32,8 do
|
||||
local key = brandom(key_size)
|
||||
|
||||
print()
|
||||
print("* aes test key_size "..tostring(key_size))
|
||||
|
||||
print("clear text: "..clear_text);
|
||||
|
||||
print("* encrypting")
|
||||
encrypted = aes(true, key, clear_text)
|
||||
print("encrypted: "..str_or_hex(encrypted))
|
||||
|
||||
print("* decrypting everything good")
|
||||
decrypted = aes(false, key, encrypted)
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted==clear_text)
|
||||
|
||||
print("* decrypting bad payload with good key")
|
||||
decrypted = aes(false, key, brandom(16))
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted~=clear_text)
|
||||
|
||||
print("* decrypting good payload with bad key")
|
||||
decrypted = aes(false, brandom(key_size), encrypted)
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted~=clear_text)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function test_aes_gcm()
|
||||
local authenticated_data = "authenticated message "..brandom_az09(math.random(10,50))
|
||||
local clear_text="test message "..brandom_az09(math.random(10,50))
|
||||
local iv, key, encrypted, atag, decrypted, atag2
|
||||
|
||||
for key_size=16,32,8 do
|
||||
iv = brandom(12)
|
||||
key = brandom(key_size)
|
||||
|
||||
print()
|
||||
print("* aes_gcm test key_size "..tostring(key_size))
|
||||
|
||||
print("clear text: "..clear_text);
|
||||
print("authenticated data: "..authenticated_data);
|
||||
|
||||
print("* encrypting")
|
||||
encrypted, atag = aes_gcm(true, key, iv, clear_text, authenticated_data)
|
||||
print("encrypted: "..str_or_hex(encrypted))
|
||||
print("auth tag: "..string2hex(atag))
|
||||
|
||||
print("* decrypting everything good")
|
||||
decrypted, atag2 = aes_gcm(false, key, iv, encrypted, authenticated_data)
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted==clear_text)
|
||||
print("auth tag: "..string2hex(atag2))
|
||||
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
|
||||
test_assert(atag==atag2)
|
||||
|
||||
print("* decrypting bad payload with good key/iv and correct authentication data")
|
||||
decrypted, atag2 = aes_gcm(false, key, iv, brandom(#encrypted), authenticated_data)
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted~=clear_text)
|
||||
print("auth tag: "..string2hex(atag2))
|
||||
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
|
||||
test_assert(atag~=atag2)
|
||||
|
||||
print("* decrypting good payload with good key/iv and incorrect authentication data")
|
||||
decrypted, atag2 = aes_gcm(false, key, iv, encrypted, authenticated_data.."x")
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted==clear_text)
|
||||
print("auth tag: "..string2hex(atag2))
|
||||
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
|
||||
test_assert(atag~=atag2)
|
||||
|
||||
print("* decrypting good payload with bad key, good iv and correct authentication data")
|
||||
decrypted, atag2 = aes_gcm(false, brandom(key_size), iv, encrypted, authenticated_data)
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted~=clear_text)
|
||||
print("auth tag: "..string2hex(atag2))
|
||||
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
|
||||
test_assert(atag~=atag2)
|
||||
|
||||
print("* decrypting good payload with good key, bad iv and correct authentication data")
|
||||
decrypted, atag2 = aes_gcm(false, key, brandom(12), encrypted, authenticated_data)
|
||||
print("decrypted: "..str_or_hex(decrypted))
|
||||
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
|
||||
test_assert(decrypted~=clear_text)
|
||||
print("auth tag: "..string2hex(atag2))
|
||||
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
|
||||
test_assert(atag~=atag2)
|
||||
end
|
||||
end
|
||||
|
||||
function test_aes_ctr()
|
||||
local clear_text="test message "..brandom_az09(math.random(10,50))
|
||||
local iv, key, encrypted, decrypted
|
||||
|
||||
for key_size=16,32,8 do
|
||||
iv = brandom(16)
|
||||
key = brandom(key_size)
|
||||
|
||||
print()
|
||||
print("* aes_ctr test key_size "..tostring(key_size))
|
||||
|
||||
print("clear text: "..clear_text);
|
||||
|
||||
print("* encrypting")
|
||||
encrypted = aes_ctr(key, iv, clear_text)
|
||||
print("encrypted: "..str_or_hex(encrypted))
|
||||
|
||||
print("* decrypting")
|
||||
decrypted = aes_ctr(key, 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 key")
|
||||
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(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
|
||||
|
||||
-- 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
|
||||
local v = math.random(0,f[3])
|
||||
local pos = math.random(1,20)
|
||||
local s = brandom(pos-1)..f[2](v)..brandom(20)
|
||||
local v2 = f[1](s,pos)
|
||||
print("u"..tostring(f[4]).." pos="..tostring(pos).." "..tostring(v).." "..tostring(v2))
|
||||
test_assert(v==v2)
|
||||
end
|
||||
end
|
||||
|
||||
function test_bit()
|
||||
local v, v2, v3, v4, b1, b2, pow
|
||||
|
||||
v = math.random(0,0xFFFFFFFF)
|
||||
b1 = math.random(1,16)
|
||||
|
||||
v2 = bitrshift(v, b1)
|
||||
pow = 2^b1
|
||||
v3 = divint(v, pow)
|
||||
print(string.format("rshift(0x%X,%u) = 0x%X 0x%X/%u = 0x%X", v,b1,v2, v,pow,v3))
|
||||
test_assert(v2==v3)
|
||||
|
||||
v2 = bitlshift(v, b1)
|
||||
pow = 2^b1
|
||||
v3 = (v * pow) % 0x100000000
|
||||
print(string.format("lshift(0x%X,%u) = 0x%X 0x%X*%u %% 0x10000000 = 0x%X", v,b1,v2, v,pow,v3))
|
||||
test_assert(v2==v3)
|
||||
|
||||
v2 = math.random(0,0xFFFFFFFF)
|
||||
v3 = bitxor(v, v2)
|
||||
v4 = bitor(v, v2) - bitand(v, v2)
|
||||
print(string.format("xor(0x%X,0x%X) = %X or/and/minus = %X", v, v2, v3, v4))
|
||||
test_assert(v3==v4)
|
||||
|
||||
b2 = b1 + math.random(1,15)
|
||||
v2 = bitget(v, b1, b2)
|
||||
pow = 2^(b2-b1+1) - 1
|
||||
v3 = bitand(bitrshift(v,b1), pow)
|
||||
print(string.format("bitget(0x%X,%u,%u) = 0x%X bitand/bitrshift/pow = 0x%X", v, b1, b2, v2, v3))
|
||||
test_assert(v2==v3)
|
||||
|
||||
v4 = math.random(0,pow)
|
||||
v2 = bitset(v, b1, b2, v4)
|
||||
v3 = bitor(bitlshift(v4, b1), bitand(v, bitnot(bitlshift(pow, b1))))
|
||||
print(string.format("bitset(0x%X,%u,%u,0x%X) = 0x%X bitand/bitnot/bitlshift/pow = 0x%X", v, b1, b2, v4, v2, v3))
|
||||
test_assert(v2==v3)
|
||||
end
|
||||
|
||||
function test_ux()
|
||||
local v1, v2, v3, usum, sum
|
||||
for k,test in pairs({
|
||||
{ add=u8add, fname="u8add", max = 0xFF },
|
||||
{ add=u16add, fname="u16add", max = 0xFFFF },
|
||||
{ add=u24add, fname="u24add", max = 0xFFFFFF },
|
||||
{ add=u32add, fname="u32add", max = 0xFFFFFFFF }
|
||||
}) do
|
||||
io.write(test.fname.." : ")
|
||||
for i=1,1000 do
|
||||
v1=math.random(-test.max,test.max)
|
||||
v2=math.random(-test.max,test.max)
|
||||
v3=math.random(-test.max,test.max)
|
||||
usum = test.add(v1,v2,v3)
|
||||
sum = bitand((v1+v2+v3)%(test.max+1),test.max)
|
||||
if sum~=usum then
|
||||
print("FAIL")
|
||||
end
|
||||
test_assert(sum==usum)
|
||||
end
|
||||
print("OK")
|
||||
end
|
||||
end
|
||||
|
||||
function test_bin(...)
|
||||
test_run({test_ub, test_bit, test_ux},...)
|
||||
end
|
||||
|
||||
|
||||
function test_ipstr()
|
||||
local s_ip, ip, s_ip2
|
||||
|
||||
s_ip = string.format("%u.%u.%u.%u", math.random(0,255), math.random(0,255), math.random(0,255), math.random(0,255));
|
||||
ip = pton(s_ip)
|
||||
s_ip2 = ntop(ip)
|
||||
print("IP: "..s_ip)
|
||||
print("IPBIN: "..string2hex(ip))
|
||||
print("IP2: "..s_ip2)
|
||||
test_assert(s_ip==s_ip2)
|
||||
|
||||
s_ip = string.format("%x:%x:%x:%x:%x:%x:%x:%x", math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF));
|
||||
ip = pton(s_ip)
|
||||
s_ip2 = ntop(ip)
|
||||
print("IP: "..s_ip)
|
||||
print("IPBIN: "..string2hex(ip))
|
||||
print("IP2: "..s_ip2)
|
||||
test_assert(s_ip==s_ip2)
|
||||
end
|
||||
|
||||
|
||||
function test_dissect()
|
||||
local dis, raw1, raw2
|
||||
|
||||
for i=1,20 do
|
||||
print("* dissect test "..tostring(i))
|
||||
|
||||
local ip_tcp = {
|
||||
ip = {
|
||||
ip_tos = math.random(0,255),
|
||||
ip_id = math.random(0,0xFFFF),
|
||||
ip_off = 0,
|
||||
ip_ttl = math.random(0,255),
|
||||
ip_p = IPPROTO_TCP,
|
||||
ip_src = brandom(4),
|
||||
ip_dst = brandom(4),
|
||||
options = brandom(math.random(0,40))
|
||||
},
|
||||
tcp = {
|
||||
th_sport = math.random(0,0xFFFF),
|
||||
th_dport = math.random(0,0xFFFF),
|
||||
th_seq = math.random(0,0xFFFFFFFF),
|
||||
th_ack = math.random(0,0xFFFFFFFF),
|
||||
th_x2 = math.random(0,0xF),
|
||||
th_flags = math.random(0,0xFF),
|
||||
th_win = math.random(0,0xFFFF),
|
||||
th_urp = math.random(0,0xFFFF),
|
||||
options = {
|
||||
{ kind = 1 },
|
||||
{ kind = 0xE0, data = brandom(math.random(1,10)) },
|
||||
{ kind = 1 },
|
||||
{ kind = 0xE1, data = brandom(math.random(1,10)) },
|
||||
{ kind = 0 }
|
||||
}
|
||||
},
|
||||
payload = brandom(math.random(0, 20))
|
||||
}
|
||||
raw1 = reconstruct_dissect(ip_tcp)
|
||||
print("IP+TCP : "..string2hex(raw1))
|
||||
dis1 = dissect(raw1);
|
||||
raw2 = reconstruct_dissect(dis1)
|
||||
dis2 = dissect(raw2);
|
||||
print("IP+TCP2: "..string2hex(raw2))
|
||||
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
|
||||
test_assert(raw1==raw2)
|
||||
|
||||
local ip6_udp = {
|
||||
ip6 = {
|
||||
ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF),
|
||||
ip6_hlim = math.random(1,0xFF),
|
||||
ip6_src = brandom(16),
|
||||
ip6_dst = brandom(16),
|
||||
exthdr = {
|
||||
{ type = IPPROTO_HOPOPTS, data = brandom(6+8*math.random(0,2)) },
|
||||
{ type = IPPROTO_AH, data = brandom(6+4*math.random(0,4)) }
|
||||
}
|
||||
},
|
||||
udp = {
|
||||
uh_sport = math.random(0,0xFFFF),
|
||||
uh_dport = math.random(0,0xFFFF)
|
||||
},
|
||||
payload = brandom(math.random(0, 20))
|
||||
}
|
||||
|
||||
raw1 = reconstruct_dissect(ip6_udp)
|
||||
print("IP6+UDP : "..string2hex(raw1))
|
||||
dis1 = dissect(raw1);
|
||||
raw2 = reconstruct_dissect(dis1)
|
||||
dis2 = dissect(raw2);
|
||||
print("IP6+UDP2: "..string2hex(raw2))
|
||||
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
|
||||
test_assert(raw1==raw2)
|
||||
end
|
||||
end
|
||||
|
||||
function test_csum()
|
||||
local payload = brandom(math.random(10,20))
|
||||
local ip4b, ip6b, raw, tcpb, udpb, dis1, dis2
|
||||
local ip = {
|
||||
ip_tos = math.random(0,255),
|
||||
ip_id = math.random(0,0xFFFF),
|
||||
ip_len = math.random(0,0xFFFF),
|
||||
ip_off = 0,
|
||||
ip_ttl = math.random(0,255),
|
||||
ip_p = IPPROTO_TCP,
|
||||
ip_src = brandom(4),
|
||||
ip_dst = brandom(4),
|
||||
options = brandom(4*math.random(0,10))
|
||||
}
|
||||
ip4b = reconstruct_iphdr(ip)
|
||||
raw = bu8(0x40 + 5 + #ip.options/4) ..
|
||||
bu8(ip.ip_tos) ..
|
||||
bu16(ip.ip_len) ..
|
||||
bu16(ip.ip_id) ..
|
||||
bu16(ip.ip_off) ..
|
||||
bu8(ip.ip_ttl) ..
|
||||
bu8(ip.ip_p) ..
|
||||
bu16(0) ..
|
||||
ip.ip_src .. ip.ip_dst ..
|
||||
ip.options
|
||||
raw = csum_ip4_fix(raw)
|
||||
print( raw==ip4b and "IP4 RECONSTRUCT+CSUM OK" or "IP4 RECONSTRUCT+CSUM FAILED" )
|
||||
test_assert(raw==ip4b)
|
||||
|
||||
|
||||
local tcp = {
|
||||
th_sport = math.random(0,0xFFFF),
|
||||
th_dport = math.random(0,0xFFFF),
|
||||
th_seq = math.random(0,0xFFFFFFFF),
|
||||
th_ack = math.random(0,0xFFFFFFFF),
|
||||
th_x2 = math.random(0,0xF),
|
||||
th_flags = math.random(0,0xFF),
|
||||
th_win = math.random(0,0xFFFF),
|
||||
th_urp = math.random(0,0xFFFF),
|
||||
options = {
|
||||
{ kind = 1 },
|
||||
{ kind = 0xE0, data = brandom(math.random(1,10)) },
|
||||
{ kind = 1 },
|
||||
{ kind = 0xE1, data = brandom(math.random(1,10)) },
|
||||
{ kind = 0 }
|
||||
}
|
||||
}
|
||||
tcpb = reconstruct_tcphdr(tcp)
|
||||
raw = bu16(tcp.th_sport) ..
|
||||
bu16(tcp.th_dport) ..
|
||||
bu32(tcp.th_seq) ..
|
||||
bu32(tcp.th_ack) ..
|
||||
bu8(l4_len({tcp = tcp}) * 4 + tcp.th_x2) ..
|
||||
bu8(tcp.th_flags) ..
|
||||
bu16(tcp.th_win) ..
|
||||
bu16(0) ..
|
||||
bu16(tcp.th_urp) ..
|
||||
bu8(tcp.options[1].kind)..
|
||||
bu8(tcp.options[2].kind)..bu8(2 + #tcp.options[2].data)..tcp.options[2].data ..
|
||||
bu8(tcp.options[3].kind)..
|
||||
bu8(tcp.options[4].kind)..bu8(2 + #tcp.options[4].data)..tcp.options[4].data ..
|
||||
bu8(tcp.options[5].kind)
|
||||
raw = raw .. string.rep(bu8(TCP_KIND_NOOP), bitand(4-bitand(#raw,3),3))
|
||||
print( raw==tcpb and "TCP RECONSTRUCT OK" or "TCP RECONSTRUCT FAILED" )
|
||||
test_assert(raw==tcpb)
|
||||
|
||||
raw = reconstruct_dissect({ip=ip, tcp=tcp, payload=payload})
|
||||
dis1 = dissect(raw)
|
||||
tcpb = csum_tcp_fix(ip4b,tcpb,payload)
|
||||
dis2 = dissect(ip4b..tcpb..payload)
|
||||
print( dis1.tcp.th_sum==dis2.tcp.th_sum and "TCP+IP4 CSUM OK" or "TCP+IP4 CSUM FAILED" )
|
||||
test_assert(dis1.tcp.th_sum==dis2.tcp.th_sum)
|
||||
|
||||
|
||||
local ip6 = {
|
||||
ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF),
|
||||
ip6_hlim = math.random(1,0xFF),
|
||||
ip6_src = brandom(16),
|
||||
ip6_dst = brandom(16),
|
||||
exthdr = {
|
||||
{ type = IPPROTO_HOPOPTS, data = brandom(6+8*math.random(0,2)) }
|
||||
}
|
||||
}
|
||||
ip6.ip6_plen = packet_len({ip6=ip6,tcp=tcp,payload=payload}) - IP6_BASE_LEN
|
||||
ip6b = reconstruct_ip6hdr(ip6, {ip6_last_proto=IPPROTO_TCP})
|
||||
raw = bu32(ip6.ip6_flow) ..
|
||||
bu16(ip6.ip6_plen) ..
|
||||
bu8(ip6.exthdr[1].type) ..
|
||||
bu8(ip6.ip6_hlim) ..
|
||||
ip6.ip6_src .. ip6.ip6_dst ..
|
||||
bu8(IPPROTO_TCP) ..
|
||||
bu8((#ip6.exthdr[1].data+2)/8 - 1) ..
|
||||
ip6.exthdr[1].data
|
||||
print( raw==ip6b and "IP6 RECONSTRUCT OK" or "IP6 RECONSTRUCT FAILED" )
|
||||
test_assert(raw==ip6b)
|
||||
|
||||
raw = reconstruct_dissect({ip6=ip6, tcp=tcp, payload=payload})
|
||||
dis1 = dissect(raw)
|
||||
tcpb = csum_tcp_fix(ip6b,tcpb,payload)
|
||||
dis2 = dissect(ip6b..tcpb..payload)
|
||||
print( dis1.tcp.th_sum==dis2.tcp.th_sum and "TCP+IP6 CSUM OK" or "TCP+IP6 CSUM FAILED" )
|
||||
test_assert(dis1.tcp.th_sum==dis2.tcp.th_sum)
|
||||
|
||||
|
||||
ip.ip_p = IPPROTO_UDP
|
||||
ip4b = reconstruct_iphdr(ip)
|
||||
ip6.ip6_plen = packet_len({ip6=ip6,udp=udp,payload=payload}) - IP6_BASE_LEN
|
||||
ip6b = reconstruct_ip6hdr(ip6, {ip6_last_proto=IPPROTO_UDP})
|
||||
|
||||
local udp = {
|
||||
uh_sport = math.random(0,0xFFFF),
|
||||
uh_dport = math.random(0,0xFFFF),
|
||||
uh_ulen = UDP_BASE_LEN + #payload
|
||||
}
|
||||
|
||||
udpb = reconstruct_udphdr(udp)
|
||||
raw = bu16(udp.uh_sport) ..
|
||||
bu16(udp.uh_dport) ..
|
||||
bu16(udp.uh_ulen) ..
|
||||
bu16(0)
|
||||
print( raw==udpb and "UDP RECONSTRUCT OK" or "UDP RECONSTRUCT FAILED" )
|
||||
test_assert(raw==udpb)
|
||||
|
||||
raw = reconstruct_dissect({ip=ip, udp=udp, payload=payload})
|
||||
dis1 = dissect(raw)
|
||||
udpb = csum_udp_fix(ip4b,udpb,payload)
|
||||
dis2 = dissect(ip4b..udpb..payload)
|
||||
print( dis1.udp.uh_sum==dis2.udp.uh_sum and "UDP+IP4 CSUM OK" or "UDP+IP4 CSUM FAILED" )
|
||||
test_assert(dis1.udp.uh_sum==dis2.udp.uh_sum)
|
||||
|
||||
raw = reconstruct_dissect({ip6=ip6, udp=udp, payload=payload})
|
||||
dis1 = dissect(raw)
|
||||
udpb = csum_udp_fix(ip6b,udpb,payload)
|
||||
dis2 = dissect(ip6b..udpb..payload)
|
||||
print( dis1.udp.uh_sum==dis2.udp.uh_sum and "UDP+IP6 CSUM OK" or "UDP+IP6 CSUM FAILED" )
|
||||
test_assert(dis1.udp.uh_sum==dis2.udp.uh_sum)
|
||||
end
|
||||
|
||||
function test_resolve()
|
||||
local pos
|
||||
|
||||
pos = zero_based_pos(resolve_multi_pos(fake_default_tls,"tls_client_hello","1,extlen,sniext,host,sld,midsld,endsld,endhost,-5"))
|
||||
test_assert(pos)
|
||||
print("resolve_multi_pos tls : "..table.concat(pos," "))
|
||||
pos = zero_based_pos(resolve_range(fake_default_tls,"tls_client_hello","host,endhost"))
|
||||
test_assert(pos)
|
||||
print("resolve_range tls : "..table.concat(pos," "))
|
||||
pos = resolve_pos(fake_default_tls,"tls_client_hello","midsld")
|
||||
test_assert(pos)
|
||||
print("resolve_pos tls : "..pos - 1)
|
||||
pos = resolve_pos(fake_default_tls,"tls_client_hello","method")
|
||||
test_assert(not pos)
|
||||
print("resolve_pos tls non-existent : "..tostring(pos))
|
||||
|
||||
pos = zero_based_pos(resolve_multi_pos(fake_default_http,"http_req","method,host,sld,midsld,endsld,endhost,-5"))
|
||||
test_assert(pos)
|
||||
print("resolve_multi_pos http : "..table.concat(pos," "))
|
||||
pos = resolve_pos(fake_default_http,"http_req","sniext")
|
||||
test_assert(not pos)
|
||||
print("resolve_pos http non-existent : "..tostring(pos))
|
||||
end
|
||||
|
||||
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,
|
||||
ip_id = math.random(0,0xFFFF),
|
||||
ip_off = 0,
|
||||
ip_ttl = 1,
|
||||
ip_p = IPPROTO_UDP,
|
||||
ip_src = pton("192.168.1.1"),
|
||||
ip_dst = pton("192.168.1.2")
|
||||
}
|
||||
udp = {
|
||||
uh_sport = math.random(0,0xFFFF),
|
||||
uh_dport = math.random(0,0xFFFF)
|
||||
}
|
||||
dis = {ip = ip, udp = udp, payload = payload}
|
||||
print("send ipv4 udp")
|
||||
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_print(d))
|
||||
end
|
||||
|
||||
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_print(raw, {repeats=5}))
|
||||
|
||||
ip6 = {
|
||||
ip6_flow = 0x60000000,
|
||||
ip6_hlim = 1,
|
||||
ip6_src = pton("fdce:3124:164a:5318::1"),
|
||||
ip6_dst = pton("fdce:3124:164a:5318::2")
|
||||
}
|
||||
dis = {ip6 = ip6, udp = udp, payload = payload}
|
||||
print("send ipv6 udp")
|
||||
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_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_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_print(d))
|
||||
end
|
||||
|
||||
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" })
|
||||
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" })
|
||||
ip6.ip6_flow = 0x60001234;
|
||||
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_print(d, {fwmark = 0x50EA}))
|
||||
end
|
||||
|
||||
fix_ip6_next(ip6) -- required to forge next proto in the second fragment
|
||||
ip6.ip6_flow = 0x6000AE38;
|
||||
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80, ipfrag_next = IPPROTO_TCP})
|
||||
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. 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_print(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true}))
|
||||
end
|
||||
end
|
||||
79
zapret-winws/lua/zapret-wgobfs.lua
Normal file
79
zapret-winws/lua/zapret-wgobfs.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-wgobfs.lua --in-range=a --out-range=a --lua-desync=wgobfs:secret=mycoolpassword
|
||||
-- encrypt standard wireguard messages - initiation, response, cookie - and change udp packet size
|
||||
-- do not encrypt data messages and keepalives
|
||||
-- wgobfs adds maximum of 30+padmax bytes to udp size
|
||||
-- reduce MTU of wireguard interface to avoid ip fragmentation !
|
||||
-- without knowing the secret encrypted packets should be crypto strong white noise with no signature
|
||||
-- arg : secret - shared secret. any string. must be the same on both peers
|
||||
-- arg : padmin - min random garbage bytes. 0 by default
|
||||
-- arg : padmax - max random garbage bytes. 16 by default
|
||||
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 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)
|
||||
_G[key_cache_name] = key
|
||||
end
|
||||
return key
|
||||
end
|
||||
local function maybe_encrypted_payload(payload)
|
||||
for k,plsize in pairs({2+12+16+148, 2+12+16+92, 2+12+16+64}) do
|
||||
if #payload>=(plsize+padmin) and #payload<=(plsize+padmax) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
local function wg_payload_from_size(payload)
|
||||
if #payload==148 then return "wireguard_initiation"
|
||||
elseif #payload==92 then return "wireguard_response"
|
||||
elseif #payload==64 then return "wireguard_cookie"
|
||||
else return nil
|
||||
end
|
||||
end
|
||||
|
||||
if not desync.dis.udp then
|
||||
instance_cutoff(ctx)
|
||||
return
|
||||
end
|
||||
if not desync.arg.secret or #desync.arg.secret==0 then
|
||||
error("wgobfs requires secret")
|
||||
end
|
||||
if padmin>padmax then
|
||||
error("wgobfs: padmin>padmax")
|
||||
end
|
||||
if desync.l7payload=="wireguard_initiation" or desync.l7payload=="wireguard_response" or desync.l7payload=="wireguard_cookie" and #desync.dis.payload<65506 then
|
||||
DLOG("wgobfs: encrypting '"..desync.l7payload.."'. size "..#desync.dis.payload)
|
||||
local key = genkey()
|
||||
-- in aes-gcm every message require it's own crypto secure random iv
|
||||
-- encrypting more than one message with the same iv is considered catastrophic failure
|
||||
-- iv must be sent with encrypted message
|
||||
local iv = bcryptorandom(12)
|
||||
local encrypted, atag = aes_gcm(true, key, iv, bu16(#desync.dis.payload)..desync.dis.payload..brandom(math.random(padmin,padmax)), nil)
|
||||
desync.dis.payload = iv..atag..encrypted
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
|
||||
if desync.l7payload=="unknown" and maybe_encrypted_payload(desync.dis.payload) then
|
||||
local key = genkey()
|
||||
local iv = string.sub(desync.dis.payload,1,12)
|
||||
local atag = string.sub(desync.dis.payload,13,28)
|
||||
local decrypted, atag2 = aes_gcm(false, key, iv, string.sub(desync.dis.payload,29))
|
||||
if atag==atag2 then
|
||||
local plen = u16(decrypted)
|
||||
if plen>(#decrypted-2) then
|
||||
DLOG("wgobfs: bad decrypted payload data")
|
||||
else
|
||||
desync.dis.payload = string.sub(decrypted, 3, 3+plen-1)
|
||||
if b_debug then DLOG("wgobfs: decrypted '"..(wg_payload_from_size(desync.dis.payload) or "unknown").."' message. size "..plen) end
|
||||
return VERDICT_MODIFY
|
||||
end
|
||||
else
|
||||
DLOG("wgobfs: decrypt auth tag mismatch")
|
||||
end
|
||||
end
|
||||
end
|
||||
41
zapret-winws/preset2_example.cmd
Normal file
41
zapret-winws/preset2_example.cmd
Normal file
@@ -0,0 +1,41 @@
|
||||
start "zapret: http,https,quic" /min "%~dp0winws2.exe" ^
|
||||
--wf-tcp-out=80,443 ^
|
||||
--lua-init=@"%~dp0lua\zapret-lib.lua" --lua-init=@"%~dp0lua\zapret-antidpi.lua" ^
|
||||
--lua-init="fake_default_tls = tls_mod(fake_default_tls,'rnd,rndsni')" ^
|
||||
--blob=quic_google:@"%~dp0files\quic_initial_www_google_com.bin" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.discord_media.txt" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.stun.txt" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.wireguard.txt" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.quic_initial_ietf.txt" ^
|
||||
--filter-tcp=80 --filter-l7=http ^
|
||||
--out-range=-d10 ^
|
||||
--payload=http_req ^
|
||||
--lua-desync=fake:blob=fake_default_http:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5 ^
|
||||
--lua-desync=fakedsplit:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5 ^
|
||||
--new ^
|
||||
--filter-tcp=443 --filter-l7=tls --hostlist="%~dp0files\list-youtube.txt" ^
|
||||
--out-range=-d10 ^
|
||||
--payload=tls_client_hello ^
|
||||
--lua-desync=fake:blob=fake_default_tls:tcp_md5:repeats=11:tls_mod=rnd,dupsid,sni=www.google.com ^
|
||||
--lua-desync=multidisorder:pos=1,midsld ^
|
||||
--new ^
|
||||
--filter-tcp=443 --filter-l7=tls ^
|
||||
--out-range=-d10 ^
|
||||
--payload=tls_client_hello ^
|
||||
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000:repeats=6 ^
|
||||
--lua-desync=multidisorder:pos=midsld ^
|
||||
--new ^
|
||||
--filter-udp=443 --filter-l7=quic --hostlist="%~dp0files\list-youtube.txt" ^
|
||||
--out-range=-d10 ^
|
||||
--payload=quic_initial ^
|
||||
--lua-desync=fake:blob=quic_google:repeats=11 ^
|
||||
--new ^
|
||||
--filter-udp=443 --filter-l7=quic ^
|
||||
--out-range=-d10 ^
|
||||
--payload=quic_initial ^
|
||||
--lua-desync=fake:blob=fake_default_quic:repeats=11 ^
|
||||
--new ^
|
||||
--filter-l7=wireguard,stun,discord ^
|
||||
--out-range=-d10 ^
|
||||
--payload=wireguard_initiation,wireguard_cookie,stun,discord_ip_discovery ^
|
||||
--lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2
|
||||
11
zapret-winws/preset_example.cmd
Normal file
11
zapret-winws/preset_example.cmd
Normal file
@@ -0,0 +1,11 @@
|
||||
start "zapret: http,https,quic" /min "%~dp0winws.exe" ^
|
||||
--wf-tcp=80,443 ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.discord_media.txt" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.stun.txt" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.wireguard.txt" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.quic_initial_ietf.txt" ^
|
||||
--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --new ^
|
||||
--filter-tcp=443 --hostlist="%~dp0files\list-youtube.txt" --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=md5sig --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new ^
|
||||
--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=midsld --dpi-desync-repeats=6 --dpi-desync-fooling=badseq,md5sig --new ^
|
||||
--filter-l7=quic --hostlist="%~dp0files\list-youtube.txt" --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic="%~dp0files\quic_initial_www_google_com.bin" --new ^
|
||||
--filter-l7=quic --dpi-desync=fake --dpi-desync-repeats=11
|
||||
3
zapret-winws/preset_wireguard.cmd
Normal file
3
zapret-winws/preset_wireguard.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
start "zapret: wireguard" /min "%~dp0winws.exe" ^
|
||||
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.wireguard.txt" ^
|
||||
--filter-l7=wireguard --dpi-desync=fake --dpi-desync-repeats=2
|
||||
30
zapret-winws/service_create.cmd
Normal file
30
zapret-winws/service_create.cmd
Normal file
@@ -0,0 +1,30 @@
|
||||
@rem THIS BATCH FILE REQUIRES MANUAL EDIT
|
||||
@rem SERVICE INSTALL IS COMMENTED TO PREVENT SCRIPT KIDDIES FROM DAMAGING THEIR SYSTEMS WITHOUT KNOWING HOW TO RECOVER
|
||||
@rem ЭТОТ ФАЙЛ ТРЕБУЕТ РЕДАКТИРОВАНИЯ
|
||||
@rem УСТАНОВКА СЛУЖБЫ ЗАКОММЕНТИРОВАНА, ЧТОБЫ ОГРАДИТЬ НИЧЕГО НЕ ПОНИМАЮЩИХ НАЖИМАТЕЛЕЙ НА ВСЕ ПОДРЯД ОТ ПРОБЛЕМ, КОТОРЫЕ ОНИ НЕ В СОСТОЯНИИ РЕШИТЬ
|
||||
@rem ЕСЛИ НИЧЕГО НЕ ПОНИМАЕТЕ - НЕ ТРОГАЙТЕ ЭТОТ ФАЙЛ, ОТКАЖИТЕСЬ ОТ ИСПОЛЬЗОВАНИЯ СЛУЖБЫ. ИНАЧЕ БУДЕТЕ ПИСАТЬ ПОТОМ ВОПРОСЫ "У МЕНЯ ПРОПАЛ ИНТЕРНЕТ , КАК ВОССТАНОВИТЬ"
|
||||
|
||||
set ARGS=^
|
||||
--wf-tcp=80,443 ^
|
||||
--wf-raw-part=@\"%~dp0windivert.filter\windivert_part.discord_media.txt\" ^
|
||||
--wf-raw-part=@\"%~dp0windivert.filter\windivert_part.stun.txt\" ^
|
||||
--wf-raw-part=@\"%~dp0windivert.filter\windivert_part.wireguard.txt\" ^
|
||||
--wf-raw-part=@\"%~dp0windivert.filter\windivert_part.quic_initial_ietf.txt\" ^
|
||||
--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --new ^
|
||||
--filter-tcp=443 --hostlist=\"%~dp0files\list-youtube.txt\" --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=md5sig --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new ^
|
||||
--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=midsld --dpi-desync-repeats=6 --dpi-desync-fooling=badseq,md5sig --new ^
|
||||
--filter-l7=quic --hostlist=\"%~dp0files\list-youtube.txt\" --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=\"%~dp0files\quic_initial_www_google_com.bin\" --new ^
|
||||
--filter-l7=quic --dpi-desync=fake --dpi-desync-repeats=11
|
||||
|
||||
rem call :srvinst winws1
|
||||
set ARGS=--wf-raw-part=@\"%~dp0windivert.filter\windivert_part.wireguard.txt\" ^
|
||||
--filter-l7=discord,stun --dpi-desync=fake
|
||||
rem call :srvinst winws2
|
||||
goto :eof
|
||||
|
||||
:srvinst
|
||||
net stop %1
|
||||
sc delete %1
|
||||
sc create %1 binPath= "\"%~dp0winws.exe\" %ARGS%" DisplayName= "zapret DPI bypass : %1" start= auto
|
||||
sc description %1 "zapret DPI bypass software"
|
||||
sc start %1
|
||||
7
zapret-winws/service_del.cmd
Normal file
7
zapret-winws/service_del.cmd
Normal file
@@ -0,0 +1,7 @@
|
||||
call :srvdel winws1
|
||||
call :srvdel winws2
|
||||
goto :eof
|
||||
|
||||
:srvdel
|
||||
net stop %1
|
||||
sc delete %1
|
||||
2
zapret-winws/service_start.cmd
Normal file
2
zapret-winws/service_start.cmd
Normal file
@@ -0,0 +1,2 @@
|
||||
sc start winws1
|
||||
sc start winws2
|
||||
2
zapret-winws/service_stop.cmd
Normal file
2
zapret-winws/service_stop.cmd
Normal file
@@ -0,0 +1,2 @@
|
||||
net stop winws1
|
||||
net stop winws2
|
||||
10
zapret-winws/task_create.cmd
Normal file
10
zapret-winws/task_create.cmd
Normal file
@@ -0,0 +1,10 @@
|
||||
@rem THIS BATCH FILE REQUIRES MANUAL EDIT
|
||||
@rem SERVICE INSTALL IS COMMENTED TO PREVENT SCRIPT KIDDIES FROM DAMAGING THEIR SYSTEMS WITHOUT KNOWING HOW TO RECOVER
|
||||
@rem ЭТОТ ФАЙЛ ТРЕБУЕТ РЕДАКТИРОВАНИЯ
|
||||
@rem УСТАНОВКА СЛУЖБЫ ЗАКОММЕНТИРОВАНА, ЧТОБЫ ОГРАДИТЬ НИЧЕГО НЕ ПОНИМАЮЩИХ НАЖИМАТЕЛЕЙ НА ВСЕ ПОДРЯД ОТ ПРОБЛЕМ, КОТОРЫЕ ОНИ НЕ В СОСТОЯНИИ РЕШИТЬ
|
||||
@rem ЕСЛИ НИЧЕГО НЕ ПОНИМАЕТЕ - НЕ ТРОГАЙТЕ ЭТОТ ФАЙЛ, ОТКАЖИТЕСЬ ОТ ИСПОЛЬЗОВАНИЯ СЛУЖБЫ. ИНАЧЕ БУДЕТЕ ПИСАТЬ ПОТОМ ВОПРОСЫ "У МЕНЯ ПРОПАЛ ИНТЕРНЕТ , КАК ВОССТАНОВИТЬ"
|
||||
|
||||
set WINWS1=--wf-l3=ipv4,ipv6 --wf-tcp=80,443 --dpi-desync=fake,fakedsplit --dpi-desync-ttl=7 --dpi-desync-fooling=md5sig
|
||||
rem schtasks /Create /F /TN winws1 /NP /RU "" /SC onstart /TR "\"%~dp0winws.exe\" %WINWS1%"
|
||||
rem set WINWS2=--wf-l3=ipv4,ipv6 --wf-udp=443 --dpi-desync=fake
|
||||
rem schtasks /Create /F /TN winws2 /NP /RU "" /SC onstart /TR "\"%~dp0winws.exe\" %WINWS2%"
|
||||
4
zapret-winws/task_remove.cmd
Normal file
4
zapret-winws/task_remove.cmd
Normal file
@@ -0,0 +1,4 @@
|
||||
schtasks /End /TN winws1
|
||||
schtasks /Delete /TN winws1 /F
|
||||
rem schtasks /End /TN winws2
|
||||
rem schtasks /Delete /TN winws2 /F
|
||||
2
zapret-winws/task_start.cmd
Normal file
2
zapret-winws/task_start.cmd
Normal file
@@ -0,0 +1,2 @@
|
||||
schtasks /Run /TN winws1
|
||||
rem schtasks /Run /TN winws2
|
||||
2
zapret-winws/task_stop.cmd
Normal file
2
zapret-winws/task_stop.cmd
Normal file
@@ -0,0 +1,2 @@
|
||||
schtasks /End /TN winws1
|
||||
rem schtasks /End /TN winws2
|
||||
14
zapret-winws/windivert.filter/README.txt
Normal file
14
zapret-winws/windivert.filter/README.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Цель этих фильтров - отсекать полезную нагрузку в режиме ядра, не насилуя процессор перенаправлением целого потока на winws.
|
||||
Задействуются через `winws --wf-raw-part=@filename`. Может быть несколько частичных фильтров. Они могут сочетаться с --wf-tcp и --wf-udp.
|
||||
Однако, язык фильтров windivert не содержит операций с битовыми полями, сдвигов и побитовой логики.
|
||||
Поэтому фильтры получились более слабыми, способными передавать неправильную нагрузку.
|
||||
Дофильтрация производится силами winws.
|
||||
|
||||
Описание языка фильтров : https://reqrypt.org/windivert-doc.html#filter_language
|
||||
Пример инстанса для пробития медиапотоков в discord : `winws --wf-raw-part=@windivert_part.discord_media.txt --wf-raw-part=@windivert_part.stun.txt --filter-l7=stun,discord --dpi-desync=fake`
|
||||
|
||||
|
||||
These filters are invoked using `winws --wf-raw-part=@filename`. Multiple filter parts are supported. They can be combined with --wf-tcp and --wf-udp.
|
||||
Filters are kernel mode and save great amount of CPU.
|
||||
However windivert cannot filter by bit fields, lacks shift and bitwise logic operations.
|
||||
Filters are relaxed and can pass wrong payloads. Finer filtering is done by winws.
|
||||
@@ -0,0 +1,20 @@
|
||||
outbound and ip and
|
||||
udp.DstPort>=50000 and udp.DstPort<=50099 and
|
||||
udp.PayloadLength=74 and
|
||||
udp.Payload32[0]=0x00010046 and
|
||||
udp.Payload32[2]=0 and
|
||||
udp.Payload32[3]=0 and
|
||||
udp.Payload32[4]=0 and
|
||||
udp.Payload32[5]=0 and
|
||||
udp.Payload32[6]=0 and
|
||||
udp.Payload32[7]=0 and
|
||||
udp.Payload32[8]=0 and
|
||||
udp.Payload32[9]=0 and
|
||||
udp.Payload32[10]=0 and
|
||||
udp.Payload32[11]=0 and
|
||||
udp.Payload32[12]=0 and
|
||||
udp.Payload32[13]=0 and
|
||||
udp.Payload32[14]=0 and
|
||||
udp.Payload32[15]=0 and
|
||||
udp.Payload32[16]=0 and
|
||||
udp.Payload32[17]=0
|
||||
@@ -0,0 +1,4 @@
|
||||
outbound and
|
||||
udp.PayloadLength>=256 and
|
||||
udp.Payload[0]>=0xC0 and udp.Payload[0]<0xD0 and
|
||||
udp.Payload[1]=0 and udp.Payload16[1]=0 and udp.Payload[4]=1
|
||||
3
zapret-winws/windivert.filter/windivert_part.stun.txt
Normal file
3
zapret-winws/windivert.filter/windivert_part.stun.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
outbound and
|
||||
udp.PayloadLength>=20 and
|
||||
udp.Payload32[1]=0x2112A442 and udp.Payload[0]<0x40
|
||||
@@ -0,0 +1,3 @@
|
||||
outbound and
|
||||
udp.PayloadLength=148 and
|
||||
udp.Payload[0]=0x01
|
||||
21
zapret-winws/windivert_delete.cmd
Normal file
21
zapret-winws/windivert_delete.cmd
Normal file
@@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
|
||||
if "%1%" == "del" (
|
||||
echo DELETE WINDIVERT DRIVER
|
||||
sc delete windivert
|
||||
sc stop windivert
|
||||
goto :end
|
||||
)
|
||||
|
||||
sc qc windivert
|
||||
if errorlevel 1 goto :end
|
||||
|
||||
echo.
|
||||
choice /C YN /M "Do you want to stop and delete windivert"
|
||||
if ERRORLEVEL 2 goto :eof
|
||||
|
||||
"%~dp0elevator" %0 del
|
||||
goto :eof
|
||||
|
||||
:end
|
||||
pause
|
||||
BIN
zapret-winws/winws.exe
Normal file
BIN
zapret-winws/winws.exe
Normal file
Binary file not shown.
BIN
zapret-winws/winws2.exe
Normal file
BIN
zapret-winws/winws2.exe
Normal file
Binary file not shown.
Reference in New Issue
Block a user