Маскировка VLESS трафика XOR'ом

Не знаю какую выбрать тему.

Ситуация следующая, решил по приколу написать на питоне скрипт который бы XOR’ил трафик VLESS, мол может быть так можно будет замаскировать его трафик чтобы он не определялся DPI.
Написал, поправил конфигурационный файл на клиенте VLESS чтобы он трафик слал на локальный порт на котором висит скрипт.
На начальном этапе писанины я со скрипта запросы слал напрямую на свой сервер на 443 порт на котором висит xray, и все работало.
Удивительные странности начались когда на сервер я закинул по сути такой же скрипт, только с измененными адресами и портом и работать все перестало.
Принципиально (по моему мнению) ниче не изменилось, просто в цепочке передачи трафика появился еще один сервер, пинг проходит, а сайты не грузятся.
Может быть кто-то тут экспериментировал с маскировкой трафика, или просто тема знакомая, подскажите идеи в какую сторону копать.

Скину код сюда потому что он не особо большой вроде как, это код клиента, на сервере код в котором изменены только 2 строки, закоментированные в этом коде.

import asyncio

async def vless_callback(reader, writer):

	client = writer.get_extra_info('peername')

	print(f"[+] {client} connected")

	xray_reader, xray_writer = await asyncio.open_connection('ip', 443)
	
	#на сервере
	#xray_reader, xray_writer = await asyncio.open_connection('127.0.0.1', 443)

	async def client_to_xray():
		while True:
			data = await reader.read(65000)
			if not data: break
			xray_writer.write(data)
			await xray_writer.drain()
			print(f"{client} sent {len(data)} to xray")

	async def xray_to_client():
		while True:
			data = await xray_reader.read(65000)
			if not data: break
			writer.write(data)
			await writer.drain()
			print(f"xray sent {len(data)} to {client}")

	await asyncio.gather(client_to_xray(), xray_to_client(), return_exceptions=True)

	writer.close()
	await writer.wait_closed()
	xray_writer.close()
	await xray_writer.wait_closed()

async def main():
	server = await asyncio.start_server(vless_callback, "127.0.0.1", 9090)

	#на сервере 
	#server = await asyncio.start_server(vless_callback, "0.0.0.0", 9090)
	
	print(server.sockets)
	async with server:
		await server.serve_forever()

if __name__ == "__main__":
    asyncio.run(main())

Изучите как снимать дамп трафика. Снимаете на клиенте, на сервере и сравниваете.

У меня, например, любой TCP не HTTPS-like блокируется в сторону AmazonAWS или M247 Europe.

Навскидку чуть-чуть.

  1. Порт сменили, а firewall?
  2. Что-то пошло не так, asyncio.gather() висит в бесконечном чтении.
  3. А xor-то где, пардон?

а зачем извращаться с XOR? там в VLESS же шифрование завезли в версии Xray 25.9.5.

А если клиенты старые или используется sing-box и хочется чтоточтонеtls-over-TCP, то всегда можно взять старый добрый VMess вместо VLESS. для случаев MitM (например при проксировании через CDN) аналогично.

А зачем это делать, если я могу скриптами выводить все что на них приходит и уходит, и без прокладки в виде самодельного прокси сервера на той же машине на которой запущен xray, все работает.

firewall у меня не работает, порт чего сменили ?

asyncio.gather() висит в бесконечном чтении
так и есть, я с async не знаком вообще, но с висящем бесконечным чтением проходит пинг и проходит трафик если его гнать напрямую в xray
а смысл xor писать если без него ниче не работает)

ну ркн чето там определять научились же, подумал мб xorом можно трафик замаскировать, хз

Только хуже сделаете. РКН гораздо чаще блокирует полностью неопознанные протоколы.

А если и есть такая цель, то как уже сказал выше, старый VMess или VLESS-encryption - уже готовые и проверенные решения.

Чтобы не тратить время форумчан. Почитайте правила, разберитесь самостоятельно и поделитесь результатом.

XOR с фиксированным ключом не даст маскировку от DPI. IMHO ты пытаешь изобрести велосипед с квадратными колесами, shadowsocks с простыми шифрами уже давно блокируют.

Проблема скорее в сетевой конфигурации или MTU. Если фрагментация происходит, то как будешь XOR-ить? У тебя смещение будет в ключевом потоке. Ещё гляньте DF (Don’t Fragment) флаг в IP пакетах. Если он установлен и пакет больше MTU (1500 через интернет), он дропнется. Попробуй уменьшить размер буфера чтения с 65000 до 1400-1450 байт. Path MTU Discovery может ломаться на промежуточных хопах.

А так правильно выше сказали, раз интересно Вам, хотя бы дамп через wireshark посмотри на промежуточном сервере. Проверьте routing там же.

asyncio.gather() с return_exceptions=True будет висеть пока обе корутины не завершатся. У тебя client_to_xray() и xray_to_client() висят в бесконечных циклах while True с await reader.read(). Это нормально для TCP proxy, они ждут данных. Проблема не в этом. Пинг проходит потому что ICMP, а не TCP. Если трафик работает через xray, но не через твой proxy, значит он что-то ломает в handshake или передаче данных.

