Advanced
Reference
The winws2 argument format: everything you need to write your own strategy without running into "green but not working".
You can create your own strategy on the "Strategies" tab: duplicate a built-in one and edit the copy.
It is saved in presets.json. To inspect the resulting command line in full without
launching the engine, use the command preview in the interface.
What follows are practical rules verified against the engine that ships with the program. The exhaustive primary source for every capability is the official zapret2 manual, in English.
Command structure
A command consists of a global part and one or more profiles. Every profile after the
first begins with --new.
# global part: capture width, blobs, cache
--wf-tcp-out=80,443-65535
--blob=tls_google:@{FILES}\fake\tls_clienthello_www_google_com.bin
# the first profile follows straight away, with no --new
--filter-tcp=443-65535 --filter-l7=tls {HOSTLIST:discord}
--payload=tls_client_hello
--lua-desync=fake:blob=tls_google:tcp_ts=-1000:repeats=6
--new
# the second profile
--filter-udp=443-65535 --filter-l7=quic {HOSTLIST:discord}
--payload=quic_initial
--lua-desync=fake:blob=quic_google:repeats=11
- The global part comes before the first
--newand holds--wf-*,--blob=,--ipcache-*,--lua-init=. The first profile lives there too. - Capture ports are listed comma-separated in one flag. A second
--wf-tcp-outdoes not add ports, it overwrites the previous one. - A profile answers two questions: who it applies to (the filters) and what to do
(
--lua-desync). A profile may carry several techniques; they run in order.
Sticky flags
Three flags apply from where they are written until they are overridden, not to the whole profile. This is the most common reason for strategies that look right but behave otherwise.
| Flag | What it sets |
|---|---|
--payload= | Which packet counts as the target: tls_client_hello, quic_initial and others. |
--out-range= | The range of outgoing packets the technique applies to. For example -d10. |
--in-range= | The same for incoming ones. Orchestrators need it to see resets from the DPI. |
Put them before the --lua-desync they affect. Placed after, the technique runs
with the previous values. There will be no error: the engine starts and the bypass simply does not
work.
The --filter-*, --hostlist and --ipset flags are not sticky;
their order within a profile does not matter.
Profile filters
| Flag | Meaning |
|---|---|
--filter-tcp= | TCP ports, for example 443-65535. |
--filter-udp= | UDP ports, for example 19294-19344,50000-65535. |
--filter-l7= | Protocol: tls, quic, http, discord, stun. Several can be listed comma-separated. |
--hostlist= | Apply only to domains from the file. |
--hostlist-exclude= | Do not apply to domains from the file. This is how banks and government services are protected from catch-all profiles. |
--ipset= | Apply to the subnets in the file. For when a service is cut off by address. |
Tokens
Strategies are stored with tokens rather than absolute paths. The program expands them at launch, substituting the current paths and settings.
| Token | Expands to |
|---|---|
{FILES} | Path to the engine\files folder, where the blobs live. |
{WF} | Path to the engine\windivert.filter folder. |
{WF_TCP} | --wf-tcp-out=80,443-65535: the TCP capture width. |
{WF_UDP} | --wf-udp-out= with 443, STUN and Discord voice. The game filter widens the range. |
{HOSTLIST} | --hostlist= with the active list, or nothing. |
{HOSTLIST:name} | --hostlist=lists\name.txt, or nothing if the file is missing. |
{EXCLUDE:name} | --hostlist-exclude=lists\name.txt. |
{IPSET} | --ipset= with the built list of Discord subnets, if there is one. |
{IPSET:name} | --ipset=lists\ipset-name.txt. |
An empty token is simply dropped from the command. For example, {HOSTLIST} with no
active list leaves nothing behind.
Desync verbs
Written as --lua-desync=verb:parameter=value:.... The most
important column here is the last one: it decides which fooling you may use and which will break the
connection.
| Verb | What it does | Fooling applies to |
|---|---|---|
fake |
Inserts a fake packet from a blob. | fakes only; the real packet goes out clean |
multisplit |
Cuts the real packet into segments. | every segment, including the real ones |
multidisorder |
Cuts it and sends the pieces in reverse order. | every segment |
multidisorder_legacy |
The earlier variant of the same technique. | every segment |
hostfakesplit |
Cuts on the name marker and substitutes a false name of the same length. Adapts to the packet size. | fakes only; fooling is mandatory |
fakedsplit |
Splitting with forged segments inserted. | the forged ones only |
fakeddisorder |
The same in reverse order. | the forged ones only |
syndata |
Puts data into the connection-setup packet. | nothing destructive: tcp_seq, tcp_ack and badsum will break the handshake |
tcpseg |
One segment with an overlap. Does not remove the original; add --lua-desync=drop after it. |
|
wssize, wsize |
Shrink the TCP window, forcing the server to break its reply into pieces. | not applied |
drop, pass |
Drop or let the packet through. | not applied |
The main rule. Never put tcp_ack, tcp_seq, badsum or
a broken TTL into multisplit or multidisorder: they will be applied to the
real segments and break the connection. Only pos, seqovl and harmless
things like ip_id belong there. Destructive fooling lives in fake and the
faked* family.
Common parameters: repeats=N sets how many times the fake is repeated;
:optional asks the engine to skip the technique quietly if it did not apply, rather than
aborting the rest of the processing.
Fooling
Techniques that stop the fake from reaching the server. More on why this is needed: How it works.
| Parameter | Mechanism | Caveats |
|---|---|---|
tcp_ts=-N |
An older timestamp; the packet is discarded by PAWS. | Negative values only. Requires TCP timestamps to be enabled. |
tcp_md5 |
Adds an MD5 signature option; an ordinary server does not accept such a packet. | Safe behind a home NAT. A good replacement for badsum. |
tcp_seq=-N |
Shifts the sequence number outside the window. | Some DPIs also check sequence numbers and stop believing the fake. |
badsum |
Corrupts the checksum. | Often useless at home: a router behind NAT drops the packet before the DPI sees it. |
ip_autottl, ip6_autottl |
Picks a TTL so the packet expires between the DPI and the server. | Depends on the route; may stop fitting when the network changes. |
ip_id=zero |
Zeroes the identifier in the IP header. | An extra signal, harmless. |
tls_mod=rnd,rndsni |
Varies the contents of the fake client hello so it does not repeat. | Applied to the blob, not to the real packet. |
The sign of tcp_ts is critical. PAWS discards the packet with the smaller
timestamp. A negative value kills the fake; a positive one kills the real packet. In the
second case the connection breaks silently, and it looks like "the bypass is on but nothing
opens".
Beyond that, ts techniques are pointless without TCP timestamps enabled. Windows keeps them in the
allowed state, which does not guarantee a timestamp in outgoing packets, so Zapret2UI
switches them to enabled for the session and restores the previous value on stop.
Position markers
These say where to cut the packet. Used in pos= and accept offsets:
sld+1, midsld-2. Several cut points are listed comma-separated.
| Marker | Where it points |
|---|---|
method | The start of the request method. |
host | The start of the site name. |
endhost | The end of the site name. |
sld | The start of the second-level domain. |
midsld | The middle of the second-level domain. The most commonly used one. |
endsld | The end of the second-level domain. |
sniext | The start of the extension carrying the site name. |
extlen | The extension length field. |
pos=1 on its own does nothing. The engine strips that position and the
technique becomes a no-op. It needs a second, real marker: pos=1,midsld.
About seqovl
The seqovl parameter means different things for different verbs, and that is a
regular source of mistakes.
| Where | What it means |
|---|---|
multisplit |
A number of bytes: how many bytes of content to prepend, overlapping the start of the real packet. Values are often large, such as 681. |
multidisorder |
A position marker, and it must be smaller than the first cut point. |
The seqovl_pattern= parameter says what to fill the overlap with. It makes sense to put
a real ClientHello there, for example tls_google: a plausible beginning survives checks
that random junk does not pass.
Blobs
A blob is a file containing a ready-made network packet used as a fake. First it is given a name in the global part, then it is referred to by that name.
--blob=tls_google:@{FILES}\fake\tls_clienthello_www_google_com.bin
...
--lua-desync=fake:blob=tls_google:repeats=6
The engine ships with client hellos from www.google.com, vk.com,
sberbank.ru and gosuslugi.ru, plus their QUIC variants. In strategies they
are available as tls_google, tls_vk, tls_sber,
tls_gos, quic_google, quic_vk.
Names like tls5, tls7, tls1 do not exist here. They
appear in third-party builds that ship their own blobs. If you are bringing a strategy over from
one of those, replace any such tlsN with tls_google. An unknown blob name
causes a start-up error.
Orchestrators
A separate class: they do not modify packets, they drive other techniques. They live in the
zapret-auto.lua library, which the engine loads automatically.
| Orchestrator | What it does |
|---|---|
circular | Cycles through variants, switching on failure. |
repeater | Repeats a technique. |
stopif | Breaks the chain on a condition. |
condition | Applies a technique conditionally. |
The most useful of them is circular. The rules are:
- Declared as
--lua-desync=circular:fails=N:time=T, with the variants following. - Each technique is tagged
strategy=K. Numbering starts at one and runs without gaps. A single variant may include several techniques. - Requires
--in-range=: without a cache of incoming packets the detector cannot see a reset from the DPI, so there is nothing to switch on. - Without a
finalmark, the variants keep cycling until a working one is found. Once found, it is left alone, and the engine is not restarted.
A complete working example is walked through in the adaptive strategy breakdown.
A complete example
A minimal working strategy for Discord: TLS with a fake and a split, QUIC with a fake, and voice.
{WF_TCP}
{WF_UDP}
--ipcache-hostname=1
--blob=tls_google:@{FILES}\fake\tls_clienthello_www_google_com.bin
--blob=quic_google:@{FILES}\fake\quic_initial_www_google_com.bin
--wf-raw-part=@{WF}\windivert_part.stun.txt
--wf-raw-part=@{WF}\windivert_part.quic_initial_ietf.txt
# 1. Discord over TLS
--filter-tcp=443-65535 --filter-l7=tls {HOSTLIST:discord}
--out-range=-d10 --payload=tls_client_hello
--lua-desync=fake:blob=tls_google:tcp_md5:tcp_seq=-10000:repeats=6
--lua-desync=multisplit:pos=1,midsld:seqovl=681:seqovl_pattern=tls_google:optional
--new
# 2. Discord over QUIC: attachments and CDN
--filter-udp=443-65535 --filter-l7=quic {HOSTLIST:discord}
--payload=quic_initial
--lua-desync=fake:blob=quic_google:repeats=11
--new
# 3. Voice
--filter-udp=19294-19344,50000-65535 --filter-l7=discord,stun
--lua-desync=fake:blob=quic_google:repeats=6
The engine only starts with administrator rights, so the only real way to test a strategy is to run it. The command preview in the interface shows the full line and helps catch typos in advance.
Common mistakes
| Symptom | Cause |
|---|---|
| The engine exits with code 87 | Invalid parameter: a typo in a verb name, an unknown blob name, or a missing library. The journal will carry a line with the detail. |
| The bypass is on but nothing changed | A sticky flag placed after the technique. Or pos=1 without a second marker. Or
tcp_ts without timestamps enabled. |
| The connection breaks completely | Destructive fooling ended up in multisplit or multidisorder and
was applied to the real segments. Or tcp_ts was given a positive value. |
| Login works but nothing else does | The capture is too narrow. Discord media travels on high ports; catching 443 alone is not enough. |
| Voice does not work under any strategy | There is no profile for the voice UDP range, or windivert_part.stun.txt is not
attached. |
tcpseg has no effect |
It does not remove the original packet. A following drop technique is
required. |