Форум   Статьи   Новости   Файлы   Bugtraq   Сниффер   Друзья   О Клубе

Последнее на Форуме

Пожертвования

Liberty Reserve: U9999024
Кошельки WebMoney:
Z583322939655
E121331800314
R274644017049
U349709454906


YM: 410011220120073

Пожертвовать

Контакты

Связь с Администрацией

hpcteam1[@]gmail.com

Статьи rss

[ Добавить Статью на сайт ]

Статьи / Программирование / C/C++, C#

Создание спам-ботов на С#

Для начала, я хотел бы пояснить, почему я выбрал С#, учитывая, что скриптовые языки намного более удобны для парсинга и автоматизации действий.

Плюсы использования С#:

  • Удобство для конечного пользователя. Если вы работаете на фриланс-биржах, вы вероятно уже сталкивались с заказчиками, которые едва умеют запустить *.exe файл или открыть произвольный сайт в браузере. Естественно, скомпилированная программа будет преимуществом перед скриптом, для которого необходимо будет настроить среду.
  • Комфортные инструменты .NET для работы с эмуляцией браузера. Запросы, конечно, намного более изящное решение, однако если в вашем случае скорость разработки играет значительную роль, эмуляция браузера – это удачный выбор.
  • Если вы только начинаете переходить в веб из разработки прикладных десктопных приложений, разработка на С# будет для вас намного более комфортным решением.

Надеюсь, я смог убедить вас в актуальности использования C# .NET. Переходим к практике.

Базовые методы


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

В качестве примера выберем форум KidsLife. Нашей целью будет написание функций логина/логаута и отправки сообщения в заданную ветку заданной темы.

Регистрируем новый аккаунт на форуме, он понадобится нам для тестирования. Получаем данные для входа на форум:

Login: little_proger
Password: qwertyuiop

