finalmask
finalmask ماسکهای finalmask در Xray-core را روی اتصالهایی که یک خروجی باز میکند اعمال میکند.
ماسک بایتهای روی سیم را تغییر میدهد: آنها را تکهتکه میکند، بستهٔ نویز اضافه میکند، هدر میافزاید یا
آنها را در پروتکلی دیگر (DNS، ICMP، جریانی شبیه Minecraft و…) میپیچد. این فیلد finalmask در
فیلدهای اتصال (dial) است؛ پس روی هر خروجی که اتصال میسازد (و روی سرورهای DNS و جاهای دیگری که
فیلدهای اتصال را میپذیرند) قابل تنظیم است. JSON آن همان streamSettings.finalmask در Xray است.
چرا از آن استفاده کنیم
- برای استفاده از سرور Xray که با ماسکهای finalmask راهاندازی شده است: کلاینت باید همان ماسکها را اعمال کند، وگرنه سرور ترافیک را نمیفهمد.
- برای تغییر شکل اتصال روی سیم بدون تغییر پروتکل: تکهتکه کردن ClientHello در TLS (
fragment)، فرستادن بستههای UDP بیمعنا پیش از دستدهی WireGuard یا QUIC (noise)، یا مبهمسازی UDP با رمز (salamander).
چطور از فیلترینگ عبور میکند
فیلترها روی اولین بایتها و بستههای اتصال، اندازه و زمانبندی آنها تطبیق میدهند و ماسکها دقیقاً همین را
تغییر میدهند. fragment ClientHello را به تکههای کوچک میبرد تا فیلتری که آنها را سرهم نمیکند
نتواند SNI را بخواند. noise اول بستههای تصادفی میفرستد تا جریان با دستدهی واقعی شروع نشود.
ماسکهای هدر و پوششی ترافیک را شبیه پروتکلی دیگر، یا شبیه بایتهای تصادفی، میکنند.
ماسکهایی که فقط اندازه و زمان را تغییر میدهند (fragment، merge، oob، mirage، noise) با هر سروری
کار میکنند. ماسکهایی که محتوا را تغییر میدهند (هدرها، salamander، sudoku، xdns، xicmp،
xmc، mkcp-legacy، realm) همان ماسک را روی سرور لازم دارند، یعنی سرور Xray که برای آن تنظیم شده
باشد. udphop سروری میخواهد که روی همهٔ پورتهایی که به آنها میپرد گوش بدهد. وقتی فیلتر IP سرور را
مسدود کرده باشد ماسک کمکی نمیکند، جز xdns: از راه resolverهای عمومی DNS به سرور میرسد و هرگز به
نشانی خود سرور وصل نمیشود.
چطور کار میکند
- ماسکهای
tcpهر اتصال TCP خروجی را میپوشانند و ماسکهایudpهر سوکت UDP و اتصال UDP متصل را. - ماسکها به ترتیب فهرست اجرا میشوند: اولین ماسک بایتهایی را که خروجی مینویسد میگیرد و خروجی آخرین ماسک روی سیم میرود. پاسخها به ترتیب برعکس از آنها میگذرند.
- ماسکها زیر پروتکل و لایهٔ TLS آن قرار دارند، پس دقیقاً همان چیزی را میبینند که روی سیم میرود. با
detour، اتصالی را میپوشانند که از طریق خروجی detour ساخته شده است. - اتصال، مثل Xray، از درون ماسکها برقرار میشود. ماسکی که سوکتهای خودش را باز میکند (
xdnsبه resolverهایش،udphopبه هر پورت تازه) آنها را با dialer خروجی باز میکند؛ پس همانdetour، اینترفیس و routing mark اتصال را میگیرند.xdns،xicmpوudphopجای سوکتِ رو به سرور را میگیرند و باید آخرین ماسکudpباشند. - فقط سمت کلاینت پیاده شده است: ورودیها
finalmaskنمیگیرند. - ماسکها همان کد Xray-core هستند (از Xray-core v26.10.31)، جز
mergeوoobکه از Xray-core نسخهٔ patterniha آمدهاند؛ رفتار و تنظیمات آنها از مستنداتfinalmaskدر Xray پیروی میکند. ماسکfilterدر Xray که فقط برای ورودی است در دسترس نیست.
پیکربندی
یک خروجی VLESS که ClientHello آن تکهتکه میشود، و یک endpoint از نوع WireGuard که پیش از هر دستدهی نویز میفرستد:
{
"outbounds": [
{
"type": "vless",
"server": "example.com",
"server_port": 443,
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
"tls": { "enabled": true, "server_name": "example.com" },
"finalmask": {
"tcp": [
{ "type": "fragment", "settings": { "packets": "tlshello", "length": "100-200", "delay": "10-20" } }
]
}
}
],
"endpoints": [
{
"type": "wireguard",
"tag": "wg",
"address": ["10.0.0.2/32"],
"private_key": "...",
"peers": [{ "address": "203.0.113.1", "port": 51820, "public_key": "...", "allowed_ips": ["0.0.0.0/0"] }],
"finalmask": {
"udp": [
{ "type": "noise", "settings": { "noise": [{ "rand": "10-50", "delay": "5-10" }] } }
]
}
}
]
}
| فیلد | نوع | پیشفرض | معنا |
|---|---|---|---|
finalmask.tcp |
array | — | ماسکهای اتصالهای TCP، به ترتیب. |
finalmask.udp |
array | — | ماسکهای UDP، به ترتیب. |
finalmask.quicParams |
object | — | پذیرفته میشود تا کانفیگهای Xray بارگذاری شوند؛ استفاده نمیشود. پروتکلهای QUIC در sing-box گزینههای خودشان را دارند. |
…[].type |
string | — | نوع ماسک (پایین را ببینید). حروف کوچک و بزرگ فرقی ندارد. |
…[].settings |
object | {} |
تنظیمات ماسک، دقیقاً مثل Xray. |
نوعهای پشتیبانیشده:
| نوع | tcp |
udp |
کار | تنظیمات اصلی |
|---|---|---|---|---|
fragment |
بله | — | اولین نوشتنها (یا ClientHello در TLS) را با تأخیر به تکههای کوچک میبرد. | پایین را ببینید |
mirage |
بله | — | ClientHello در TLS را به دو رکورد TLS میشکند و آنها را جداگانه یا در یک نوشتن میفرستد تا فیلتر نتواند نام سرور را تطبیق دهد. کتابخانهٔ Mirage ساختهٔ PoriyaVali. | offset، layout |
header-custom |
بله | بله | بایتهای دلخواهی که سرور انتظار دارد (هدر پروتکل جعلی) اضافه میکند. | TCP: clients، servers، errors؛ UDP: mode، client، server |
sudoku |
بله | بله | مبهمسازی Sudoku با کلید رمز. | password، ascii، customTable، customTables، paddingMin، paddingMax |
merge |
بله | — | نوشتنهای پشت سر هم را نگه میدارد و یکجا میفرستد، برای اولین نوشتنهای اتصال. از Xray-core نسخهٔ patterniha. | پایین را ببینید |
oob |
بله | — | هر یک از اولین نوشتنها را با یک بایت اضافه بهصورت دادهٔ فوری (out-of-band) TCP میفرستد که سرور آن را کنار میگذارد و فیلتر ممکن است اشتباه بخواند. همان dialMode با شکل oob.<bytes> در Xray (نسخهٔ patterniha). |
پایین را ببینید |
xmc |
بله | — | جریان را شبیه اتصال Minecraft میکند. | hostname، profiles، password |
noise |
— | بله | پیش از بستههای واقعی، بستههای تصادفی یا ثابت میفرستد؛ بستهٔ با type برابر exp از یک عبارت ساخته میشود. |
noise (فهرستی از rand، randRange، type، packet، delay)، reset، script |
salamander |
— | بله | مبهمسازی Salamander در Hysteria2؛ با packetSize گونهٔ Gecko که اندازهٔ بستهها را هم تغییر میدهد. |
password، packetSize (حداکثر ۲۰۴۸) |
mkcp-legacy |
— | بله | هدرهای قدیمی بستههای mKCP (dns، dtls، srtp، utp، wechat، wireguard) یا لایهٔ AES-GCM آن. |
header، value |
xdns |
— | بله | UDP را در درخواستهای DNS به دامنهٔ سرور حمل میکند، از راه resolverهای DNS روی UDP، TCP یا DoH. | پایین را ببینید |
xicmp |
— | بله | UDP را در بستههای ICMP echo حمل میکند. | ips، dgram |
realm |
— | بله | از راه NAT hole punching با سرورهای STUN و یک نشانی rendezvous به طرف مقابل وصل میشود. | url (realm:// یا realm+http://)، stunServers، tlsConfig |
udphop |
— | بله | پرش پورت: جریان UDP را هر چند ثانیه، یا برای هر اتصال، به پورت دیگری از سرور و سوکت محلی دیگری میبرد. | پایین را ببینید |
نوع ناشناخته، نوعی که فقط TCP است در udp (یا برعکس)، یا تنظیمات نادرست، خطای پیکربندی میدهد که
نام ماسک را میگوید، مثل finalmask.tcp[0] fragment.
تنظیمات fragment:
| فیلد | نوع | پیشفرض | معنا |
|---|---|---|---|
packets |
string | — | tlshello ClientHello را در TLS تکه میکند؛ بازهای مثل "1-3" همان نوشتنهای اتصال را تکه میکند. |
length |
range | — | اندازهٔ هر تکه به بایت. |
delay |
range | 0 |
مکث میان تکهها به میلیثانیه. |
lengths |
range list | — | یک طول برای هر تکه، به جای length. |
delays |
range list | — | یک تأخیر برای هر تکه، به جای delay. |
maxSplit |
range | — | بیشترین تعداد تکهها. |
بازه به شکل "from-to" یا یک عدد است؛ فهرست بازه آرایهای از آنها، یا تنها یکی است.
noise با "type": "exp"، packet را عبارتی از تکهها میگیرد: <b 0x1700> بایتهای ثابت به
هگز، <r 10-20> بایتهای تصادفی، <rc 8> حروف تصادفی، <rd 4> رقمهای تصادفی، <t> مُهر زمان،
<c> شمارنده و <n> یک nonce. script مسیر یک فایل محلی .lua است که به جای noise بستهها را
میسازد (Xray فعلاً اسکریپت درونخطی را خاموش کرده است).
تنظیمات xdns برای کلاینت:
| فیلد | نوع | پیشفرض | معنا |
|---|---|---|---|
domains[].names |
فهرست رشته | — | دامنههای سرور؛ درخواستها به زیردامنههای آنها میروند. الزامی. |
domains[].types |
فهرست عدد | [16] |
نوع رکوردهای DNS که پرسیده میشوند: ۱ (A)، ۵ (CNAME)، ۱۶ (TXT)، ۲۸ (AAAA). |
domains[].edns0 |
number | 0 |
اندازهٔ UDP که با EDNS0 اعلام میشود. همین مقدار روی سرور میگذارد پاسخ بیشتر جا بگیرد: بدون آن هر پاسخ حدود ۵۰۰ بایت جا دارد که برای تکههای mKCP کم است. |
domains[].lenLimit، labelLimit |
number | 255، 63 |
بلندترین نام درخواست و برچسب. |
resolvers[].addrs |
فهرست رشته | — | resolverها: 8.8.8.8 یا udp://8.8.8.8:53، tcp://9.9.9.9:53، https://1.1.1.1/dns-query. الزامی. |
resolvers[].worker |
number | 32 |
درخواستهای همزمان برای هر resolver. |
resolvers[].tlsConfig |
object | — | TLS یک resolver از نوع DoH، مثل tlsSettings در Xray: serverName، alpn (["h3"] برای DoH روی QUIC)، fingerprint، pinnedPeerCertSha256، verifyPeerCertByName، echConfigList و مانند آن. برای https:// الزامی است. |
resolvers[].quicConfig |
object | — | تنظیمات QUIC برای DoH روی h3، مثل quicParams در Xray. |
extraPoll |
number | 0 |
درخواستهای خالی اضافه برای هر نوشتن، ۰ تا ۳، تا پاسخها زودتر برگردند. |
تنظیمات نسخههای قدیمیتر Xray هنوز بارگذاری میشوند: "resolvers": ["t.example.com+udp://8.8.8.8:53"]
به دامنهٔ t.example.com با resolver udp://8.8.8.8:53 تبدیل میشود، و {"name": ...} و
{"type": ..., "addr": ...} به names و addrs.
تنظیمات udphop:
| فیلد | نوع | پیشفرض | معنا |
|---|---|---|---|
mode |
string | — | با کاما جدا: intervalLocal سوکت محلی را، intervalRemote پورت سرور را در فاصلههای زمانی عوض میکند، perConnRemote برای هر اتصال یک پورت سرور برمیگزیند. الزامی. |
interval |
range | 30 |
ثانیههای میان پرشها، دستکم ۵. |
remotePorts |
فهرست پورت | — | پورتهای سرور برای پرش: "20000-20100,443". |
remoteIPs |
فهرست رشته | — | نشانیها یا پیشوندهای سرور که میانشان هم پرش میشود. |
تنظیمات merge:
| فیلد | نوع | پیشفرض | معنا |
|---|---|---|---|
times |
number | 0 |
چند نوشتن ادغامشده فرستاده شود؛ پس از آن نوشتنها مستقیم میروند. 0 یا کمتر یعنی در تمام عمر اتصال. |
delayMs |
number | 30 |
نوشتن نگهداشتهشده وقتی میرود که این چند میلیثانیه نوشتن تازهای نیاید. |
maxBytes |
number | 16384 |
بایتهای نگهداشتهشده با رسیدن به این اندازه فوراً فرستاده میشوند. |
تنظیمات oob:
| فیلد | نوع | پیشفرض | معنا |
|---|---|---|---|
bytes |
فهرست عدد | — | یک بایت (۰ تا ۲۵۵) برای هر نوشتن: نوشتن i با bytes[i] بهصورت دادهٔ فوری فرستاده میشود. الزامی. |
تنظیمات mirage:
| فیلد | نوع | پیشفرض | معنی |
|---|---|---|---|
offset |
عدد | 5 |
اندازهٔ بایتهای handshake در رکورد اول (۱ تا ۱۶۳۸۳). اندازهٔ امن به شبکه بستگی دارد: در ایران در ۲۰۲۶ اندازههای ۱، ۲، ۴ و ۵ عبور کردند و ۳ و ۶ به بالا رد شدند. |
layout |
رشته | separate |
separate هر رکورد را جداگانه مینویسد (آنچه همراه اول و ایرانسل در سپتامبر ۲۰۲۶ عبور دادند)؛ single هر دو را در یک نوشتن میفرستد (آنچه در اوت ۲۰۲۶ در ایران عبور کرد). |
mirage فقط اولین نوشتن را تغییر میدهد، آن هم وقتی یک ClientHello کامل با نام سرور باشد؛ هر
نوشتن اول دیگری بدون تغییر میرود. آن را آخرِ tcp بگذارید، یا بعد از ماسکهایی که ClientHello ساده
لازم دارند. offset نامناسب میتواند باعث شود فیلتر کل اتصال را رد کند، حتی برای نام مجاز.
oob روی سوکت کار میکند، پس آن را آخرِ tcp بگذارید؛ dialMode=oob.0,32 در لینک اشتراکگذاری
پس از ماسکهای دیگر لینک به {"type": "oob", "settings": {"bytes": [0, 32]}} تبدیل میشود. به
سوکت واقعی TCP نیاز دارد: از راه detour کاری نمیکند.
گزینههای مرتبط: tls_fragment خروجی بهعنوان اولین ماسک fragment اضافه
میشود. tls_tricks خود ClientHello را تغییر میدهد و ماسک نیست.
tls_fragment را نمیتوان با tcp_fast_open به کار برد.