Содержание статьи
Конкурсы с голосованием — модный тренд. Они привлекают большое количество посетителей, а трафик, как известно, — это деньги. Особый интерес вызывают конкурсы, где за победу предлагаются лакомые призы. В этой статье я хочу рассказать о довольно своеобразном способе участия в подобных опросах.
Предыстория
Как-то раз, глубокой ночью, когда все нормальные люди уже спят, я сидел и о чем-то яростно дискутировал с другом в аське. В этот момент ко мне постучался клиент, который предложил плевую, на первый взгляд, задачу, суть которой заключалась в накрутке определенного количества голосов в одном онлайн-голосовании. Само голосование проходило за лучший короткометражный авторский фильм. Уже имея опыт накручивания всевозможных счетчиков, я сначала подумал, что потребуется набросать очередной скрипт из пары запросов на cURL, проходящих через прокси-сервер. Сомнения закрались, когда я увидел главный приз конкурса — один миллион рублей.
Тут мне стало интересно, к каким методам защиты от накрутки прибегла администрация сайта при таком существенном спонсировании. Ниже — мой отчет о том, как я последовательно решал проблемы, необходимые для реализации эффективного накрутчика.
Разведка
Сразу предупрежу, что в подобных голосованиях отслеживается IP-адрес каждого запроса, но подробно рассматривать это проблему мы не будем.
Существует довольно много способов добыть свежие прокси, а с технической точки зрения смена прокси для выполнения нового запроса решается всего одной строкой c cURL:
curl_setopt($c, CURLOPT_PROXY, $proxy_address);
Разработку накрутчика всегда стоит начинать с разведки. Я решил не изменять этому правилу и, прежде чем голосовать, запустил свой снифер. Для идентификации нас как уникального посетителя сайт сначала ставил в куки банальную сессию. Затем ресурс предлагал указать возраст для подтверждения совершеннолетия (скорее всего, потому, что некоторые фильмы были «для взрослых»).
Хакер #157. Деньги на багах в Chrome
Посмотрим на это с точки зрения накрутки: скрипт каждый раз проверяет дату рождения, поэтому было бы глупо производить накрутку с тысяч IP-адресов с одинаковой датой рождения. А значит, первое, что мы должны предусмотреть, — это граббинг сессии и рандомную генерацию даты рождения в cookie. Для этого в самом начале нашего скрипта мы объявим глобальную переменную с ранее сгенерированной датой в кукисах:
$cookie_session = array(
'BIRTHDAY='.rand(1, 29).'-'.rand(1, 12).'-'.rand(1960, 1985),
'IS_18OLDER=1',
'LANG=en'
);
Теперь, после того как мы подтвердили, что уже взрослые дядьки, нам следует зарегистрироваться. Переходим на страницу регистрации и видим, что нас просят ввести имя, фамилию, e-mail, на который придет активационный код, и текст с капчи. Данные для первых двух полей, а именно фамилию и имя, мы тоже должны генерировать каждый раз разные, так как 20 тысяч голосов от Петра Сидорова немного насторожат администратора. 🙂
Имена и фамилии легко достать в интернете или спарсить самому с какого-нибудь сайта имен и фамилий. А вот с активационным кодом и капчей придется повозиться. Хорошо, заполняем форму, нажимаем «Зарегистрироваться» и топаем на мыло. Там нас ждет письмо со ссылкой на подтверждение регистрации. После активации аккаунта нам следует авторизоваться, то есть зайти на страницу авторизации и отправить логин и пароль. При этом мы получаем в кукисах вторую сессию, отвечающую за доступ к аккаунту. Затем нужно перейти на страничку видеоролика, который мы накручиваем, и нажать кнопочку «Голосовать». Запрос передается через Ajax, что немного ускоряет процесс накрутки. В принципе, это все. Но не забываем, что на кону целый миллион, поэтому нам нужно учесть все детали и мелочи, чтобы накрутку нельзя было заметить.
Уникальность заголовков
В любой накрутчик сразу же хочется добавить многопоточность. Но данный случай, скорее, представляет собой исключение из правил, и многопоточность нам только помешает. Если проголосовать в одну секунду тысячу раз, а потом весь день не голосовать вообще, это будет слишком подозрительно. Поэтому мы прибегнем к поочередному голосованию, во время которого к тому же будем делать паузы.
Вдобавок следует отметить, что браузер (заголовок User-Agent) тоже нужно генерировать всегда рандомно, так как для пущей достоверности у всех ботов должны быть разные «браузеры». Для этого у меня давно подготовлен скрипт с большим количеством юзер-агентов, который ждет тебя на нашем диске. Достаточно просто добавить две строки:
include('./useragents.lib.php');
$chosen_useragent = chooseBrowser();
Таким образом, мы получим рандомный браузер из 150 возможных. Идем дальше. Сайт сразу устанавливает сессию, которая потом передается в каждом запросе через cookie. Поэтому первое, что нам нужно сделать при работе с сайтом, — это придумать, под какой браузер мы будем маскироваться (User-Agent), и получить сессию, чтобы потом вставлять ее в каждый запрос.
В cURL есть возможность манипулировать cookie-записями, но я не люблю этот способ и сохраняю куки в глобальной переменной, которая доступна из всех функций. Для этого просто составляем запрос с предустановленным юзер-агентом к главной странице сайта, получаем куки, которые возвращает сервер, и сохраняем их в переменной для дальнейшего использования.
Загрузка всей страницы
Еще один момент, который может показаться излишним, — это выполнение абсолютно всех запросов, которые выполняет браузер. Возникает соблазн отправить минимум запросов с данными. Но это было бы ошибкой. Почему? Потому, что на сайте может действовать такой способ выявления накрутки, как, скажем, учет хитов по картинке.
Это означает, что количество загрузок, например, картинки видеоролика должно приблизительно равняться количеству просмотров этого видео. Количество посещений всегда должно превышать (раза в два-четыре) количество голосов. Таким образом, мы будем не только накручивать голоса с помощью левых аккаунтов, но и эмулировать «ботов», просматривающих видео. Лично я взял соотношение 1 : 3, чтобы на три просмотра приходился один голос. Но если грузить только страницу просмотра видео (накручивать количество просмотров), то нужно также не забывать загружать и весь остальной контент, то есть картинки, JavaScript-файлы, таблицы стилей и всё-всё-всё, вплоть до иконки сайта. А как иначе? Для 100 голосов нужно 1000 посещений, а если при 1000 посещений картинка видеоролика будет загружена всего 20 раз, то это вызовет явные подозрения.
Каким же образом можно эмулировать абсолютно всю загрузку? Ведь для этого необходимо парсить страницу, скрипты, таблицы стилей и все остальное. Нет, парсить мы ничего не будем, точнее, будем, но не страницу. Все можно сделать проще. Достаточно лишь воспользоваться плагином LiveHTTPHeaders в браузере Mozilla Firefox (или же сервисом Opera Dragonfly из могучей Оперы) и открыть с его помощью страницу, загрузку которой мы хотим эмулировать. На выходе мы получим длинный лог всех обращений ко всем файлам, которые загрузил браузер. Сохраним этот лог в файле и напишем две функции. Первая будет парсить этот лог-файл и возвращать нам массив из значений, где адресом загружаемого файла служит ключ, а значением — хидер текущей загрузки, причем с предустановленными куки (сессией) и браузером, так как куки и браузер должны меняться при каждом голосовании (поскольку объем журнала крайне ограничен, советую прямо сейчас найти на нашем диске соответствующую функцию и изучить ее). После сохранения лога в файле и вызова вышеупомянутой функции в виде
$list = parseRequests(file_get_contents('./index_map.txt'),
$chosen_useragent, $cookie);
мы получим массив из всех запросов, которые выполнил браузер при загрузке страницы. Вторая функция называется curlMulti() и отвечает за выполнение этих запросов. Здесь мы как раз вполне можем использовать многопоточность, поскольку браузер умеет загружать файлы многопоточно (снова смотри диск). Эта функция принимает массив ссылок и массив шапок (headers), которые впоследствии загружаются многопоточно, что ускоряет процесс.
Также можно опционально выключить/включить загрузку самих файлов, оставив только загрузку шапки, либо просто посылать запросы, ничего не загружая. Последний параметр функции позволяет загрузить только определенный элемент, если нам не нужны все остальные. Открою тебе небольшую тайну: это пригодится при загрузке страницы регистрации, а именно при загрузке файла капчи.
Извлекаем CAPTCHA
Чтобы эмулировать действия пользователя, для начала мы должны зайти на главную страницу. Сделаем это с помощью следующего кода:
function loadIndex()
{
global $chosen_useragent, $cookie_session;
$list = parseRequests(file_get_contents('./index_map.txt'),
$chosen_useragent, 'Cookie: ' . implode('; ', $cookie_session));
$links = array(); $heads = array();
foreach ($list as $link => $head){$links[] = $link; $heads[] = $head;}
$paged = cM($links, $heads, 1, 1);
}
Как видно по вышеприведенной функции, файл index_map.txt как раз и представляет собой лог, созданный с помощью аддона к Firefox при загрузке всей страницы. Этот лог, кстати, также следует немного обработать вручную, так как загружать, например, рекламу Гугла или файлы, размещаемые на других сайтах, не имеет смысла. С главной страницы перейдем на страницу регистрации. За некоторыми отличиями функция для захода на страницу регистрации будет похожа на предыдущую. Нам нужно подготовить еще один лог-файл с помощью LiveHTTPHeaders и поправить его, а также заменить строку
$paged = cM($links, $heads, 1, 1);
на
$paged = cM($links, $heads, 1, 1, 'captcha.php');
list($c_url, $sid) = explode('captcha_sid=', $links[11]);
return array('sid' => $sid, 'image' => base64_encode($paged[11]));
В данном случае будет эмулироваться загрузка всех элементов, а картинка капчи даже вполне успешно вернется. $links[11] и $paged[11] — это ссылка и значение запроса для 12-го элемента загрузки, а именно капчи, соответственно (рассчитывается на основе порядка следования файлов в логе снифера). Из ссылки выдирается sid, к которому привязано значение текста с капчи. Далее нужно разгадать капчу.
В этом нам поможет известный сервис antigate.com, который за символическую плату ($1 за 1000 изображений) предлагает решить капчи вручную с помощью армии китайцев. В моем случае API-функция распознавания, представленная на официальном сайте, немного модифицирована. В ней я указал только путь к сохраненному файлу капчи и ключ доступа:
$captcha = loadReg();
$local = md5($captcha['image']);
$write_c = fopen('./captchas/'.$local.'.jpg', 'wb');
fputs($write_c, base64_decode($captcha['image']));
fclose($write_c);
$cresult =
recognize('./captchas/'.$local.'.jpg', 'e12dc4858bac1f4ee338c577f9d300');
Теперь у нас есть ответ капчи в переменной $cresult.
Проблема с почтой
Далее мы сталкиваемся с необходимостью как-то зарегистрировать почту, чтобы позже использовать ее для активации аккаунта. Само собой, мыло каждый раз должно быть разное. Проблему можно решить тремя способами:
- Зарегистрировать аккаунты на бесплатных почтовых серверах, например Яндексе или Рамблере. Конечно, если нужно большое количество мейлов, то придется использовать крутой автореггер или купить кем-то зарегистрированные (опять же с помощью автореггера) аккаунты.
- Купить домен, схожий по названию с известными почтовиками, и настроить скрипт, собирающий в один ящик почту, отправляемую на все адреса. Таким образом, почта, отправленная на 123@домен.ру и 234@домен.ру, попадет в один ящик, а значит, можно смело генерировать тысячи адресов. Здесь снова нужны деньги и знания.
- Самый простой и бесплатный способ — использовать скрипт, который любезно подготовлен автором этой статьи. Скрипт использует бесплатный сервис mailinator.net для проверки любого адреса, уже зарегистрированного в системе. Нам нужно просто указать произвольный адрес на доменах этого сервиса, а потом зайти (без авторизации!) в соответствующий аккаунт через веб-интерфейс и проверить почту. Всего доступно 11 доменов. Скрипт с подробными комментариями по использованию ждет тебя на диске.
Какой же из способов мы применим? К сожалению, нам придется потратиться на первый вариант, так как важность момента требует жертв. Мы не будем писать автореггер, а просто найдем на любом из хакерских форумов продавца аккаунтов, у которого можно купить адреса электронной почты, подходящие для проверки через расширение PHP IMAP. Да, доступ к серверам почтовых сервисов осуществляется по-разному, поэтому нужно заранее узнать, какой из них нам подойдет, и лишь только потом закупаться аккаунтами. Лично мне подходит аккуант на Рамблере, так как никаких проблем с проверкой почты я там никогда не испытывал. Далее пишем вот такую вот функцию для получения тела последнего письма:
function getMessage($login, $password)
{
$imap = imap_open('{mail.rambler.ru:110/pop3/notls}INBOX',
$login, $password);
if ($imap){$body = imap_qprint(imap_body($imap,
(imap_num_msg($imap) - 1)));}
else{return false;}
return $body;
}
Эта функция возвращает текст последнего письма, пришедшего в ящик. Подготовим все необходимые данные, а именно само мыло, имя, фамилию и пароль, для соответствующих переменных. Где взять адреса для активации аккаунтов, я рассказал выше, а множество имен и фамилий легко найти в интернете. Пароль можно генерировать вот так:
$password = substr(md5(time()), 0, rand(6, 10)).rand(10,99);
Регистрация
Теперь все готово, и мы можем написать саму функцию регистрации, в чем нам снова поможет плагин LiveHTTPHeaders. В данном случае у нас имеются POST-данные, отправленные в виде multipart/form-data. Достаточно просто подставить в лог со снифера свои значения и послать их в POST-запросе курла (CURLOPT_POST, CURLOPT_POSTFIELDS).
Не забываем также о меняющемся значении заголовка Content-Type и о том, что при запросе через multipart/form-data нужно генерировать boundaries. Отправляемые пакеты целесообразно сохранить где-нибудь, например в базе MySQL, для использования в дальнейшем. Сохранить необходимо логин и пароль от почты, пароль от аккаунта на сайте, юзер-агент, куки. Почему именно в дальнейшем?
Потому, что сначала следует подождать, пока к нам на мейл придет активационное письмо. Но не стоит тратить время впустую, лучше заняться регистрацией аккаунтов, чтобы их было как можно больше. Таким образом, разумно разделить весь процесс на регистрацию аккаунтов и накрутку и выполнять их, например, ночью и днем соответственно.
Через некоторое время мы открываем сохраненные данные и ищем там логин и пароль от почты. Далее проверяем, нет ли в ящике письма:
$activation = getMessage($email_login, $email_passw);
Если письмо пришло, то выдираем ссылку активации простой регуляркой. Тут никаких особых проблем возникнуть не должно — просто формируем запрос на подтверждение регистрации (обязательно вставляем referer и добавляем куки и user-agent, выбранные ранее). Опять же, не забываем про загрузку всех элементов: создаем лог-файл со всеми запросами, парсим его и повторяем действия браузера.
После получения всех данных и подтверждения регистрации нам остается только авторизоваться. Снова делаем запрос, эмулируя браузер и сохраняем полученные куки в глобальной переменной. Таким же образом переходим на страницу видеоролика, который надо накрутить, и смотрим, куда Ajax отправляет запрос после нажатия на кнопку голосования. Последний шаг — отправка этого запроса через cURL. Все запросы желательно делать с трех-пятисекундной паузой.
Как же автоматизировать процесс накрутки? Можно, конечно, использовать циклы, sleep() и прочее непотребство, но я поступил проще и сделал так, чтобы после прохода скрипта (без циклов, один голос за один запуск скрипта) в браузер выкидывался код JavaScript, обновляющий страницу через одну-две минуты. С автоматизацией тебе точно так же поможет и cron на каком-нибудь платном хостинге.
Что можно накручивать?
- Партнерки Накрутка партнерок по трафику пользуется очень большим спросом, но здесь далеко не всё так просто, как кажется. Во-первых, придется проанализировать множество JavaScript'ов, встраиваемых в страницу, во-вторых, ты, скорее всего, столкнешься с проблемой привязки некоторых посылаемых данных к параметрам браузера, которые не так-то просто подделать.
- Популярные социальные сервисы Вконтакте, Facebook, YouTube и прочие знаменитые сайты часто используют разные голосования, «лайки» и другие средства для увеличения популярности разных объектов. Здесь также придется возиться с JavaScript'ами и анализировать привязки к браузерам, а вдобавок разбираться с авторизацией, капчей и другими методами защиты от ботов.
- Голосования с призами В статье как раз идет речь о таком голосовании. Многие сайты с радостью проводят всевозможные голосования, которые, как правило, не составляет труда накрутить. Но даже в самом простом случае предварительно необходимо провести разведку, чтобы учесть все параметры, которые могут использоваться для оценки уникальности каждого голоса.
Получилось или нет?
Что мы получили в результате? Видеоролик постепенно набирал голоса посетителей и «лайки». Все шло гладко, посетителей прибавлялось в три раза больше, чем лайков. Ночью голосование я отключал, так как иначе все это было бы подозрительно. Накрутив пару тысяч голосов, мой заказчик все-таки выиграл тот самый миллион, ну и я тоже получил небольшой пряник. Без награды не остались и еще одни участники этой истории — инсайдеры.
Да, ради миллиона в компанию, проводившую это голосование, был заслан «Штирлиц», который информировал заказчика о некоторых важных деталях. Как оказалось, кроме меня, были и другие накрутчики, причем таких претендентов специально не снимали с конкурса и не обнуляли их голоса — инсайдер доложил, что их выкинут в самом конце. Насчет нас, конечно же, изначально не возникло никаких подозрений. 🙂
Занимаясь накруткой, всегда смотри на этот процесс глазами администратора. Следует обратить внимание на всё, к чему можно прикопаться: время, заголовки, сессии, куки, IP-адрес, мыло, скорость. Эти слова сразу же наводят на мысль, что учет всех мелочей очень замедляет процесс. Но сроки в данном случае как раз не поджимают, Советую тебе внимательно изучить все прилагающиеся к статье скрипты, чтобы получить полное представление о том, как работает разработанный накрутчик.