Создаём новый проект Windows Forms. Бросаем туда три textbox`а для логина, пароля и ссылки на тему, которую мы планируем комментировать, а также один textbox multiline для комментария. Также бросаем кнопку «подтвердить».

Рассмотрим основные возможности С# для парсинга и работы с интернетом.

Во-первых, можно использовать HttpRequest/HttpResponse из пространства имён System.Web. В данной статье этот способ рассматриваться не будет ввиду почти полного отсутствия различий со скриптовыми языками.

Во-вторых, это использование класса WebBrowser из пространства имён System.Windows.Controls. Мы остановимся на нём.

Объект класса WebBrowser – это, по сути, копия Internet Explorer без графического отображения. Следовательно, мы можем программно вызывать все необходимые функции IE, а также получать веб-страницу в виде объекта класса HtmlDocument, для того, чтобы парсить её и получать информацию.

Вернемся к нашему проекту. Первой функцией, которую мы реализуем будет функция входа в аккаунт. Заходим в обычный браузер, переходим по ссылке «Вход», получаем ссылку на страницу входа:

http://kidslife.ru/forum/ucp.php?mode=login

Поставим обработчик события кнопки. В теле обработчика для начала создаём экземпляр класса WebBrowser.

WebBrowser wb = new WebBrowser();

Для перехода на страничку в классе WebBrowser есть метод Navigate, который принимает строку со ссылкой и переходит по ней. Перейдем на страницу комментирования.

wb.Navigate("http://kidslife.ru/forum/ucp.php?mode=login");

Данный метод создаёт отдельный поток, в котором запускает загрузку странички по указанному URL. Как только страница загрузится, мы сможем обратиться к свойству Document объекта WebBrowser.

Важный момент: по той причине, что метод выполняется в отдельном потоке, мы должны дождаться полной загрузки страницы перед обращением к свойству Document. Проверку на завершенность загрузки мы можем выполнить, обратившись к свойству ReadyState объекта WebBrowser.

while (wb.ReadyState != WebBrowserReadyState.Complete)
            Application.DoEvents();

Данный код приостанавливает выполнение текущего потока, до момента полной загрузки страницы. Метод DoEvents() обрабатывает сообщения, которые в данный момент находятся в очереди. Таким образом, остальные функции нашей программы будут работать корректно, в тот момент, когда страница загружается.

Теперь рассмотрим работу со свойством Document. Document является экземпляром класса HtmlDocument, после загрузки страницы Document содержит в себе её исходный код. Для того, чтобы произвести авторизацию, нам необходимо найти соответствующие полям ввода элементы в этом объекте, заполнить их и имитировать нажатие на кнопку подтверждения.

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

<input class="post" type="text" name="username" size="25" value="" tabindex="1" />

<input class="post" type="password" name="password" size="25" tabindex="2" />

<input type="submit" name="login" class="btnmain" value="Вход" tabindex="5" />

Убедившись в том, что у каждого из этих полей атрибут name уникален, мы можем свести задачу к перебору всех тегов <input> и заполнению тех, которые нам подходят.

Рассмотрим следующий код:

foreach (HtmlElement he in wb.Document.GetElementsByTagName("INPUT"))
{
      if (he.GetAttribute("name") == "username")
             he.InnerText = tbLogin.Text;
      if (he.GetAttribute("name") == "password")
             he.InnerText = tbPassword.Text;
}

foreach (HtmlElement he in wb.Document.GetElementsByTagName("INPUT"))
     if (he.GetAttribute("name") == "login")
             he.InvokeMember("click");

В первом цикле мы перебираем все теги <input> (обратите внимание, в методе GetElementsByTagName аргумент указывается заглавными буквами), проверяем атрибут name и, при совпадении его с соответствующим у поля логина/пароля, заполняем его.

Второй цикл перебирает те же теги, находит кнопку «Вход» и производит клик по ней.

Почему мы разделили этот цикл на две части? Ответ прост – тег, содержащий в себе кнопку входа мог быть перечислен перед полями ввода и тогда попытка входа была бы провалена.

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

Функция авторизации написана. Перейдем к непосредственно комментированию. Для этого напишем отдельную функцию, которая будет принимать следующие аргументы:

  • Ссылку на объект WebBrowser, в котором уже произведена авторизация на сайт.
  • Объект типа string, который содержит ссылку на тему, в которой мы планируем оставить комментарий.
  • Объект типа string, который содержит текст комментария.

private void postComment(WebBrowser authWB, string lnk, string comment)

Для начала рассмотрим структуру на сайте. Выберем произвольную тему. Посмотрим на её ссылку:

http://kidslife.ru/forum/viewtopic.php?f=71&t=22

В конце странички есть кнопка «Ответить», которая ведет на страницу, которая нам и нужна. Её ссылка выглядит следующим образом:

http://kidslife.ru/forum/posting.php?mode=reply&f=71&t=22

Мы можем заметить, что из первой ссылки можно сформировать вторую, заменив подстроку “viewtopic.php?” на “?posting.php?mode=reply&”. Формируем нужную нам ссылку и переходим по ней.

lnk = lnk.Replace("viewtopic.php?", "?posting.php?mode=reply&");
            authWB.Navigate(lnk);

while (authWB.ReadyState != WebBrowserReadyState.Complete)
            Application.DoEvents();

Опять исследуем исходный код полученной странице в браузере, ищем поле ввода сообщения и кнопку «Отправить».

<textarea name="message" rows="15" cols="76" tabindex="3" onselect="storeCaret(this);" onclick="storeCaret(this);" onkeyup="storeCaret(this);" onfocus="initInsertions();" style="width: 700px; height: 270px; min-width: 98%; max-width: 98%;"></textarea>

<input class="btnmain" type="submit" accesskey="s" tabindex="11" name="post" value="Отправить" />

Заполняем поле комментария и нажимаем на кнопку «Отправить».

foreach (HtmlElement he in authWB.Document.GetElementsByTagName("TEXTAREA"))
           if (he.GetAttribute("name") == "message")
                   he.InnerText = comment;

foreach (HtmlElement he in authWB.Document.GetElementsByTagName("INPUT"))
           if (he.GetAttribute("name") == "post")
                   he.InvokeMember("click");

while (authWB.ReadyState != WebBrowserReadyState.Complete)
           Application.DoEvents();

Функция отправки комментария готова. Вызовем её в обработчике клика по кнопке после авторизации.

postComment(wb, tbLink.Text, tbComment.Text);

Мы можем проверить программу и увидеть, что она работает. Тем не менее, нам необходима ещё одна функция. Это функция выхода из аккаунта. При автоматизации комментирования для нескольких аккаунтов, мы столкнёмся с невозможностью авторизации в тот момент, когда предыдущий аккаунт уже авторизован. Перейдем на главную страницу форума.

http://kidslife.ru/forum/

В блоке «Личное меню» мы видим ссылку на выход из аккаунта. Нам остаётся только перейти по ней в программе. Анализируем исходный код страницы. Видим следующую строчку:

<a href="./ucp.php?mode=logout&amp;sid=9dd6e12ee0d8c4959ebfba5641f494ec">Выход [ little_proger ]</a>

Атрибут sid содержит в себе некий хэш, судя по всему хэш сессии. Остальная строка всегда будет одинаковой. Следовательно, мы можем искать среди тегов <a> такой, атрибут href которого начинается с определенной строки, а потом переходить по значению этого атрибута. Реализуем выход из аккаунта.

wb.Navigate("http://kidslife.ru/forum/");

while (wb.ReadyState != WebBrowserReadyState.Complete)
   Application.DoEvents();

foreach(HtmlElement he in wb.Document.GetElementsByTagName("A"))
   if (he.GetAttribute("href").StartsWith("./ucp.php?mode=logout&amp;sid="))
           {
                wb.Navigate(he.GetAttribute("href"));

                while (wb.ReadyState != WebBrowserReadyState.Complete)
                     Application.DoEvents();
 
                break;
           }

Программа закончена. Мы ознакомились с основными принципами автоматизации спама на языке C#. Теперь, рассмотрим вопросы, которые могут возникнуть при использовании знаний, полученных в этом блоке.

В: При использовании метода GetAttribute для атрибута class ничего не работает. Как решить эту проблему?
О: Имя class зарезервировано для внутренних нужд системы. Для того, чтобы получить значение атрибута class можно использовать GetAttribute(“classname”);

В: Цикл проверки состояния загрузки страницы был завершен, но страница не подгрузилась. Что делать?
О: Иногда, при медленном интернете, или на нагруженных flash-контентом сайтах, действительно возникает такая проблема. Решением может стать небольшая задержка после цикла.

while (wb.ReadyState != WebBrowserReadyState.Complete)
        Application.DoEvents();
        System.Threading.Thread.Sleep(500);

В: Работает ли WebBrowser с другими протоколами, кроме http?
О: WebBrowser – это, по сути, копия internet explorer. Следовательно, мы можем переходить по ссылкам через все протоколы, которые поддерживает IE, такими как https, ftp и проч. Работа с https никак не отличается от обычной работы, мы взаимодейтвуем с сетью на более высоком уровне.

В: Можно ли использовать многопоточность в приложениях?
О: Косвенно, в тестовых приложениях, многопоточность реализуема, однако

На остальные вопросы, которые у вас возникнут я постараюсь ответить в комментариях.

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

Использование proxy


Естественно, при работе с множеством аккаунтов, нам понадобится использовать прокси для того, чтобы сделать видимость работы группы компьютеров с разными адресами. В этом блоке я не буду освещать вопросы выбора, проверки и покупки прокси (в современных реалиях, это тема для отдельной статьи, а возможно и для цикла статей). Заметим, что нам нужны HTTP/HTTPS прокси.

Ввиду того, что WebBrowser является экземпляром IE, очевидно, что для подмены прокси нужно как-то влиять на настройки IE. Наиболее легким способом является изменение записи в системном реестре. Рассмотрим методы редактирования системного реестра, которые существуют в C#.

Управление реестром можно реализовать посредством классов пространства имён Microsoft.Win32. Всё, что нам необходимо - это установить два значения:

  • Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ProxyEnable - 0 или 1, определяет включен ли прокси сервер. По умолчанию установлен 0.
  • Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ProxyServer - строковое значение, определяет адрес и порт прокси сервера.

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

Для начала, подключаем нужное пространство имён. После этого можно приступить к написанию функции.

private void changeProxy(string proxy)
{
            string key = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
            RegistryKey RegKey = Registry.CurrentUser.OpenSubKey(key, true);
            RegKey.SetValue("ProxyServer", proxy);
            RegKey.SetValue("ProxyEnable", 1);
            RegKey.Close();
}

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

Добавим следующий код перед объявлением функций:

[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37;

Здесь мы объявляем необходимые константы для работы с библиотекой, а также функцию установки интернет-опций.

  • INTERNET_OPTION_SETTINGS_CHANGED - оповещает систему о том, что в реестре произошли изменения.
  • INTERNET_OPTION_REFRESH - указывает, что опции реестра требуется перегрузить.

Теперь в функцию смены прокси необходимо добавить следующие строки:

InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);

Эти строки обновляют измененные поля в реестре и перегружают настройки IE (и, соответственно, WebBrowser) не требуя перегрузки приложения.

Функция смены прокси написана. Теперь по аналогии напишем функцию отключения прокси.

private void disableProxy()
{
     string key = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
     RegistryKey RegKey = Registry.CurrentUser.OpenSubKey(key, true);
     RegKey.SetValue("ProxyEnable", 0);
     RegKey.Close();
  
     InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
     InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}

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

Стоит отметить, что такой способ работает с любыми видами прокси: HTTP, HTTPS, FTP, SOCKS4,5 и прочие.

Подмена user-agent


Очень часто, при создании парсеров и спам-ботов, возникает потребность работы с мобильными версиями, ввиду легкости их структуры и отсутствия нагромождения скриптов и flash-контента который тормозит работу. Естественно, владельцы сайтов пытаются бороться с таким "коридором" для спаммеров. Самым частым способом (который можно пронаблюдать, например, в мобильной версии Youtube) является частичный или полный запрет на посещение мобильной версии с десктопных браузеров. Как именно это контролируется?

При посещении сайта, браузер обычно высылает серверу информацию о себе. Информация передаётся как часть HTTP запроса - строчки, которая начинается с "User-agent:". Мобильные браузеры тоже имеют этот идентификатор и при подмене этой информации мы можем получить необходимый нам функционал.

Для подмены user-agent мы можем использовать библиотеку urlmon.dll. Данная библиотека контролирует запросы в IE и предоставляет функционал для работы с ними.

Добавим перед функциями следующий код:

[DllImport("urlmon.dll", CharSet = CharSet.Ansi)]
private static extern int UrlMkSetSessionOption(int dwOption, string pBuffer, int dwBufferLength, int dwReserved);
const int URLMON_OPTION_USERAGENT = 0x10000001;

Тут мы объявляем нужные константы и объявляем функцию установки определенного значения в HTTP запросах. Для более удобной работы объявим функцию смены user-agent, которая будет принимать строку и устанавливать её как текущее значение.

public void ChangeUserAgent(string Agent)
{
     UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, Agent, Agent.Length, 0);
}

Теперь, мы умеем менять данные о браузере и имитировать работу браузера с телефона. Вдобавок, я приведу несколько user-agent`ов мобильных устройств.

