
Controlador de protección de procesos en modo kernel con GUI de usuario
Controlador de protección de procesos en modo kernel que utiliza ObRegisterCallbacks para eliminar permisos de acceso a memoria de los handles. Implementación básica que intercepta la creación de handles y elimina PROCESS_VM_READ/WRITE/OPERATION de intentos de acceso no autorizados.


Nota: Si el GIF no se anima, míralo directamente en Imgur
El controlador registra callbacks OB en la altitud 321123 e intercepta las operaciones OB_OPERATION_HANDLE_CREATE y OB_OPERATION_HANDLE_DUPLICATE. Cuando un proceso intenta abrir un handle hacia un objetivo protegido, verificamos el nombre del proceso fuente contra una lista blanca (explorer.exe, csrss.exe, svchost.exe, MsMpEng.exe, RuntimeBroker.exe) y eliminamos los derechos de acceso si no está permitido.
La implementación actual utiliza coincidencia basada en nombres mediante (API no documentada). La lista de protección se gestiona a través de una lista enlazada con sincronización mediante spinlock. Existe un hilo de trabajo pero la lógica de actualización aún no está implementada.
PsGetProcessImageFileNameEl componente de modo usuario proporciona una GUI con ImGui para agregar procesos a la lista de protección. La comunicación se realiza mediante códigos IOCTL a través del enlace simbólico \\Device\\Aegis utilizando transferencia METHOD_BUFFERED. El controlador crea un enlace simbólico de \\DosDevices\\Aegis a \\Device\\Aegis para que el modo usuario pueda acceder a él mediante \\\\.\\Aegis.
Kernel (km/):
driver.c - Entrada del controlador, registro OB, despachador IOCTL, creación de dispositivo/enlace simbólicoprotect_manager/ - Gestión de lista de procesos, hilo de trabajo (lógica de actualización pendiente)Modo usuario (um/):
client.cpp - Bucle principal, renderizado ImGui, gestión de ventanasgui/ - Configuración de DirectX 11 + ImGui, fuentes personalizadas (Gilroy)service_manager/ - Integración SCM para cargar/descargar el controladorioctl_manager/ - Envoltorio DeviceIoControl para comunicación con el controlador321123 (el estándar de terceros es 321000+, se establece más alto para evitar colisiones)PsGetProcessImageFileName - API no documentada que devuelve solo el nombre del archivo .exe (máx. 15 caracteres)KSPIN_LOCK en DISPATCH_LEVEL (IRQL 2) para prevenir condiciones de carreraNonPagedPool para protect_manager (siempre en RAM, acceso más rápido, pool limitado)PagedPool para estructuras de proceso individuales (pueden ser paginadas fuera)KEVENT para procesamiento asíncrono (actualmente es un stub)METHOD_BUFFERED para manejo seguro de búfer, tipo de dispositivo 0x8000 (rango de terceros)SERVICE_DEMAND_STARTDeviceIoControl con códigos IOCTL definidos en ioctl.h compartidoRegistro de callback 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);
Eliminación de acceso a handles:
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;
}
Gestión de lista de procesos:
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;
}
Comunicación IOCTL en modo usuario:
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;
}
Todo el código está completamente comentado con explicaciones de conceptos del kernel, niveles IRQL, gestión de memoria y posibles problemas. Siéntete libre de revisar el código fuente: es educativo y está bien documentado.
bcdedit /set testsigning on)Salidas de compilación: km.sys (controlador) y um.exe (aplicación de modo usuario).
OB_OPERATION_HANDLE_CREATE, son posibles ataques de duplicaciónprotect_manager.c está vacíaExAllocatePool en lugar de ExAllocatePool2PsActiveProcessHead, ocultación de controladores desde PsLoadedModuleListSolo con fines educativos. Utiliza APIs no documentadas que pueden romperse con actualizaciones de Windows. Requiere firma de prueba o firma de código adecuada para ejecutarse.