Да XOR пока что вообще нет, и ничего лучше чем ксорить одним байтом я не придумал, поможет это или нет узнать можно будет только когда соединение в обе стороны заработает и ркн снова что-то ударит в голову.
Буфер я уже уменьшит, судя по вайршарку xray как правило больше 1.5кб данных не отправляет.
А вот про висение корутин интересно, я асинхронный код вообще никогда не писал, просто посмотрел на то сколько клиент открывает соединений и понял что на потоках такое писать не пойдет.
Про пинг не уточнил, я именно про TCP пинг который клиент отправляет на сервер.

Бог с тобой, можешь просто не отвечать в эту ветку и не тратить свое время. Что именно по-твоему я должен увидеть в трафике вайршарком ? Установление TLS соединения и зашифрованный трафик ? И что это даст ?

Какой ещё tls после xorения? Увидишь как пакеты до клиента или сервера не доходят из-за блокировки.

В данный момент ксора в коде нет, и блокировки в данный момент тоже нет, она происходит периодически.
Что я щас вижу из трафика - это tls и обмен application data.
Только когда подключение идет напрямую, они обмениваются данными и затихают, а когда через прокси, то данные ходят так-же как и при подключении напрямую, но после обмена данными клиент снова устанавливает tls и снова отправляет данные, могу 2 файла с трафиком скинуть если хочешь.

xor есть в названии темы.

Я смотреть не буду. Можете ещё отладить трафик на loopback между программами.

На свежую голову мне только что дошло что у тебя deadlock при работе с сокетами.

Например, когда клиент перестает слать данные и посылает FIN, функция client_to_xray выходит из цикла. Однако ты не закрываешь xray_writer. Сервер Xray не получает сигнал о закрытии и продолжает ждать данных. Вторая функция xray_to_client висит на чтении. asyncio.gather ждет завершения обеих функций. В итоге соединение висит в полуоткрытом состоянии пока клиент не отвалится по таймауту. Это вызывает бесконечные попытки TLS handshake.

А проходил TCP-пинг скорее всего из-за того что он делает handshake и сразу рвет соединение. Он не ведет длительный обмен данными и успевает завершиться до возникновения блокировки.

Тебе надо явно закрывать сокет на запись, если чтение из парного сокета вернуло 0 байт или возникла ошибка. И вот уже с таким решением asyncio.gather() должен висеть нормально.

У Вас примерно должно получиться что-то такое:

async def client_to_xray():
        try:
            while True:
                data = await reader.read(1500)
                if not data:
                    break
                xray_writer.write(data)
                await xray_writer.drain()
        except Exception:
            pass
        finally:
            try:
                xray_writer.close()
            except:
                pass

На свежую голову мне только что дошло что у тебя deadlock при работе с сокетами.

Например, когда клиент перестает слать данные и посылает FIN, функция client_to_xray выходит из цикла. Однако ты не закрываешь xray_writer. Сервер Xray не получает сигнал о закрытии и продолжает ждать данных. Вторая функция xray_to_client висит на чтении. asyncio.gather ждет завершения обеих функций. В итоге соединение висит в полуоткрытом состоянии пока клиент не отвалится по таймауту. Это вызывает бесконечные попытки TLS handshake.

А проходил TCP-пинг скорее всего из-за того что он делает handshake и сразу рвет соединение. Он не ведет длительный обмен данными и успевает завершиться до возникновения блокировки.

Тебе надо явно закрывать сокет на запись, если чтение из парного сокета вернуло 0 байт или возникла ошибка. И вот уже с таким решением asyncio.gather() должен висеть нормально.

У Вас примерно должно получиться что-то такое:

async def client_to_xray():

try:

while True:

data = await reader.read(1500)

if not data:

break

xray_writer.write(data)

await xray_writer.drain()

except Exception:

pass

finally:

try:

xray_writer.close()

except:

pass

async def xray_to_client():

try:

while True:

data = await xray_reader.read(1500)

if not data:

break

writer.write(data)

await writer.drain()

except Exception:

pass

finally:

try:

writer.close()

except:

pass

Для линуха готовый xt_XOR, для РКН такой траф станет просто нераспознанным tcp без сигнатур. У меня в сторону одного хостинга на такую обфускацию вешается дефолтный блок 16кб (любые порты любой проткол, включая открытый SSH), но! просто открытый TLS с SNI из белого списка работает. Так что, не везде нераспознанный протокол - решение. Надо проверять сначала, как работает просто tcp без сигнатур явных, погонять несколько сотен кб мусора и если всё ок тогда ксор прокатит в указанном кейсе.

здесь ваще нечего обсуждать.

  1. vless не детектнули
  2. выдать непонятно что вместо трафика - подход ss который уже давно детектнули и блокнули
  3. задача современного прокси не стать непонятно чем а наоборот вписаться в разрешенный трафик - про это сам vless и про это весь xray-core. если чешется можно че то туда контрибьютить в ядро, там хотя бы тима сильная, сперва правда в целом познакомившись с матчастью