Windows - Blocking Process Creation

07 Apr, 21
Tags:
51
5
0
ACCESS_DENIED

Windows Supplies drivers with multiple callbacks to get notified about events happening in the system. One of them, as well as the only one that allows blocking, is the process notify routine. It alert all the drivers that are registered to it about process creation and termination.

There are 3 possible ways to register to those callbacks:

  1. PsSetCreateProcessNotifyRoutine - this function takes a callback of type PCREATE_PROCESS_NOTIFY_ROUTINE, which receives the least amount of data from the system. It also doesn't supply drivers with an easy way to block processes from starting. The only benefit of this function is that it exists since Windows 2000. But if your driver is meant to run on Windows Vista and later (and hopefully it does), you should use one of the other options:
  2. PsSetCreateProcessNotifyRoutineEx - this function receives a callback of type PCREATE_PROCESS_NOTIFY_ROUTINE_EX, which gets called for every process that gets created or terminated and receives the EPROCESS structure of the new process, a handle to the parent process and, if the process is being created, a PS_CREATE_NOTIFY_INFO structure, containing some information about the new process. This function exists since Windows Vista and allows blocking the new process.
  3. PsSetCreateProcessNotifyRoutineEx2 - this function adds support for WSL processnotifications. It receives the same arguments as the previous one, as well as a PSCREATEPROCESSNOTIFYTYPE value that indicates the type of enumeration. There is only one valid value - PsCreateProcessNotifySubsystem (0). This will cause the system to notify the driver about both regular Win32 processes and WSL processes. However, this function only exists since Windows 10 RedStone2, so drivers that want to use this functionality will usually need to support both the Ex and Ex2 functions, depending on the build the driver is running on.

The registered callbacks get added to an internal array in the kernel and must be unregistered before the driver unloads using the same function it was registered with and with the Remove argument set to True.

A callback function of type PCREATE_PROCESS_NOTIFY_ROUTINE_EX receives as its 3rd argument a pointer to a PS_CREATE_NOTIFY_INFO structure. This structure will only be sent if the process is being created (will be NULL for terminating processes) and it contains the new process' file name, command line, and several other useful fields. It also contains a CreationStatus field, set to STATUS_SUCCESS by default. The driver can choose to set this field to an error code, causing the process to immediately terminate with this error code.

In this example the driver registers a process notify routine and will block all processes that have an ImageFileName of \\??\\C:\\WINDOWS\\system32\\calc.exe with STATUS_ACCESS_DENIED:

#include <ntddk.h>

EXTERN_C_START
DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD DriverUnload;
EXTERN_C_END

DECLARE_CONST_UNICODE_STRING(k_calc, L"\\??\\C:\\WINDOWS\\system32\\calc.exe");

VOID
ProcessNotifyRoutine (
    _Inout_ PEPROCESS Process,
    _In_ HANDLE ProcessId,
    _In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
    UNREFERENCED_PARAMETER(Process);
    UNREFERENCED_PARAMETER(ProcessId);

    if (CreateInfo != nullptr)
    {
        if (!RtlCompareUnicodeString(CreateInfo->ImageFileName, &k_calc, TRUE))
        {
            CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
        }
    }
}

VOID
DriverUnload (
    _In_ PDRIVER_OBJECT DriverObject
)
{
    UNREFERENCED_PARAMETER(DriverObject);

    PsSetCreateProcessNotifyRoutineEx(ProcessNotifyRoutine, TRUE);
}


NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)
{
    UNREFERENCED_PARAMETER(RegistryPath);

    DriverObject->DriverUnload = DriverUnload;
    return PsSetCreateProcessNotifyRoutineEx(ProcessNotifyRoutine, FALSE);
}