You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Please Add outbound TLS spoof and spoof_method fields. When enabled, a forged ClientHello carrying a whitelisted SNI is sent before the real handshake to fool SNI-filtering middleboxes. Requires CAP_NET_RAW + CAP_NET_ADMIN or root on Linux and macOS, and Administrator privileges on Windows (ARM64 is not supported). IP-literal server names are rejected.
Reacted by megasoheilsh, nima1024m, Darcy Tate, mmx2004, montiego, 易安 and Farshad
Reacted by محمد علی پور فتحکوهی, megasoheilsh and Farshad
Please Add outbound TLS spoof and spoof_method fields. When enabled, a forged ClientHello carrying a whitelisted SNI is sent before the real handshake to fool SNI-filtering middleboxes. Requires CAP_NET_RAW + CAP_NET_ADMIN or root on Linux and macOS, and Administrator privileges on Windows (ARM64 is not supported). IP-literal server names are rejected.
I tested it on Linux and it works properly, but Windows doesn't have any fake packets that should have fake SNI inside, apparently there is a problem with windivert.
Please Add outbound TLS spoof and spoof_method fields. When enabled, a forged ClientHello carrying a whitelisted SNI is sent before the real handshake to fool SNI-filtering middleboxes. Requires CAP_NET_RAW + CAP_NET_ADMIN or root on Linux and macOS, and Administrator privileges on Windows (ARM64 is not supported). IP-literal server names are rejected.
By the way, the idea and implementation of TLS spoof is for @patterniha , Singbox doesn't know anything else except copy paste.
Reacted by محمد علی پور فتحکوهی, Omid, montiego, vod666kaU, Ali Kesht Kar and Gleb
this could be implemented as "serverless"
the way that people in iran are using TLS spoofing is as a fake server (not a proxy!)
then pointing their VLESS configs at the spoofer
when the spoofer recieves packets, it connects to a big cdn that they cant block (with the spoofed SNI) and forwards the packets
BlackHole keeps the real Xray TLS/SNI/transport configuration untouched and applies an external DPI-desync layer using winws2/Zapret2 + WinDivert.
The interesting part is that, in testing, simple TLS fragmentation is not always sufficient, while combinations of the following techniques can make a significant difference:
TLS ClientHello split / multisplit
multidisorder
semantic split positions such as midsld and sniext+1
fake ClientHello / fake SNI decoys
TCP badseq
TTL / AutoTTL
badsum
md5sig
repeated fake packets
I think this is closely related to #5964 and #5969.
The main feature I would like to propose is not bundling winws2 into Xray, but introducing an optional native desync abstraction in Xray-core.
Windows → WinDivert-compatible backend if necessary
Linux → raw socket / NFQUEUE or another appropriate backend
The advantage of implementing this inside Xray is that desync could be attached to a specific outbound/connection, instead of intercepting traffic globally.
It would also allow Xray to keep the current lightweight fragment implementation while exposing more advanced techniques only when explicitly enabled.
BlackHole can be used as a practical testbed/reference for configurations that currently require Xray + an external desync engine.
If maintainers are interested in this direction, I can also document which individual primitives/combinations have been effective and map them into a minimal Xray-side API
Proposal: a client-side fake-segment finalmask.tcp mask ("tls spoof")
A concrete spec for the narrow piece, following the guidance already given on #6103 ("add as a tcpmask", generic naming, base64 payloads). Happy to implement on current main if there is appetite.
Mechanism (verified against sing-box common/tlsspoof @ 2ff3985c)
Before the real ClientHello, send one hand-built TCP/IP segment carrying a fake ClientHello for a whitelisted SNI on the same 4-tuple, via raw socket (WinDivert on Windows). The segment is deliberately invalid so the origin server drops it, while a passive SNI filter still reads it:
wrong-sequence (default): seq = snd_nxt - len(fake) -> ends at the peer's rcv_nxt -> old duplicate, discarded
snd_nxt/rcv_nxt: TCP_REPAIR + TCP_QUEUE_SEQ (Linux), pcblist_n (macOS), captured packet (Windows). The fake CH is generated with crypto/tls, classical curves pinned so it stays under one MSS. Kernel TCP state is never touched; the real handshake proceeds unchanged.
Placement
Client-only finalmask.tcp mask. FinalMask.DialTCP already wraps the raw conn before TLS/REALITY in tcp/ws/grpc/xhttp/httpupgrade, so the first Write is the real ClientHello: no dialer changes, no tlsSettings coupling, server role no-op.
payload = arbitrary fake bytes; sni auto-generates a CH (wins if both set); neither -> no-op. Name left to maintainers.
Platforms / privileges
Linux: CAP_NET_RAW + CAP_NET_ADMIN, IPv4/IPv6. macOS: root, no wrong-timestamp. Windows x64/x86: Administrator + WinDivert (ARM64 unsupported). Elsewhere: warn + no-op. Fails open (log + continue) when caps/driver missing - best-effort by design.
Testing
Unit: frame-builder goldens per method x IPv4/IPv6; fake CH under one MSS. Integration (root-gated, tcpdump): fake and real SNI both observed on the wire, server receives the real CH unchanged; full method matrix. Docs under finalmask.
Notes
Credits to @codewithtamim: TLS : tls spoofing #6103 pioneered this and carries a similar port (draft since June, needs rebase; bundled with a larger rawpacket transport). This spec is meant to be the narrow, reviewable piece - happy to coordinate so nothing duplicates, or fold into TLS : tls spoofing #6103's work instead.
Iranian reports in TLS : tls spoofing #6103 showed detection on some operators; the ttl/count knobs and method choice (plus stacking with the existing fragment mask) target exactly that, and I can test from inside Iran.
Implementation would be written fresh from the technique (MIT-licensed reference) so provenance is clean for MPL-2.0.
Activity
megasoheilsh commentedon Apr 17, 2026
I tested it on Linux and it works properly, but Windows doesn't have any fake packets that should have fake SNI inside, apparently there is a problem with windivert.
megasoheilsh commentedon Apr 17, 2026
By the way, the idea and implementation of TLS spoof is for @patterniha , Singbox doesn't know anything else except copy paste.
alipour66m commentedon Apr 17, 2026
I love Xray core.
I tested on windows and working well with .exe from @patterniha
Fangliding commentedon Apr 17, 2026
这个东西很久之前就有人提出过了 除了无效的序列号还有短ttl等等等等 俗称desync....
至于这个功能本身我觉得现在这样放在一个单独的程序上就好 反正也没法跨平台 而且封禁并不难
mehdi-saber commentedon Apr 17, 2026
@Fangliding
Have you considered this might actually work in Iran, given the DPI is SNI-based, not sequence-aware?
RPRX commentedon Apr 17, 2026
给 finalmask 的 header-custom 加个 TTL 不就行了 @LjhAUMEM
LjhAUMEM commentedon Apr 18, 2026
给的链接怎么都是 singbox,不如直接去用 singbox最近开始忙了,不想研究 new feature 了,之前说的 xicmp 多 ip 可能也要推迟,
xicmp 谁有兴趣重构也欢迎 prmegasoheilsh commentedon Apr 18, 2026
我尝试了一个,但遇到了错误:tls: server did not echo the legacy session ID。
"finalmask": {
"tcp": [
{
"type": "header-custom",
"settings": {
"clients": [
[
{
"delay": 0,
"type": "hex",
"packet": "16030107a8010007a403034852c52cc41358f9decaf36064e503cabc3be22bdbc3df0e82b2079b53689f4420c5dfa397eb9281864b71bfd4b0056fe273e6bc9a4678075509969a555432f19900208a8a130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f00350100073b1a1a0000fe0d00ba000001000183002066ed8d3f6bc3d31f8f2d36764e8eb25767b72279d68ec6095daf37225066275900904c4f63288c9f11b26e2007b143a6cf5760752e6467deb231dee035342ebdddf54386fd62b178c8008740250d28fc7fdeb63d8a02b07ffc126c33a176f5bd41e96e70e608c8a0547a1a07d6ba67f19622437fa41ff4c3021a4d1ba89175415bfcbb22971fd7708f0020bbdd42b718998b554dd906abe19593f36f6a644b098e0919083e4e29fabd51935f77c2fb87dd110023000000120000000b00020100003304ef04ed9a9a00010011ec04c0c646357c8a8239d01cdeaa5e68f465357656222a732fcb8f60f3051de2b3f51b4c6dd55b610ca8de651a5507040302a950c67f23f50587102f6096889d52a6c2218d4c00336859c0a9e3656c6c63bff3273c37aac28bae54252c73f4149622ad5fbb94dcb065c986a892178956fcc705fab2af006b44ea4fe74a0a42bc736fdb1a869abcb7a92ec7a46393b61f90f84783c21b2b5c1a33bb7de0775627e9ba235466367580f37a552075c3fd027549f18dbd084154e45cda4764ea30c59e8420bcd20fa1973ae5e21d13b7226a118f18fa78f6616e05fa49de7a95dcc78de4a492754a0648a598ed8542646ab81130b035c7afaa2c6245806e40a1c01a8253c428284f1a10ce8456e65ba49f39c678f62de3f1c9b5c3bcfb116a07da46c69267aa904a92c8ab5cda6379593596a97a9226249f0712a6445402e3380bd20ade6a26aed43770364bb2a3113e5bc641195511c1c1cc404a90e11af39127f4d365c6c26cf2710e8f05be2a2c41489394712b19af045dbd329c6ca07ad75834abc02444184819345b09d04ce0ea847c571e5011a09a057d4266b5bfac1ba1a626e19b5e996b4a6c577129bc1ae54742e28bc4b703a20b257284d02785e5b64b095b77b8a51faac728d97efe49a3665629b92053a212351e358ebacb3be0d4966b712e75a50d0ff8b63c96bdd2054901969088b97c2a0177b10b787d29c2c793cb81126f8a7aa0161707b34aac6dfb64f401d0d9941798654b47637c6fac86a0b40434db2f08524433f9bd7703c0ba24246b0343dd882460c3c3a09c784af90db9337f570375480710b8f2404e9981bb42b0faa054204b391f3a399161a0bb4563493361e5d81d5899a1cb9549bcb459951ab681b56c2845af1df93c78bb16b676c56b5517b9516590d02b7dc89c7735cb1948bf203629a760345d2c945ca44a0be153069c8e8a7995f2a5ab8e4aa27994957113ba7ba14d8f35657e8461eb58850277b44686c4efb5ad7508278be08b568912bc1bb0eb27a0cefb979b3633bb669b5d5518d2a062f554a2cb7599eee6a4da7a44aff04411a6c8e3ea9b06a36f60f06f1b85630637be882c17abb2afae0104bfd710eacc4690d041e4c62e23c9c6b8dbcc34a361712c00342c830dc80c99bb2c571c166fd198e3c304bfe04352e4852d9b8e6936115e942099776c4d3a8ca3154b5d6b1caeb3031f632f2cdbbe3ddb2215c31488d0099b3255da003aa9fb77be511fbde6bc4466690539305ef677b056cc1947c5828ac34410c5f01380f7530c46c964cbb73f4d748b8425a3826698ae04174d9c4baaa5802a128a07844889e6cfef541b59f47cfc8247f893bb8a65a3ec468c8604c1c68199235209e80ba52723572cc4b227bc2968348483c6ba3095bcf315597fe5a31164a88fe764dea1621e36c81d598eafa14f58912e32989f90a9c35b64c64bb362ccf397489a6be531b739f4ca53c0c57fc07eafd402f0121abac464c5eb46bb320fb6194b6b3a667e866f66b7882d37b96f720746c3be0cb1353c7ab0a51575754638f014b7aa841a9243ad5a05a0c4c1b0f0583e340499c2605349999eb067203e06c9a04816f2787f64e0c3c8950107f1b71dc607434674ae4bfd86cccadbc705f534626b1bce7683358283382decfe74601340af23f0a2efc52fbdb007068767c5249f9e6facd2d11c8bb747deb47b734a8b9cce185d6c001d0020e34b7905cba69fa4e6f96948878d743b3d6fa967f1fbdda3f043372e04b7bf1c0017000044cd00050003026832002b0007062a2a03040303000a000c000a9a9a11ec001d00170018001b00030200020000001500130000107777772e68636170746368612e636f6d000500050100000000000d0012001004030804040105030805050108060601002d000201010010000e000c02683208687474702f312e31ff010001003a3a000100002900eb00c600c0b8f57aa277c0ccd113cfe97f12b201b7c20613644c49cdb55e803bbfff51bdf92d24f13494bcb4e4ec9af5f3d22848eae3891e460b0d55b502b6936e6f9b83101d3188478771dbd7cc00c3bc1ac815dfddf6f4b8e9a781122bebe91f5bbbbc903d93e5b7a720cd49a52bf1acf1437b835545a7549f28ed95e62910e35e9e32445f6f5897b0a2199d74f85f0f0b3013a7656cf7945a87c5138f141f71f583a21845dd9cd88998c74651d63ff6134fe7e4b226a2481d39c046abcc46ed43a1e4b0b0c0d84d0021208c2b59c00b837765fdd4200df298959dc3c1d94bc96281fcf5d660608a975aa6"
}
]
],
"servers": [[]],
"errors": [[]]
}
}
]
}
RPRX commentedon Apr 18, 2026
@megasoheilsh 首先这需要分包发送,其次第一个 CH 的 TTL 要设短些,只给 GFW 看却到不了 CDN(目前 Xray 还没有相关代码)
montiego commentedon Apr 18, 2026
LjhAUMEM commentedon Apr 18, 2026
想了一下好像也不是 fm 能完成的,伪造 ip 包需要 raw socket,raw socket 需要 root,而 fm 并不负责 dial
off-topic
header-custom 现在污染的没眼看,本来 headerConn 就是用来判断使用独立 conn 还是 one shot serialize,多余判断一层 headerConnMode,
既然打算起独立 conn 就新开个配置倒是Xray-core/transport/internet/finalmask/finalmask.go
Lines 125 to 131 in b465036
赞同,先不说 server hello,
多检查一遍 client hello 不就行了TLS: add TLS spoofing support (XTLS#5964)
TLS: add TLS spoofing support (XTLS#5964)
TLS: add TLS spoofing support (XTLS#5964)
TLS: add multi-shot and Windows WinDivert support (XTLS#5964)
TLS: add TLS spoofing support (XTLS#5964)
farshadl commentedon May 24, 2026
This would be very useful for Iran / other censored regions
therealtunge commentedon May 26, 2026
this could be implemented as "serverless"
the way that people in iran are using TLS spoofing is as a fake server (not a proxy!)
then pointing their VLESS configs at the spoofer
when the spoofer recieves packets, it connects to a big cdn that they cant block (with the spoofed SNI) and forwards the packets
rezakhosh78 commentedon Aug 10, 2026
I have a working PoC that may be useful for this proposal:
https://github.com/rezakhosh78/BlackHole
BlackHole keeps the real Xray TLS/SNI/transport configuration untouched and applies an external DPI-desync layer using
winws2/Zapret2 + WinDivert.The interesting part is that, in testing, simple TLS fragmentation is not always sufficient, while combinations of the following techniques can make a significant difference:
multisplitmultidisordermidsldandsniext+1badseqbadsummd5sigI think this is closely related to #5964 and #5969.
The main feature I would like to propose is not bundling winws2 into Xray, but introducing an optional native
desyncabstraction in Xray-core.For example:
{ "streamSettings": { "sockopt": { "desync": { "mode": "multidisorder", "splitPos": ["1", "sniext+1", "midsld"], "fake": { "sni": "example.org", "fooling": "badseq", "badSeqIncrement": -10000, "repeats": 1 } } } } }Internally, different primitives could use different implementations:
The advantage of implementing this inside Xray is that desync could be attached to a specific outbound/connection, instead of intercepting traffic globally.
It would also allow Xray to keep the current lightweight
fragmentimplementation while exposing more advanced techniques only when explicitly enabled.BlackHole can be used as a practical testbed/reference for configurations that currently require Xray + an external desync engine.
If maintainers are interested in this direction, I can also document which individual primitives/combinations have been effective and map them into a minimal Xray-side API
thatssoheil commentedon Oct 5, 2026
Proposal: a client-side fake-segment
finalmask.tcpmask ("tls spoof")A concrete spec for the narrow piece, following the guidance already given on #6103 ("add as a tcpmask", generic naming, base64 payloads). Happy to implement on current main if there is appetite.
Mechanism (verified against sing-box
common/tlsspoof@2ff3985c)Before the real ClientHello, send one hand-built TCP/IP segment carrying a fake ClientHello for a whitelisted SNI on the same 4-tuple, via raw socket (WinDivert on Windows). The segment is deliberately invalid so the origin server drops it, while a passive SNI filter still reads it:
wrong-sequence(default): seq = snd_nxt - len(fake) -> ends at the peer's rcv_nxt -> old duplicate, discardedwrong-checksum: TCP checksum inverted (raw frames bypass offload)wrong-ack: ack = rcv_nxt - 32767 -> not acceptable -> droppedwrong-md5: zeroed TCP-MD5 option -> dropped per RFC 2385wrong-timestamp: TSval backdated 1h -> PAWS discards (Linux/Windows)snd_nxt/rcv_nxt:
TCP_REPAIR+TCP_QUEUE_SEQ(Linux),pcblist_n(macOS), captured packet (Windows). The fake CH is generated withcrypto/tls, classical curves pinned so it stays under one MSS. Kernel TCP state is never touched; the real handshake proceeds unchanged.Placement
Client-only
finalmask.tcpmask.FinalMask.DialTCPalready wraps the raw conn before TLS/REALITY in tcp/ws/grpc/xhttp/httpupgrade, so the first Write is the real ClientHello: no dialer changes, notlsSettingscoupling, server role no-op.Config sketch
payload= arbitrary fake bytes;sniauto-generates a CH (wins if both set); neither -> no-op. Name left to maintainers.Platforms / privileges
Linux: CAP_NET_RAW + CAP_NET_ADMIN, IPv4/IPv6. macOS: root, no wrong-timestamp. Windows x64/x86: Administrator + WinDivert (ARM64 unsupported). Elsewhere: warn + no-op. Fails open (log + continue) when caps/driver missing - best-effort by design.
Testing
Unit: frame-builder goldens per method x IPv4/IPv6; fake CH under one MSS. Integration (root-gated, tcpdump): fake and real SNI both observed on the wire, server receives the real CH unchanged; full method matrix. Docs under finalmask.
Notes
ttl/countknobs and method choice (plus stacking with the existingfragmentmask) target exactly that, and I can test from inside Iran.