diff --git a/docs/changes.txt b/docs/changes.txt index 6af1bfe..082ae84 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -121,3 +121,4 @@ v0.7.5 * blockcheck2: check http3 with ipv6 exthdr * github actions: separate target arm-old with LUA classic, not JIT * zapret-auto: iff/neg in repeater +* zapret-antidpi: multidisorder_legacy diff --git a/lua/zapret-antidpi.lua b/lua/zapret-antidpi.lua index c2b7c18..1e5fe4a 100644 --- a/lua/zapret-antidpi.lua +++ b/lua/zapret-antidpi.lua @@ -416,7 +416,48 @@ function multisplit(ctx, desync) end end +-- internal function for code deduplication. do not call directly +function pos_normalize(pos, low, hi) + return (pos>=low and pos0 then + if seqovl>=pos[1] then + DLOG("multidisorder: seqovl cancelled because seqovl "..(seqovl-1).." is not less than the first split pos "..(pos[1]-1)) + else + ovl = seqovl - 1 + local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00" + part = pattern(pat,1,ovl)..part + end + end + if b_debug then DLOG("multidisorder: sending part "..(i+1).." "..(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) then + return VERDICT_PASS + end + end +end + -- nfqws1 : "--dpi-desync=multidisorder" +-- algorithm is not 100% the same as in nfqws1. multi-segment queries can produce different segment ordering. -- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag -- arg : pos= . 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. @@ -440,32 +481,16 @@ function multidisorder(ctx, desync) 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 + local seqovl + if 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.."'") end end + if multidisorder_send(desync, data, seqovl, pos)==VERDICT_PASS then + return VERDICT_PASS + end replay_drop_set(desync) return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP else @@ -481,6 +506,59 @@ function multidisorder(ctx, desync) end end +-- nfqws1 : "--dpi-desync=multidisorder". segment ordering is the same as in nfqws1 +-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag +-- arg : pos= . 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= . override pattern +function multidisorder_legacy(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 = desync.dis.payload + local fulldata = desync.reasm_data + if #data>0 and direction_check(desync) and payload_check(desync) then + local range_low = (desync.reasm_offset or 0) + 1 + local range_hi = range_low + #data + local spos = desync.arg.pos or "2" + -- check debug to save CPU + if b_debug then DLOG("multidisorder_legacy: split pos: "..spos) end + local pos = resolve_multi_pos(fulldata, desync.l7payload, spos) + if b_debug then DLOG("multidisorder_legacy: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end + DLOG("multidisorder_legacy: reasm piece range "..(range_low-1).."-"..(range_hi-2)) + pos_array_normalize(pos, range_low, range_hi) + delete_pos_1(pos) -- cannot split at the first byte + if #pos>0 then + if b_debug then DLOG("multidisorder_legacy: normalized split pos: "..table.concat(zero_based_pos(pos)," ")) end + local seqovl + if desync.arg.seqovl then + seqovl = resolve_pos(fulldata, desync.l7payload, desync.arg.seqovl) + if seqovl then + DLOG("multidisorder_legacy: resolved seqovl pos to "..(seqovl-1)) + seqovl = pos_normalize(seqovl, range_low, range_hi) + if seqovl then + DLOG("multidisorder_legacy: normalized seqovl pos to "..(seqovl-1)) + else + DLOG("multidisorder_legacy: normalized seqovl pos is outside of the reasm piece range") + end + else + DLOG("multidisorder_legacy: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'") + end + end + return multidisorder_send(desync, data, seqovl, pos) + else + DLOG("multidisorder_legacy: no normalized split pos in this packet") + -- send as is with applied options + if rawsend_payload_segmented(desync) then + return VERDICT_DROP + end + 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= - hostname template. generate hosts like "random.template". example : e8nzn.vk.com