"HTCP3300-Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)",
"LG-KP500 Teleca/WAP2.0 MIDP-2.0/CLDC-1.1 Novarra-Vision/8.0",
"Lenovo_ET860/Lenovo WindowsMobile/6 Release/4.2 (compatible; MSIE 6.0; Windows CE; IEMobile 7.7)",
"MOT-MPx220/1.4 Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Smartphone; 176x220)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.6) PPC; 240x320; GIGABYTE-MS800",
"NOKIAN78/UC Browser7.0.0.41/28/400",


Прикрепление картинок и файлов к постам на форумах


Часто в практике создания спам-ботов необходимо предусматривать вставку картинок или файлов в сообщение. Иногда (как в примере, который был предложен в первом блоке статьи) можно справиться штатными текстовыми средствами (в нашем случае это bb-код [img] со ссылкой на картинку). Иногда, тем не менее, такой возможности нет.

В данном блоке мы не будем рассматривать конкретный пример сайта (обычно, эта потребность появляется при работе с большими сайтами вроде vk или google.com), а поговорим абстрактно о процессах, которые происходят при "аттаче" файла или картинки на сайт.
Если для вставки картинки используется кнопка, вызывающая диалоговое окно выбора файла - это значит, что на странице есть определенный элемент - <input type="file">. Данный элемент получает ссылку на файл в компьютере пользователя и позволяет передавать таким образом передавать файлы на сервер. Строка на сайт хранится в атрибуте value.

