Здарова, братцы.
Я так вдохновился вашими положительными отзывами на
предыдущую статью, что решил поделиться ещё одной.
Предисловие.
В ноябре прошлого года у меня была одна удалённая работёнка, в которой мне потребовался ZennoPoster. Задача состояла в продвижении криптопроекта на ресурсах, где может находиться целевая аудитория. Одним из таких ресурсов был Discord, автоматизацию которого я стал реализовывать на стандартных кубиках. По сути с этого момента и началось моё изучение ZP.
Прошло несколько месяцев и от работы не осталось и следа, зато остался ZennoPoster с уже написанным Discord. Нужен был новый источник дохода, и после нескольких профитных тестов я решил реализовать регистратор и рассыльщик на запросах, дабы запустить больше потоков на паре моих ПК оставшихся после майнинга. Спустя некоторое время задача была реализована и трафик полился, а с ним и профит, но затем Discord стал запрашивать авторизацию, тут и началось моё изучение Websocket.
WS в основном используется на ресурсах, где необходим активный обмен данными. Это могут быть мессенджеры, онлайн-игры, форексы и различные приложения, где важно обмениваться большим объёмом данных в режиме реального времени.
Клиент (браузер или приложение) с помощью WS подключается к серверу только один раз и внутри этого подключения обменивается данными.
За счет этого уменьшается объём передаваемого трафика, и тем самым WS имеет огромное преимущество перед обычными POST/GET запросами, где нужно постоянно устанавливать новые подключения и передавать заголовки.
По сути WS похож на обычный POST запрос, но чтобы его выполнить на ZP нам понадобится библиотека. В моём случае это была
websocket-sharp.
О том как ей пользоваться я и опишу в этой статье.
Sniffаем.
Проанализировать трафик WS можно разными способами, но самый простой и доступный это использование браузера на базе Chromium. В моём случае это SWIron.
- Заходим на discordapp.com
- Открываем DevTools нажатием на F12
- Переходим на вкладку Network -> WS -> Frames
- Регистрируем новый аккаунт и заходим
Должна отобразиться следующая картина:
Посмотреть вложение 45838
Разберёмся что здесь к чему.
В колонке Name при наведении отобразится ссылка для запроса, её можно скопировать к себе нажав на пр. кн. мыши и соответствующее меню: wss://gateway.discord.gg/?encoding=json&v=6&compress=zlib-stream
В колонке Data можно увидеть
исходящие и
поступающие запросы. Копируем себе первый исходящий запрос (это запрос, который мы должны отправить серверу для авторизации):
{"op":2,"d":{"token":"NjMwMTU2ODYzNDcxNjE2MDMw.Xe9SFw.GLgMR8R7vQPnHOmaD5KmHqU4vmk","properties":{"os":"Windows","browser":"Chrome","device":"","browser_user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3650.0 Iron Safari/537.36","browser_version":"","os_version":"10","referrer":"","referring_domain":"","referrer_current":"","referring_domain_current":"","release_channel":"stable","client_build_number":50980,"client_event_source":null},"presence":{"status":"online","since":0,"activities":[],"afk":false},"compress":false}}
Теперь мы можем проверить будет ли работать наш WS на отдельном клиенте. Для этого можно использовать расширение
Simple WebSocket Client.
- Прописываем URL
- Открываем Websocket нажатием на Open (в логах должно появиться первое сообщение с heartbeat_interval)
- Вставляем тело исходящего запроса в поле Request и нажимаем Send. В логах мы получим ответ, что всё прошло успешно.
Посмотреть вложение 45856
Супер!
Всё работает как надо, дело осталось за малым - реализовать тоже самое на ZennoPoster.
Realизуем.
Для начала нам нужен сам websocket-sharp.dll.
Качаем и вытаскиваем его с помощью WinRAR. Закидываем библиотеку в папку ExternalAssemblies. Добавляем его в наш проект с помощью блока "Ссылки из GAC" и прописываем using:
using WebSocketSharp;
using WebSocketSharp.Server;
using WebSocketSharp.Net;
Сразу закинем тело запроса в переменную wss_data.
Посмотреть вложение 45971
Кстати, в тестовом шаблоне я использовал тело запроса от десктопной версии, поэтому просто можете закинуть свой токен в переменную для теста.
(build number - версия клиента)
Если взглянуть на трафик в браузере, то можно заметить, что сокет не закрывается и принимает/отправляет периодично новые запросы, поэтому в коде мы должны будем использовать асинхронный метод, чтобы сам шаблон выполнялся, а сокет работал в фоновом режиме и не закрывался, пока мы его об этом не попросим.
using System.Threading.Tasks;
В общем коде в классе CommonCode пишем наш код:
public static void Wss(IZennoPosterProjectModel project) //определяем метод Wss
{
Random rnd = new Random(); //инициализация Random
string data = project.Variables["wss_data"].Value; //загружаем тело запроса из переменной
//подключаем прокси, если необходимо
//string ip = project.Variables["proxy_ip"].Value;
//string login = project.Variables["proxy_login"].Value;
//string pass = project.Variables["proxy_pass"].Value;
using (var ws = new WebSocket("wss://gateway.discord.gg/")) //определяем ссылку запроса
{
//ws.SetProxy (ip, login, pass); //устанавливаем прокси, если необходимо
//ws.SetCookie (new Cookie("__cfduid", project.Variables["cookies_value"].Value)); //устанавливаем куки, если необходимо
ws.Origin = "https://discordapp.com";
ws.OnMessage += (sender, e) => //объявляем, что будем получать сообщение
project.SendInfoToLog(e.Data); //пишем инфу в лог для наглядности
ws.Connect(); //подключаемся по wss
ws.Send (data); //отправляем тело запроса
System.Threading.Thread.Sleep(1000); //небольшая пауза
ws.Send ("{\"op\":4,\"d\":{\"guild_id\":null,\"channel_id\":null,\"self_mute\":true,\"self_deaf\":true,\"self_video\":false}}"); //отправляем тело второго запроса
int q = 0;
while (Convert.ToBoolean(project.Variables["wss_lock"].Value)) //пока переменная = true, цикл будет выполняться
{
Thread.Sleep(rnd.Next(40000, 43000));
q = q + rnd.Next(4, 40);
ws.Send ("{\"op\":1,\"d\":" + q + "}"); //отправляем тело запроса
};
}
}
public static async Task WssAsync(IZennoPosterProjectModel project) //определяем асинхронный метод
{
await Task.Run(() => Wss(project)); //запуск метода Wss
}
Если упрощенно, то сначала вы пишете в методе Wss код, который вы хотите выполнить, а потом в WssAsync указываете, что Wss нужно запустить асинхронно.
Вызвать асинхронный Wss можно с помощью C# кубика.
CommonCode.WssAsync(project); //запускаем Wss асинхронно
После того как мы присвоим переменной wss_lock значение false через кубик "Обработка переменных", код успешно завершится и websocket закроется. А в Discord ваш аккаунт уйдёт в оффлайн.
Также завершить поток можно другими интересными и, возможно, более "правильными" способами, но я не сильно углублялся в эту тему.
Запускаем шаблон и проверяем лог в ProjectMaker.
Посмотреть вложение 46069
Вот и всё, наш Websocket готов!
Видео
Summary
В конечном итоге всё оказалось не так уж и сложно, но пришлось разбираться и потратить какое-то время. После чего я успешно возобновил трафик, а позже реализовал рассыльщик по чату в Reddit, там также используется wss. Вообще Websocket часто используют в авторизациях и мессенджерах, поэтому, думаю, эти знания вам пригодятся.
P.S. Асинхронный WSS гарантированно работает в версии 5.28. Если нужно заменить какие-то заголовки, то можно подредактировать некоторые файлы с
Github (например User-Agent) и скомпилировать в VS измененный websocket-sharp.dll.