Во многих фильмах есть момент, когда хакеру, взламывающего человека, нужно отправить сообщение на его номер, чтобы его отвлечь или напугать и в таких ситуациях самое важное, чтобы, когда человек позвонит на номер с которого пришло СМС у вас не загорелся экран смартфона и не заиграла музыка. Упс… Неловкая ситуация. В этой теме мы напишем программу на C++, которая позволит отправить СМС не используя вашего номера, сим-карты и души.
Слово “Анонимность” подразумевает собой то, что кто отправил сообщение на самом деле не узнается, но не всё так хорошо. При разбирательстве этот факт выяснится, так что при особо тяжких приколах я не приду навестить вас на испытательных работах.
Предупреждение. Поставленную задачу можно было решить более лёгкими путями. В данной теме приведено одно из решений именно на языке 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
}
На этом моменте с вашего счёта списываются деньги за отправку сообщения. И сообщение идёт к адресату. Когда он его получит, то это будет номер компании или текстовый идентификатор и вряд ли человек поймёт от кого было это сообщение.
Вот что вышло у меня при написании этой программы:
Получилось коряво, но что есть. Всем удачи, возможно эта тема вам чем то помогла.
Это была моя вторая статья тут. Спасибо за прочтение. Удачи в разработке ваших приложений.
Комментарии (21)
iig
04.08.2018 09:21+2В названии ошибка. Какая анонимность, если надо зарегистрироваться и закинуть деньги на счёт?
Qwarri Автор
04.08.2018 15:35И там есть дальше предупреждение. Узнать кто на самом деле стоит за отправкой можно будет только сделав запрос к сайту. А кому понравится, когда к вам лезут за выяснениями каких-либо сообщений, запрос будет отклонён, до официального решения высказать личность индивидума. Так что я вижу тут некую анонимность. Но как бы там ни было, я пересмотрю свои взгляды на тему и сделаю выводы
bogolt
04.08.2018 10:25+6Лабораторная работа да?
— json формируется руками. Что простите? Любая ошибка и добро пожаловать в отладку. Я уже не говорю про отсутствие экранирования для переменных. Что программа сделает если пользователь введет номер с кавычкой?
— Можно подробностей как работает verify_certificate? Насколько я вижу он всегда возвращает одно значение которое ему передали, а сам сертификат вроде и не проверяет даже. Впрочем допускаю что там какая-то магия внутри происходит, поэтому и спрашиваю.
— socket_.read_some может вернуть не все данные а только часть — tcp же. Чтобы получить все данные нужно читать до упора ( до закрытия коннекта в вашем случае ).
— и наконец последняя, самая серьезная придирка.
ЗАЧЕМ?
Зачем нужно писать кривую программу на си++ когда можно было выполнить
curl адрес -d '{тут-ваша-мега-жсон}'Serge78rus
04.08.2018 13:10Зачем нужно писать кривую программу на си++ когда можно было выполнить curl…
Судя по использованию boost::asio, можно предположить, что планируется работа под большой нагрузкой. Но это лишь предположение — в том виде, как написано, действительно похоже на эксперимент или лабораторную работу.bogolt
04.08.2018 13:15boost.asio имел бы смысл на сервере, а для клиента с трудом представляю как его нужно нагружать чтобы это имело смысл. Ну и уж тогда начать с переиспользования коннектов, асинхронности и тд
Serge78rus
04.08.2018 15:19Полностью согласен, но по приведенным фрагментам кода ясно же видно, что это всего лишь попытка попробовать. Конечную свою цель автор, увы, не посчитал нужным озвучить.
Qwarri Автор
04.08.2018 15:23Спасибо за критичный отзыв. Всё действительно так. Эту задачу можно было решить куда легче. Возможно даже я переосмыслю то, что в попыхах было написано и под корень исправлю. Но пусть пока это будет одним из возможных вариантов решения задачи. Главное думаю тут, не сама программа, а смысл того, как можно было отправить СМС без напряга. И ещё, эта программа была частью большого проекта, так что от туда и осталось решение создания соединения. И я хотел ещё второй темой в данном разделе показать применение именно на C++, так что я тут особо проблем не вижу (кроме корявого кода). Будем исправляться
igorgusarov
04.08.2018 15:24Почему не использовать curl или что-то подобное? Обычно http-запрос можно сформировать одной командой в консоли. К тому же, у Bytehand есть интерфейс для отправки сообщений.
Qwarri Автор
04.08.2018 15:27Да, всё верно. Решение было бы куда легче. Но а вдруг приложение обрастёт интерфейсом, сложными функциями и т.п. маловероятно, но ведь вполне возможно (именно такое приложение у меня получилось в итоге), тогда было бы кстати контролировать весь процесс. И второй темой в разделе у меня было показать как именно это сделать на C++. Спасибо за комментарий. Есть о чём подумать.
sha4
04.08.2018 15:35Можно ли в имя отправителя (в поле sms-info) вставить не текст, а цифры, чтобы получился телефонный номер, который принимающий телефон сможет ассоциировать с имеющимся в адресной книге? Bytehand будет такое блокировать?
Qwarri Автор
04.08.2018 15:44Сервер сообщает об успешной отправке, но сообщение не дойдёт до адресата, т.к. сообщение будет отклонено внутри сервиса.
technic93
06.08.2018 06:10Проверять сертификат надо согласно rfc2818, и использовать tls. А ssl::context::verify_none как бы намекает что у вас чего то не хватает.
de1m
Может быть, кто-то подскажет обратный сервис (в какой-то мере), на который наоборот можно всякий спам скидывать и при всяких регистрациях вводить?
Tairesh
SMS-REG и подобные
Naves
ищите по словам «виртуальный номер телефона для приема смс»
их сотни бесплатных и не очень