Обычно, для заполнения значений атрибутов используется метод SetAttribute() класса HtmlElement. Однако value элемента <input type="file"> нельзя заполнить таким образом - заполненный "вручную" value будет игнорироваться браузером. Такая реализация обусловлена безопастностью - злоумышленники, заполняя на своих сайтах это поле, могли бы получать файлы с компьютера пользователя без его ведома. Для правильного заполнения этого атрибута нужно использовать эмуляцию выбора файла.

Рассмотрим это подробнее на абстрактном примере:

HtmlElement he;
.
. // тут мы получаем в he элемент <input type="file">
.
he.Focus();
SendKeys.SendWait("C:/Image/Cat.jpg");

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

Обход капчи


Самым "тонким" и значимым местом в работе спам-ботов является капча. Капча - это картинка с рукописным (хотя обычно, сгенерированным путём преобразований печатного) текстом, который нужно ввести в поле ввода для подтверждения себя как реального неавтоматизированного пользователя. Однако, не существует таких вещей, которые нельзя обойти или взломать. Есть несколько вариантов обхода капчи:

  • Автоматизированное рапознавание капчи.
  • Распознавание капчи с помощью сервисов с ручным вводом.
  • Исследование механизмов вставки капчи и автоматические методы её обхода.

Рассмотрим все варианты более подробно:

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

