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

Во многих фильмах есть момент, когда хакеру, взламывающего человека, нужно отправить сообщение на его номер, чтобы его отвлечь или напугать и в таких ситуациях самое важное, чтобы, когда человек позвонит на номер с которого пришло СМС у вас не загорелся экран смартфона и не заиграла музыка. Упс… Неловкая ситуация. В этой теме мы напишем программу на C++, которая позволит отправить СМС не используя вашего номера, сим-карты и души.

Mr. Robot TV Show

Слово “Анонимность” подразумевает собой то, что кто отправил сообщение на самом деле не узнается, но не всё так хорошо. При разбирательстве этот факт выяснится, так что при особо тяжких приколах я не приду навестить вас на испытательных работах.

Предупреждение. Поставленную задачу можно было решить более лёгкими путями. В данной теме приведено одно из решений именно на языке c++.

Приступим к делу.

Сервис bytehand – это решение для бизнеса, позволяющее делать как массовые рассылки сообщений, так и одиночные. Главная особенность сервиса и почему я его выбрал является доступное со всех платформ API, которое представляет собой HTTP Запрос-Ответ с очень простой системой авторизации.

Вот что нам понадобится:

  • Аккаунт на bytehand
  • 100 рублей на счету (А что вы хотели, СМС не бесплатные)
  • Консольная программа c++
  • Собранные библиотеки Boost.Asio и OpenSSL для https соединения
  • Созданный через OpenSSL сертификат для защищённого обмена данными с сервисом bytehand.

Первым делом регистрируемся на сервисе bytehand. Регистрация представляет собой введённый e-mail и пароль. Этого уже достаточно. После этого создаётся ваш аккаунт и при пополнении счёта (минимум 100р) вы уже можете отправлять сообщения с заголовком SMS-INFO. У большинства операторов эта запись заменяется номером отправления. При желании можно придумать свой собственный текстовый псевдоним.

Далее лезем в API ресурса. И видим, что что бы отправить СМС нужно всего лишь сформировать следующий пакет данных:

POST /v2/sms/messages HTTP/1.1
Host: api.bytehand.com
Connection: close
Content-Length: *
Content-Type: application/json;charset=UTF-8
X-Service-Key: ab4db0b982dcd0ba63e44191e5d71ef8

{
  "sender": "MyShop",
  "receiver": "+79167654321",
  "text": "Today only! 20% off for all goods!!"
}

Обращу ваше внимание на то, что X-Service-Key в заголовке http это и есть вся авторизация. Этот ключ можно посмотреть в настройках на сайте в разделе “безопасность”.

Думаю здесь всё понятно и можно приступить к написанию консольной программы на c++

Первым делом представляю вашему вниманию фрагмент кода, который с помощью Boost.Asio устанавливает соединение с сайтом по https протоколу

// Подключение библиотек Boost
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
// ---------------------------

// ---------- Include OpenSSL Lib ----------
#pragma comment (lib, "libeay32.lib")
#pragma comment (lib, "ssleay32.lib")
// -----------------------------------------

using namespace boost::asio;

// Функция верификации сертификата
bool verify_certificate(bool preverified, boost::asio::ssl::verify_context& ctx)
{
	X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());

	return preverified;
}

int main()
{
	io_service service; // Класс, реализующий сокеты подключения

	/*------------------------------------------------------------
	-          Устанавливаем соединение с сервером             -
	------------------------------------------------------------*/
	ip::tcp::resolver resolver(service);
	ip::tcp::resolver::query query("api.bytehand.com", "https");
	ip::tcp::resolver::iterator iterator = resolver.resolve(query);

	ssl::context context(boost::asio::ssl::context::sslv23);
	context.load_verify_file("dh2048.pem");

	ssl::stream<ip::tcp::socket> socket_(service, context);

	socket_.set_verify_mode(ssl::context::verify_none);
	socket_.set_verify_callback(boost::bind(verify_certificate, _1, _2));

	connect(socket_.lowest_layer(), iterator);

	socket_.handshake(boost::asio::ssl::stream_base::client);

        //Здесь мы можем передавать данные серверу через функцию  socket_.write_some()
}

В коде используется ссылка на файл сертификата dh2048.pem. Его вы замените своим.
Так же можно увидеть, что тут в качестве сервера используется доменное имя api.bytehand.com

Далее пример как формируется заголовок и тело http запроса для сервера.

int main()
{
	/* Тут то, что было в предыдущем коде создания https соединения*/

	// Эти переменные заменяются вводом текста с клавиатуры и проверку на ошибки
	// Здесь код упращён для его понимания
	string number     = "+79180000000";
	string signature  = "SMS-INFO";
	string text       = "Today only! 20% off for all goods!!";

	std::stringstream request_; // Хранит запрос к серверу
	char sockBuffer[8192];      // Временное хранилище ответа от сервера

	// Выносим тело в отдельную переменную, что бы посчитать длину для заголовка Content-Length
	string reqTmp = "{\"sender\": \"" + signature + "\",\"receiver\": \"" + number + "\",\"text\": \"" + text + "\"}\r\n";

	request_ << "POST /v2/sms/messages HTTP/1.1\r\n";
	request_ << "Host: api.bytehand.com\r\n";
	request_ << "Connection: close\r\n";
	request_ << "Content-Length: " << reqTmp.length() << "\r\n";
	request_ << "Content-Type: application/json;charset=UTF-8\r\n";
	request_ << "X-Service-Key: ab4db0b982dcd0ba63e44191e5d71ef8\r\n";
	request_ << "\r\n";
	request_ << reqTmp;
	
	// Отправляем пакет на сервер
	socket_.write_some(buffer(request_.str()));
	
	memset(sockBuffer, NULL, 8192);
	
	// Читаем ответ от сервера в буффер
	socket_.read_some(buffer(boost::asio::buffer(sockBuffer)));

	// Выводим ответ на экран
	cout << endl << endl << sockBuffer << endl;
}

