
Драйвер защиты процессов на уровне ядра с пользовательским графическим интерфейсом
Драйвер защиты процессов в режиме ядра, использующий ObRegisterCallbacks для удаления прав доступа к памяти из дескрипторов. Базовая реализация, перехватывающая создание дескрипторов и удаляющая PROCESS_VM_READ/WRITE/OPERATION из попыток несанкционированного доступа.


Примечание: Если GIF не анимируется, просмотрите его напрямую на Imgur
Драйвер регистрирует OB-колбэки на высоте 321123 и перехватывает операции OB_OPERATION_HANDLE_CREATE и OB_OPERATION_HANDLE_DUPLICATE. Когда процесс пытается открыть дескриптор к защищённой цели, мы проверяем имя исходного процесса по белому списку (explorer.exe, csrss.exe, svchost.exe, MsMpEng.exe, RuntimeBroker.exe) и удаляем права доступа, если процесс не разрешён.
Текущая реализация использует сопоставление по имени с помощью PsGetProcessImageFileName (недокументированный API). Список защиты управляется через связный список с синхронизацией спинлоком. Рабочий поток существует, но логика обновления ещё не реализована.
Компонент пользовательского режима предоставляет GUI на ImGui для добавления процессов в список защиты. Взаимодействие происходит через IOCTL-коды по симлинку \\Device\\Aegis с использованием передачи METHOD_BUFFERED. Драйвер создаёт симлинк от \\DosDevices\\Aegis к \\Device\\Aegis, чтобы пользовательский режим мог обращаться к нему через \\\\.\\Aegis.
Ядро (km/):
driver.c — DriverEntry, регистрация OB, диспетчер IOCTL, создание устройства/симлинкаprotect_manager/ — Управление списком процессов, рабочий поток (логика обновления в ожидании)Пользовательский режим (um/):
client.cpp — Главный цикл, отрисовка ImGui, управление окнамиgui/ — Настройка DirectX 11 + ImGui, пользовательские шрифты (Gilroy)service_manager/ — Интеграция SCM для загрузки/выгрузки драйвераioctl_manager/ — Обёртка DeviceIoControl для взаимодействия с драйвером321123 (стандарт для сторонних драйверов 321000+, установлено выше для избежания коллизий)PsGetProcessImageFileName — недокументированный API, возвращающий только имя .exe-файла (макс. 15 символов)KSPIN_LOCK на уровне DISPATCH_LEVEL (IRQL 2) для предотвращения состояний гонкиNonPagedPool для protect_manager (всегда в ОЗУ, более быстрый доступ, ограниченный пул)PagedPool для отдельных структур процессов (могут быть выгружены)KEVENT для асинхронной обработки (пока заглушка)METHOD_BUFFERED для безопасной обработки буферов, тип устройства 0x8000 (диапазон сторонних драйверов)SERVICE_DEMAND_STARTDeviceIoControl с IOCTL-кодами, определёнными в общем ioctl.hРегистрация OB-колбэка:
// Register OB callbacks for handle creation/duplication
OB_CALLBACK_REGISTRATION ob_registration = { 0 };
OB_OPERATION_REGISTRATION op_registration = { 0 };
RtlSecureZeroMemory(&ob_registration, sizeof(ob_registration));
RtlSecureZeroMemory(&op_registration, sizeof(op_registration));
op_registration.ObjectType = PsProcessType;
op_registration.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
// we care about duplication because smart hackers or malware, often do not create handles
// but duplicate current ones to not arouse suspicion
op_registration.PreOperation = pre_open_process;
RtlInitUnicodeString(&ob_registration.Altitude, L"321123");
// 321000 is standard for 3rd party drivers, we set it a bit higher to not have collision
status = ObRegisterCallbacks(&ob_registration, ®_handle);
Удаление прав доступа к дескриптору:
OB_PREOP_CALLBACK_STATUS pre_open_process(PVOID reg_context, POB_PRE_OPERATION_INFORMATION info)
{
if (info->ObjectType == *PsProcessType) {
PEPROCESS proc = (PEPROCESS)info->Object;
char* process_name = (char*)PsGetProcessImageFileName(proc);
// Lock list at DISPATCH_LEVEL to prevent BSOD
KIRQL irql;
KeAcquireSpinLock(&g_protect_manager->list_lock, &irql);
// Check if process is in protection list
for (entry = g_protect_manager->process_list.Flink;
entry != &g_protect_manager->process_list;
entry = entry->Flink) {
pprocess proces = CONTAINING_RECORD(entry, process, entry);
if (strcmp(proces->path, process_name) == 0) {
// Check source process - allow system processes
PEPROCESS source_process = IoGetCurrentProcess();
char* source_name = (char*)PsGetProcessImageFileName(source_process);
if (strcmp(source_name, "explorer.exe") == 0 ||
strcmp(source_name, "csrss.exe") == 0 || ...) {
KeReleaseSpinLock(&g_protect_manager->list_lock, irql);
return OB_PREOP_SUCCESS;
}
// Strip VM_READ, VM_WRITE, VM_OPERATION (0x1, 0x10, 0x20)
// We don't set to 0 because it can cause bugs
info->Parameters->CreateHandleInformation.DesiredAccess &= ~(0x1 | 0x10 | 0x20);
KeReleaseSpinLock(&g_protect_manager->list_lock, irql);
return OB_PREOP_SUCCESS;
}
}
KeReleaseSpinLock(&g_protect_manager->list_lock, irql);
}
return OB_PREOP_SUCCESS;
}
Управление списком процессов:
NTSTATUS protect_manager_protect_process(pprotect_manager self, pprocess process)
{
// KIRQL (Kernel Interrupt Request Level) 0-31 tells CPU to not disturb
// 0 = PASSIVE_LEVEL, 2 = DISPATCH_LEVEL, 31 = HIGH_LEVEL
KIRQL irql;
// Acquire spinlock, raises IRQL to DISPATCH_LEVEL (2)
// Locks the list so it doesn't change while we edit
KeAcquireSpinLock(&self->list_lock, &irql);
InsertTailList(&self->process_list, &process->entry);
KeReleaseSpinLock(&self->list_lock, &irql);
// Signal worker thread to refresh
self->refresh_needed = TRUE;
KeSetEvent(&self->worker_event, 0, FALSE); // FALSE = auto revert IRQL
return STATUS_SUCCESS;
}
Взаимодействие IOCTL из пользовательского режима:
bool ioctl_manager::check(std::string name)
{
// Open driver device via symlink \\\\.\\Aegis -> \\Device\\Aegis
this->driver_object = CreateFile(this->path, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
// Send process name to driver via IOCTL
result = DeviceIoControl(this->driver_object, IOCTL_PROTECT_PID,
(LPVOID)data.c_str(), data.size() + 1,
NULL, 0, &bytes_returned, NULL);
CloseHandle(this->driver_object); // don't forget to close handles
return result;
}
Весь код тщательно прокомментирован с объяснениями концепций ядра, уровней IRQL, управления памятью и возможных проблем. Не стесняйтесь изучать исходники — они образовательные и хорошо документированы.
bcdedit /set testsigning on)Результаты сборки: km.sys (драйвер) и um.exe (приложение пользовательского режима).
OB_OPERATION_HANDLE_CREATE, возможны атаки дублированияprotect_manager.c пустаExAllocatePool вместо ExAllocatePool2PsActiveProcessHead, скрытие драйвера из PsLoadedModuleListТолько в образовательных целях. Используются недокументированные API, которые могут сломаться при обновлениях Windows. Требуется тестовая подпись или правильная подпись кода для запуска.