Кроме автоматического распознавания капчи, существует ещё "потоковое" ручное распознавание. Существуют сервисы, которые предоставляют возможность за определенную (достаточно небольшую) плату функционал распознавания капчи, причем капчу вводят реальные люди, следовательно ошибок и погрешостей гораздо меньше. Именно на этом методе мы сконцентрируемся повнимательнее.

Помимо распознавания капчи, можно разрабатывать свой алгоритм спам-бота таким образом, чтобы при определенных условиях он не натыкался на капчу в принципе. Данный метод не универсален, однако подходит не для всех сайтов. Рассмотрим его подробнее на абстрактном примере.

Предположим у нас есть сайт, проанализировав который мы узнали, что он выдаёт капчу, если с одного аккаунта постится больше 5 комментариев в час. Кроме того, пронаблюдав первую версию работы программы без капчи, мы заметили, что она размещает 5 комментариев в минуту, используя прокси. Таким образом, можно догадаться, что используя более 60 аккаунтов поочерёдно (размещая с каждого за один раз один комментарий) в программе мы можем добиться полного отсутствия капчи и таким образом обойти её.

Как самый универсальный способ, рассмотрим использование сервисов, которые применяют ручное распознавание капчи.

Среди всех сервисов, стоит отметить следующие:

У каждого из этих сервисов есть свои преимущества и недостатки. Немножко ниже я подробно рассмотрю работу с сервисом antigate.com ввиду его быстрых ответов (5-15 сек.), наибольшего потока распознавателей, удобного API и сравнительно недорогой цены ($0.7 за 1000 распознанных картинок), а сейчас сконцентрируемся на ключевых преимуществах остальных сервисов, то есть на том, что может сместить ваш выбор в их пользу.

Сервис antigate.ru лучше остальных работает с русскоязычными капчами. Сайт является русскоязычным сектором antigate.com, поэтому о его стабильности нечего и говорить. Если вы работаете с такими сайтами, как vk.com - это ваш выбор.

captchabot.com - это что-то среднее между автоматическим распознаванием и потоковым. Сам сервис не использует людей для ввода капчи, а работает посредством своих алгоритмов распознавания для широкого сектора капч. Самым значимым плюсом является скорость распознавания (до 10 секунд в среднем). Если вы работаете с легкораспознаваемыми капчами на сервисах, где установлено ограничение времени по вводу капчи - это лучший выбор.

Практически все сервисы требуют инвайты для регистрации, однако их легко найти в большом количестве в интернете.

Теперь, рассмотрим подробнее работу с сервисом antigate в нашем приложении.

Для загрузки на сервер картинки, нам необходимо сформировать определенный POST запрос:

<form method="post" action="http://antigate.com/in.php" enctype="multipart/form-data">
 <input type="hidden" name="method" value="post">

Ключ от учетной записи:
<input type="text" name="key" value="">
Файл капчи:
<input type="file" name="file">
 <input type="submit" value="">
 </form>

В ответ приходит уникальный id - идентификационный номер вашей картинки. Чтобы получиить значение капчи, необходимо отправлять GET запрос:

http://antigate.com/res.php?key=КЛЮЧ_УЧЁТНОЙ_ЗАПИСИ&action=get&id=ID_КАПЧИ

В ответ могут приходить разные значения, нас интересуют 2 из них: CAPCHA_NOT_READY - капча ещё не распознана и OK|TEXT_FROM_CAPTCHA - ответ распознаной капчи (после OK| идёт собственно ответ). Остальные значения указывают на ошибки запроса (ERROR_KEY_DOES_NOT_EXIST, ERROR_WRONG_ID_FORMAT) или невозможность распознания капчи (ERROR_CAPTCHA_UNSOLVABLE).