Напоминаю, что поле X-Service-Key вы заменяете на ваш ключ.

После выполнения этого кода и если всё прошло как надо, сервер возвращает ответ:

Status Code: 200
Content-Type: application/json;charset=UTF-8
Content-Language: en
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache

{
  "result": "created",
  "count": 1
}

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

Вот что вышло у меня при написании этой программы:

MyProgram

Получилось коряво, но что есть. Всем удачи, возможно эта тема вам чем то помогла.

Это была моя вторая статья тут. Спасибо за прочтение. Удачи в разработке ваших приложений.

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


  1. de1m
    03.08.2018 17:49
    +1

    Может быть, кто-то подскажет обратный сервис (в какой-то мере), на который наоборот можно всякий спам скидывать и при всяких регистрациях вводить?


    1. Tairesh
      03.08.2018 17:56

      SMS-REG и подобные


    1. Naves
      03.08.2018 18:05

      ищите по словам «виртуальный номер телефона для приема смс»
      их сотни бесплатных и не очень


  1. Naves
    03.08.2018 18:10
    +1

    request_ << «POS /v2/sms/messages HTTP/1.1\r\n»;

    POS?


    1. Qwarri Автор
      03.08.2018 18:18

      Ух, ошибочка вышла. Подправим. Спасибо


      1. Naves
        03.08.2018 18:22
        +1

        А оно даже так работает или в рабочем коде другой запрос был?


        1. Qwarri Автор
          03.08.2018 18:25

          В рабочем коде был POST. Так оно бы не работало.


  1. iig
    04.08.2018 09:21
    +2

    В названии ошибка. Какая анонимность, если надо зарегистрироваться и закинуть деньги на счёт?


    1. Qwarri Автор
      04.08.2018 15:35

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


  1. bogolt
    04.08.2018 10:25
    +6

    Лабораторная работа да?

    — json формируется руками. Что простите? Любая ошибка и добро пожаловать в отладку. Я уже не говорю про отсутствие экранирования для переменных. Что программа сделает если пользователь введет номер с кавычкой?
    — Можно подробностей как работает verify_certificate? Насколько я вижу он всегда возвращает одно значение которое ему передали, а сам сертификат вроде и не проверяет даже. Впрочем допускаю что там какая-то магия внутри происходит, поэтому и спрашиваю.
    — socket_.read_some может вернуть не все данные а только часть — tcp же. Чтобы получить все данные нужно читать до упора ( до закрытия коннекта в вашем случае ).

    — и наконец последняя, самая серьезная придирка.
    ЗАЧЕМ?
    Зачем нужно писать кривую программу на си++ когда можно было выполнить
    curl адрес -d '{тут-ваша-мега-жсон}'


    1. Serge78rus
      04.08.2018 13:10

      Зачем нужно писать кривую программу на си++ когда можно было выполнить curl…
      Судя по использованию boost::asio, можно предположить, что планируется работа под большой нагрузкой. Но это лишь предположение — в том виде, как написано, действительно похоже на эксперимент или лабораторную работу.


      1. bogolt
        04.08.2018 13:15

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


        1. Serge78rus
          04.08.2018 15:19

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


    1. Qwarri Автор
      04.08.2018 15:23

      Спасибо за критичный отзыв. Всё действительно так. Эту задачу можно было решить куда легче. Возможно даже я переосмыслю то, что в попыхах было написано и под корень исправлю. Но пусть пока это будет одним из возможных вариантов решения задачи. Главное думаю тут, не сама программа, а смысл того, как можно было отправить СМС без напряга. И ещё, эта программа была частью большого проекта, так что от туда и осталось решение создания соединения. И я хотел ещё второй темой в данном разделе показать применение именно на C++, так что я тут особо проблем не вижу (кроме корявого кода). Будем исправляться


  1. bogolt
    04.08.2018 13:14

    (ошибся веткой )


  1. igorgusarov
    04.08.2018 15:24

    Почему не использовать curl или что-то подобное? Обычно http-запрос можно сформировать одной командой в консоли. К тому же, у Bytehand есть интерфейс для отправки сообщений.


    1. Qwarri Автор
      04.08.2018 15:27

      Да, всё верно. Решение было бы куда легче. Но а вдруг приложение обрастёт интерфейсом, сложными функциями и т.п. маловероятно, но ведь вполне возможно (именно такое приложение у меня получилось в итоге), тогда было бы кстати контролировать весь процесс. И второй темой в разделе у меня было показать как именно это сделать на C++. Спасибо за комментарий. Есть о чём подумать.


  1. sha4
    04.08.2018 15:35

    Можно ли в имя отправителя (в поле sms-info) вставить не текст, а цифры, чтобы получился телефонный номер, который принимающий телефон сможет ассоциировать с имеющимся в адресной книге? Bytehand будет такое блокировать?


    1. Qwarri Автор
      04.08.2018 15:44

      Сервер сообщает об успешной отправке, но сообщение не дойдёт до адресата, т.к. сообщение будет отклонено внутри сервиса.


  1. SopaXT
    04.08.2018 17:52

    А зачем вам было это нужно? ;)


  1. technic93
    06.08.2018 06:10

    Проверять сертификат надо согласно rfc2818, и использовать tls. А ssl::context::verify_none как бы намекает что у вас чего то не хватает.