201 lines
8 KiB
Lua
201 lines
8 KiB
Lua
-- custom helper for nfqws2
|
|
-- load after zapret-lib.lua and zapret-antidpi.lua:
|
|
-- --lua-init=@/mnt/g/Privacy/zapret2_custom/fakemultidisorder.lua
|
|
--
|
|
-- usage example:
|
|
-- --lua-desync=fakemultidisorder:fake_blob=fake_default_tls:pos=1,midsld+1:seqovl=host:seqovl_pattern=0x1603030000
|
|
--
|
|
-- behavior:
|
|
-- real payload is split like multidisorder and sent from last segment to first
|
|
-- by default only the first original-order segment is faked, and that fake is sent first
|
|
-- this reduces packet count versus fake-all-parts while targeting the bytes DPI usually cares about most
|
|
-- fake data is taken from fake_blob at the same byte offsets; if it is shorter, the tail is padded with pattern or zero bytes
|
|
|
|
local function fakemultidisorder_fake_part(fake_data, fake_pat, pos_start, part_len)
|
|
local part = ""
|
|
if fake_data and pos_start <= #fake_data then
|
|
part = string.sub(fake_data, pos_start, pos_start + part_len - 1)
|
|
end
|
|
if #part < part_len then
|
|
part = part .. pattern(fake_pat, 1, part_len - #part)
|
|
end
|
|
return part
|
|
end
|
|
|
|
local function fakemultidisorder_part_bounds(pos, data_len, part_n)
|
|
local pos_start = part_n == 1 and 1 or pos[part_n - 1]
|
|
local pos_end = part_n <= #pos and (pos[part_n] - 1) or data_len
|
|
return pos_start, pos_end
|
|
end
|
|
|
|
local function fakemultidisorder_has_fake_protection(arg)
|
|
return arg.badsum or arg.fool or
|
|
arg.ip_ttl or arg.ip6_ttl or arg.ip_autottl or arg.ip6_autottl or
|
|
arg.ip6_hopbyhop or arg.ip6_hopbyhop2 or arg.ip6_destopt or arg.ip6_destopt2 or arg.ip6_routing or arg.ip6_ah or
|
|
arg.tcp_seq or arg.tcp_ack or arg.tcp_ts or arg.tcp_md5 or
|
|
arg.tcp_flags_set or arg.tcp_flags_unset or arg.tcp_nop_del
|
|
end
|
|
|
|
-- nfqws2 custom : "multidisorder" with interleaved "fake"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct
|
|
-- FOOLING AND REPEATS APPLIED ONLY TO FAKES. real parts keep only ip_id and tcp_ts_up, like fakeddisorder
|
|
-- arg : pos=<posmarker list> . position marker list. for example : "1,host,midsld+1,-10"
|
|
-- arg : fake_blob=<blob> - fake payload source. slices are taken from matching offsets
|
|
-- arg : pattern=<blob> - padding pattern for fake slices when fake_blob is shorter. default - zero byte
|
|
-- arg : fake_count=N - how many initial original-order segments to fake before real disorder. default - 1
|
|
-- arg : fake_all - fake all segments before real disorder
|
|
-- arg : unsafe_fake - do not auto-enable badsum on fake packets when no explicit fake fooling was requested
|
|
-- arg : nofakeN - skip N-th fake segment in original segment numbering, for example nofake1 or nofake3
|
|
-- arg : seqovl=<posmarker> . same semantics as multidisorder: decrease seq number of the second segment in the original order
|
|
-- arg : seqovl_pattern=<blob> . override pattern
|
|
-- arg : blob=<blob> - use this data instead of desync.dis.payload/reasm_data as real payload
|
|
-- arg : optional - skip if blob/fake_blob is absent. use zero pattern if seqovl_pattern or pattern blob is absent
|
|
-- arg : tls_mod=<list> - optional TLS modifications for fake_blob, same format as in fake()
|
|
-- arg : nodrop - do not drop current dissect
|
|
function fakemultidisorder(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
|
|
return
|
|
end
|
|
|
|
direction_cutoff_opposite(ctx, desync)
|
|
|
|
if not desync.arg.fake_blob then
|
|
error("fakemultidisorder: 'fake_blob' arg required")
|
|
end
|
|
|
|
if desync.arg.optional and desync.arg.blob and not blob_exist(desync, desync.arg.blob) then
|
|
DLOG("fakemultidisorder: blob '"..desync.arg.blob.."' not found. skipped")
|
|
return
|
|
end
|
|
if desync.arg.optional and not blob_exist(desync, desync.arg.fake_blob) then
|
|
DLOG("fakemultidisorder: fake_blob '"..desync.arg.fake_blob.."' not found. skipped")
|
|
return
|
|
end
|
|
|
|
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"
|
|
if b_debug then DLOG("fakemultidisorder: split pos: "..spos) end
|
|
|
|
local pos = resolve_multi_pos(data, desync.l7payload, spos)
|
|
if b_debug then DLOG("fakemultidisorder: resolved split pos: "..table.concat(zero_based_pos(pos), " ")) end
|
|
delete_pos_1(pos)
|
|
|
|
if #pos>0 then
|
|
local seqovl
|
|
if desync.arg.seqovl then
|
|
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
|
|
if not seqovl then
|
|
DLOG("fakemultidisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
|
|
end
|
|
end
|
|
|
|
local fake_data = blob(desync, desync.arg.fake_blob)
|
|
if desync.reasm_data and desync.arg.tls_mod then
|
|
local pl = tls_mod_shim(desync, fake_data, desync.arg.tls_mod, desync.reasm_data)
|
|
if pl then fake_data = pl end
|
|
end
|
|
|
|
local fake_pat = "\x00"
|
|
if desync.arg.pattern then
|
|
if desync.arg.optional and not blob_exist(desync, desync.arg.pattern) then
|
|
DLOG("fakemultidisorder: blob '"..desync.arg.pattern.."' not found. using zero pattern")
|
|
else
|
|
fake_pat = blob(desync, desync.arg.pattern)
|
|
end
|
|
end
|
|
|
|
local opts_orig = {
|
|
rawsend = rawsend_opts_base(desync),
|
|
reconstruct = {},
|
|
ipfrag = {},
|
|
ipid = desync.arg,
|
|
fooling = {tcp_ts_up = desync.arg.tcp_ts_up}
|
|
}
|
|
local fake_reconstruct = reconstruct_opts(desync)
|
|
if not desync.arg.unsafe_fake and not fakemultidisorder_has_fake_protection(desync.arg) then
|
|
fake_reconstruct.badsum = true
|
|
if b_debug then DLOG("fakemultidisorder: enabled badsum for fake packets by default") end
|
|
end
|
|
local opts_fake = {
|
|
rawsend = rawsend_opts(desync),
|
|
reconstruct = fake_reconstruct,
|
|
ipfrag = {},
|
|
ipid = desync.arg,
|
|
fooling = desync.arg
|
|
}
|
|
|
|
local part_count = #pos + 1
|
|
local fake_count = tonumber(desync.arg.fake_count) or 1
|
|
if desync.arg.fake_all then
|
|
fake_count = part_count
|
|
elseif fake_count < 0 then
|
|
fake_count = 0
|
|
elseif fake_count > part_count then
|
|
fake_count = part_count
|
|
end
|
|
|
|
for part_n=1,fake_count do
|
|
local pos_start, pos_end = fakemultidisorder_part_bounds(pos, #data, part_n)
|
|
local part_len = pos_end - pos_start + 1
|
|
local fake_part
|
|
|
|
if not desync.arg["nofake"..tostring(part_n)] then
|
|
fake_part = fakemultidisorder_fake_part(fake_data, fake_pat, pos_start, part_len)
|
|
if b_debug then
|
|
DLOG("fakemultidisorder: sending prefake part "..part_n.." "..(pos_start-1).."-"..(pos_end-1).." len="..#fake_part.." : "..hexdump_dlog(fake_part))
|
|
end
|
|
if not rawsend_payload_segmented(desync, fake_part, pos_start-1, opts_fake) then
|
|
return VERDICT_PASS
|
|
end
|
|
end
|
|
end
|
|
|
|
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_n = i + 1
|
|
|
|
local part = string.sub(data, pos_start, pos_end)
|
|
local ovl = 0
|
|
if i==1 and seqovl and seqovl>0 then
|
|
if seqovl>=pos[1] then
|
|
DLOG("fakemultidisorder: seqovl cancelled because seqovl "..(seqovl-1).." is not less than the first split pos "..(pos[1]-1))
|
|
else
|
|
ovl = seqovl - 1
|
|
local pat = "\x00"
|
|
if desync.arg.seqovl_pattern then
|
|
if desync.arg.optional and not blob_exist(desync, desync.arg.seqovl_pattern) then
|
|
DLOG("fakemultidisorder: blob '"..desync.arg.seqovl_pattern.."' not found. using zero pattern")
|
|
else
|
|
pat = blob(desync, desync.arg.seqovl_pattern)
|
|
end
|
|
end
|
|
part = pattern(pat, 1, ovl) .. part
|
|
end
|
|
end
|
|
|
|
if b_debug then
|
|
DLOG("fakemultidisorder: sending real part "..part_n.." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..ovl.." : "..hexdump_dlog(part))
|
|
end
|
|
if not rawsend_payload_segmented(desync, part, pos_start-1-ovl, opts_orig) then
|
|
return VERDICT_PASS
|
|
end
|
|
end
|
|
|
|
replay_drop_set(desync)
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
else
|
|
DLOG("fakemultidisorder: no valid split positions")
|
|
end
|
|
else
|
|
DLOG("fakemultidisorder: not acting on further replay pieces")
|
|
end
|
|
|
|
if replay_drop(desync) then
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
end
|
|
end
|