Для реализации необходимой функции нам необходимо, чтобы в проект были включены следующие пространства имён:

using System;
 using System.Collections.Generic;
 using System.Text;
 using System.IO;
 using System.Net;
 using System.Web;
 using System.Windows.Forms;
 using System.Threading;

Далее я приведу код, рекомендованный к использованию в описании API (думаю, не стоит использовать велосипед и писать свои функции - кроме того, этот код был проверен во многих проектах, в том числе и в моих):

public class PostData5
 {
 private string s_method = String.Empty;

 public string Method { get { return this.s_method; } }
 private string s_action = String.Empty;

 public string Action { get { return this.s_action; } }
 public string Param { get { return this.s_param; } }

 private string s_param = String.Empty;
 public PostData5(string s_PostString)
 {
 if (s_PostString.IndexOf("=") != -1)
 {
 this.s_method = s_PostString.Substring(0, s_PostString.IndexOf("="));
 this.s_action = s_PostString.Substring(s_PostString.IndexOf("=") + 1);
 if (this.s_action.IndexOf("!") != -1)
 {
 this.s_action = s_action.Substring(0, this.s_action.IndexOf("!")); this.s_param = s_PostString.Substring(s_PostString.IndexOf("!") + 1);
 }

 }

 }
 public static string MultiFormData(string Key, string Value, string Boundary)
 {
 lock (typeof(PostData5))
 {
 string output = "--" + Boundary + "\r\n"; output += "Content-Disposition: form-data; name=\"" + Key + "\"\r\n\r\n";
 output += Value + "\r\n";
 return output;
 }
 }
 public static string MultiFormDataFile(string Key, string Value, string FileName, string FileType, string Boundary)
 {
 lock (typeof(PostData5))
 {
 string output = "--" + Boundary + "\r\n"; output += "Content-Disposition: form-data; name=\"" + Key + "\"; filename=\"" + FileName + "\"\r\n"; output += "Content-Type: " + FileType + " \r\n\r\n";
 output += Value + "\r\n";
 return output;
 }
 }
 }

 public class AntiCaptcha
 {
 public string captcha_id;
 public string response_str;
 public string GetText(string KapchaKey, string patch, int delay)
 {
 try
 {

 //Картинка содержится в теле ответа сервера myResp
 HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create("http://antigate.com/in.php");
 myReq.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 WebMoney Advisor";
 myReq.Accept = "*/*";
 myReq.Headers.Add("Accept-Language", "ru");
 myReq.KeepAlive = true;
 myReq.AllowAutoRedirect = false;
 myReq.Method = "POST";

 //POST параметры
 string sBoundary = DateTime.Now.Ticks.ToString("x");
 myReq.ContentType = "multipart/form-data; boundary=" + sBoundary;
 string sPostMultiString = "";
 sPostMultiString += PostData5.MultiFormData("method", "post", sBoundary);
 sPostMultiString += PostData5.MultiFormData("key", KapchaKey, sBoundary);
 sPostMultiString += PostData5.MultiFormData("file", Path.GetFileName(patch), sBoundary);
 sPostMultiString += PostData5.MultiFormData("numeric", "1", sBoundary);
 sPostMultiString += PostData5.MultiFormData("regsense", "0", sBoundary);
 string sFileContent = "";

 Stream fStream = null;
 fStream = File.OpenRead(patch);
 int nread;
 try
 {
 byte[] buffer = new byte[4096];
 while ((nread = fStream.Read(buffer, 0, 4096)) != 0)
 sFileContent += Encoding.Default.GetString(buffer);
 fStream.Close();
 }
 catch (Exception exc)
 {
 MessageBox.Show(exc.Message);
 }



 sPostMultiString += PostData5.MultiFormDataFile("file", sFileContent, Path.GetFileName(patch), "image/pjpeg", sBoundary);
 sPostMultiString += "--" + sBoundary + "--\r\n\r\n";
 byte[] byteArray = Encoding.Default.GetBytes(sPostMultiString);
 myReq.ContentLength = byteArray.Length;
 myReq.GetRequestStream().Write(byteArray, 0, byteArray.Length);
 string pg = "";
 string s = "";
 try
 {
 HttpWebResponse myResp = (HttpWebResponse)myReq.GetResponse();

 StreamReader reader = new StreamReader(myResp.GetResponseStream(), Encoding.GetEncoding(1251));

 pg = reader.ReadToEnd();
 string[] pars = pg.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
 captcha_id = pars[1];
 s = pars[1];
 }
 catch (Exception exc)
 {
 if (pg == "ERROR_NO_SLOT_AVAILABLE")
 return "ERROR_NO_SLOT_AVAILABLE";
 }

 //Ждём распознавания капчи
 try
 {
 for (int i = 0; i < 30; i++)
 {
     Thread.Sleep(delay);

     HttpWebRequest myReq2 = (HttpWebRequest)HttpWebRequest.Create("http://antigate.com/res.php?key=" + KapchaKey + "&action=get&id=" + s);

     myReq2.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 WebMoney Advisor";
     myReq2.Accept = "*/*";
     myReq2.Headers.Add("Accept-Language", "ru");
     myReq2.KeepAlive = true;
     myReq2.AllowAutoRedirect = false;
     myReq2.Method = "GET";

     HttpWebResponse ret2 = (HttpWebResponse)myReq2.GetResponse();

     StreamReader reader = new System.IO.StreamReader(ret2.GetResponseStream(), Encoding.GetEncoding(1251));
     pg = reader.ReadToEnd();
     response_str = pg;
     myReq2.Abort();
     reader.Close();
     ret2.Close();
     myReq2 = null;
     ret2 = null;
     if (pg != "CAPCHA_NOT_READY")
 {
 string[] pars = pg.Split('|');

 if (pars[0] == "OK")
 {
     return pars[1];
 }
 }

 }
 }
 
 catch (Exception exc)
 {
     return exc.Message;
 }
     return "000000";
 }
     catch { return "000000"; }
 }
 public string FalseCaptcha(string KapchaKey)
 {
     string pg = "";

 try
 {
     HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create("http://antigate.com/res.php?key=" + KapchaKey + "&action=reportbad&id=" + captcha_id);
     myReq.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 WebMoney Advisor";
     myReq.Accept = "*/*";
     myReq.Headers.Add("Accept-Language", "ru");
     myReq.KeepAlive = true;
     myReq.AllowAutoRedirect = false;
     myReq.Method = "GET";

     HttpWebResponse ret = (HttpWebResponse)myReq.GetResponse();

     StreamReader reader = new StreamReader(ret.GetResponseStream(), Encoding.GetEncoding(1251));
     pg = reader.ReadToEnd();
     myReq.Abort();
     reader.Close();
     ret.Close();
 }

 catch
 {
     FalseCaptcha(KapchaKey);
 }
     return pg;
 }
 }

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

  • Формирование запроса, куда мы добавляем картинку.
  • Ожидание ответа.
  • Разбор ответа.

