This commit is contained in:
bol-van
2025-12-14 18:52:10 +03:00
commit d1f72b1edf
506 changed files with 28858 additions and 0 deletions

BIN
zapret-winws/WinDivert.dll Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
@echo off
"%~dp0elevator" cmd /k cd /d "%~dp0"

BIN
zapret-winws/cygwin1.dll Normal file

Binary file not shown.

BIN
zapret-winws/elevator.exe Normal file

Binary file not shown.

View 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

View 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

Binary file not shown.

BIN
zapret-winws/killall.exe Normal file

Binary file not shown.

View 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

View 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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,7 @@
call :srvdel winws1
call :srvdel winws2
goto :eof
:srvdel
net stop %1
sc delete %1

View File

@@ -0,0 +1,2 @@
sc start winws1
sc start winws2

View File

@@ -0,0 +1,2 @@
net stop winws1
net stop winws2

View 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%"

View 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

View File

@@ -0,0 +1,2 @@
schtasks /Run /TN winws1
rem schtasks /Run /TN winws2

View File

@@ -0,0 +1,2 @@
schtasks /End /TN winws1
rem schtasks /End /TN winws2

View 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.

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
outbound and
udp.PayloadLength>=20 and
udp.Payload32[1]=0x2112A442 and udp.Payload[0]<0x40

View File

@@ -0,0 +1,3 @@
outbound and
udp.PayloadLength=148 and
udp.Payload[0]=0x01

View 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

Binary file not shown.

BIN
zapret-winws/winws2.exe Normal file

Binary file not shown.