Need help using PsSetLoadImageNotifyRoutine

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;
}

Bad design of your code. No synchronous at all. Remember you load image
callback routine will be called once an image is loaded. It could be a user
mode app or a kernel mode driver. If the newly loading image is a driver,
then the ProcessId handle is always 0. I think you should maintain a table
to keep all these information.

Another thing, you can only trust the ProcessId in the first load image
callback for one specific process.

On Wed, Sep 16, 2009 at 4:17 AM, wrote:

> 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 <br>> 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;
> }
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule of debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

Hi Michael,

Thanks for your comments. It is my first driver so I am pretty much fumbling in the dark here, which naturally affects the design. However, I would like to take your suggestions into account. Could your elaborate a little more about your remark about “no synchronous at all”. Do you hereby mean that my approach is asynchronous do to the IoCreateNotificationEvent calls and why this is a bad thing?

Regarding maintaining a table of which images are being loaded, I think I understand what you mean. Since the callback routine for PsSetLoadImageNotifyRoutine would be called every time an image is loaded into memory, my idea was to let the callback routine send the process information to user-mode and filter it there. But right now I struggle with getting a callback upon image load. It has been suggested in another post that registering a callback for IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION when using PsSetLoadImageNotifyRoutine, could be useful. Currently, I only register for IRP_MJ_CREATE.

OK. In you load image callback routine after you set the
ProcessLoadImageEvent you simply clear it right away. At this point how do
you know that your application has already read the data back? What will
happen if another image is loaded right at this point? The old data in your
device extension may be overwritten by the new data. Then your application
may copy the wrong data back. You need to synchronize your load image
callback routine and your IOCTL_PROCVIEW_GET_IMAGEINFO IOCTL dispatch
routine.

Since you are just focusing on user mode image, so it is better to add the
following line of code in you load image callback.

if (!ProcessId) {
return;
}

On Thu, Sep 17, 2009 at 4:00 AM, wrote:

> Hi Michael,
>
> Thanks for your comments. It is my first driver so I am pretty much
> fumbling in the dark here, which naturally affects the design. However, I
> would like to take your suggestions into account. Could your elaborate a
> little more about your remark about “no synchronous at all”. Do you hereby
> mean that my approach is asynchronous do to the IoCreateNotificationEvent
> calls and why this is a bad thing?
>
> Regarding maintaining a table of which images are being loaded, I think I
> understand what you mean. Since the callback routine for
> PsSetLoadImageNotifyRoutine would be called every time an image is loaded
> into memory, my idea was to let the callback routine send the process
> information to user-mode and filter it there. But right now I struggle with
> getting a callback upon image load. It has been suggested in another post
> that registering a callback for IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
> when using PsSetLoadImageNotifyRoutine, could be useful. Currently, I only
> register for IRP_MJ_CREATE.
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule of debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

Hi Michael,

Thanks again for your reply. I have done what you said and verified that the load image callback routine gets called. However, when I load an image (starts a program) it seems that the case IOCTL_PROCVIEW_GET_IMAGEINFO in the IOCTL dispatch routine never enters. I have simply put an DbgPrint statement after the IOCTL_PROCVIEW_GET_IMAGEINFO case statement and connected a kernel debugger. Is there something wrong with the way I define the IOCTL_PROCVIEW_GET_IMAGEINFO control statement?

Cheers
Brian

How you trigger your application to send down IOCTL_PROCVIEW_GET_IMAGEINFO?
By waiting for extension->ProcessLoadImageEvent? Then how you share this
event object between your app and your driver?

On Mon, Sep 21, 2009 at 4:52 AM, wrote:

> Hi Michael,
>
> Thanks again for your reply. I have done what you said and verified that
> the load image callback routine gets called. However, when I load an image
> (starts a program) it seems that the case IOCTL_PROCVIEW_GET_IMAGEINFO in
> the IOCTL dispatch routine never enters. I have simply put an DbgPrint
> statement after the IOCTL_PROCVIEW_GET_IMAGEINFO case statement and
> connected a kernel debugger. Is there something wrong with the way I define
> the IOCTL_PROCVIEW_GET_IMAGEINFO control statement?
>
> Cheers
> Brian
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule of debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>