I am creating a filter driver with the purpose of preventing certain processes from executing. The way I currently do it, is by using PsSetCreateProcessNotifyRoutine to get a notification when the process actually executes and extract the name of the process and terminate it if it is included in a list of “marked” processes. I know that this is not the best approach, since it could easily be avoided by renaming the process, but this is not the primary concern at this point.
However, this approach is not good enough since the process actually gets created before being terminated. Instead, I would like to use the PsSetLoadImageNotifyRoutine to get a notification when a process is mapped into memory and decide whether it should be terminated using the strategy described above. However, I can not get it to work with PsSetLoadImageNotifyRoutine. I have done a lot of searching to find a simple example of how to use PsSetLoadImageNotifyRoutine unfortunately without any success.
The way I currently do it is by using the following
// Kernel-mode
#define FILE_DEVICE_UNKNOWN 0x00000022
#define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN
#define IOCTL_PROCVIEW_GET_IMAGEINFO \
CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
typedef struct _ProcessLoadImageCallbackInfo {
CHAR ImageNameA[255];
HANDLE IProcessId;
PIMAGE_INFO ImageInfo;
} PROCESS_LOAD_IMAGE_CALLBACK_INFO,*PPROCESS_LOAD_IMAGE_CALLBACK_INFO;
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT DeviceObject;
HANDLE ProcessLoadImageHandle;
PKEVENT ProcessLoadImageEvent;
HANDLE PParentId;
HANDLE PProcessId;
BOOLEAN PCreate;
CHAR ImageNameA[255];
HANDLE IProcessId;
IMAGE_INFO ImageInfo;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
// …
DriverObject->DriverUnload = UnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
RtlInitUnicodeString( &uszProcessLoadImageEventString, L"\BaseNamedObjects \ProcessLoadImageEvent" );
extension->ProcessLoadImageEvent = IoCreateNotificationEvent( &uszProcessLoadImageEventString, &extension->ProcessLoadImageHandle );
ntStatus = PsSetLoadImageNotifyRoutine(ProcessLoadImageCallback);
// …
}
VOID ProcessLoadImageCallback(
IN PUNICODE_STRING FullImageName,
IN HANDLE ProcessId,
IN PIMAGE_INFO ImageInfo
)
{
PDEVICE_EXTENSION extension;
// Assign extension variable
extension = g_pDeviceObject->DeviceExtension;
// User-mode apps will pick it up using DeviceIoControl calls.
extension->IProcessId = ProcessId;
RtlCopyMemory( &extension->ImageInfo, ImageInfo, sizeof(IMAGE_INFO) );
sprintf( extension->ImageNameA, “%S”, FullImageName->Buffer );
KeSetEvent( extension->ProcessLoadImageEvent, 0, FALSE );
KeClearEvent( extension->ProcessLoadImageEvent );
}
NTSTATUS DispatchIoctl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
PPROCESS_LOAD_IMAGE_CALLBACK_INFO pProcessLoadImageCallbackInfo;
switch(irpStack->Parameters.DeviceIoControl.IoControlCode) {
//…
case IOCTL_PROCVIEW_GET_IMAGEINFO:
{
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(PROCESS_LOAD_IMAGE_CALLBACK_INFO) ) {
pProcessLoadImageCallbackInfo = Irp->AssociatedIrp.SystemBuffer;
strcpy( pProcessLoadImageCallbackInfo->ImageNameA, extension->ImageNameA );
pProcessLoadImageCallbackInfo->IProcessId = extension->IProcessId;
RtlCopyMemory( &pProcessLoadImageCallbackInfo->ImageInfo, &extension->ImageInfo, sizeof(IMAGE_INFO) );
ntStatus = STATUS_SUCCESS;
}
break;
}
default:
break;
}
Irp->IoStatus.Status = ntStatus;
if ( ntStatus == STATUS_SUCCESS) {
Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
}
else {
Irp->IoStatus.Information = 0;
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}