Попробуйте поэкспериментировать с этим кодом. Если какая-то часть вам неясна - на сайте с описанием API есть описание примеров.

Я не останавливаюсь на моментах реализации функции отправки капчи по причине того, что API время от времени меняется. Однако, теперь вы знаете основные принципы разбора капчи и использования сервисов распознавания.

В завершение...


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

Сейчас, разрабатываемые мною приложения для спама vk.com и youtube.com и базирующиеся на разобранных сегодня принципах позволяют оставлять с одного компьютера более 30 000 комментариев в сутки. Только 3% аккаунтов подвержены бану. Траффик, приходящий от таргетированного спама в несколько раз превышает траффик от Яндекс.Директ.

Тем не менее, уважаемые коллеги, призываю вас использовать полученные звания в хорошем русле, не понижайте карму - не рекламируйте средства для похудения :)

С проектом, в котором мы экспериментировали, вы можете ознакомиться тут.

До новых встреч, жду вопросов в комментариях.

Автор: Артём Мигда

Источник: hpc.name

Материал добавил raccoon


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

Дата: 2013-09-15 21:39:35

Добавить Комментарий к Материалу

Вы должны быть авторизованы на форуме чтобы добавлять комментарии. Регистрация Уже авторизованы?

Комментарии к Материалу

 vladyxa13              2014-01-01 15:05:26

Кулл Веселый

Последнее на Сайте

Новости

Статьи

Bugtraq

Файлы

Copyright © 2008 - 2017 «HPC». При копировании материалов ставьте ссылку на источник.
Все материалы представлены только в ознакомительных целях, администрация за их использование ответственности не несет.
Пользовательское соглашение Реклама на сайте