Hooking fast i/o filesystem callbacks in Win 2K & below

(sorry for the change of the subject of this thread, I thought it is
appropriate)
Hooking NtCreate section - or any NT APIs is a bad idea. These are
internal undocumented APIs, mimicking their behaviour is tough to get
right, and moreover they are subject to revision in future versions
which can break your product badly.

Since many people have been expressing interest on how to hook the fast
i/o callbacks into the filesystem in Win 2K & below, here’s an example
below of a reasonably safe way of doing it, while being a good citizen
with respect to other filters in the system who might wish to do the
same.

For Windows XP, filters should use
FsRtlRegisterFilesystemFilterCallbacks() to receive callbacks for the
below routines automatically. (Do NOT use the below method on XP. You
may have to #ifdef code appropriately and I can understand it’s yucky,
but this is strictly only for Win 2K & below)

AcquireFileForNtCreateSection
ReleaseFileForNtCreateSection
AcquireForModWrite
ReleaseForModWrite
AcquireForCcFlush
ReleaseForCcFlush

For Windows 2000 & below, there was no way to register for these. Cc &
Mm directly called in to the base filesystem. If filters really, really
want to hook them in these OS versions, here are the guidelines:

1.) First of all these guidelines & the technique presented below is
ONLY targeted towards local filesystems. There are complications when
you are filtering remote filesystems that I will have to address
separately in another email if there’s interest.

2 .) Hook them in the FsNotification() callback for file system
registration notification

3.) Hook the Release*() callback before the corresponding Acquire*()
callback. This eliminates the window where their acquire can be called
without a corresponding release.

4 .) When mangling the file system’s dispatch table they need to
remember & directly replace the appropriate FastIo dispatch in the base
filesystem’s driver object. NOT the immediate lower device object. This
will ensure if there are multiple filters who hook it, it’ll chain
right.

5 .) When their dispatch is called, they need to do their work &
then call the routine that they replaced in step 3.)
6.) This is more of a note: in Windows 2K & below you cannot fail
the AcquireFileForNtCreateSection callback regardless - XP allows u to
fail it.

Here’s an example of the right way to hook it. This will hook it in the
FsNotification() routine that is called when a filesystem registers
itself. The below example shows how to hook the
AcquireFileForNtCreateSection/ReleaseFileForNtCreateSection callbacks.
The others can be similarily hooked.

Once hooked, the old routines are remembered in OldAcquire* , and
OldRelease* function pointers.

When the filter’s Acquire*(), Release*() are called, they should simply
do their work and then proceed to call the OldAcquireFile*() and
OldRelease*() functions appropriately.

VOID
MyFsNotification(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN FsActive
)

{

if (FsActive) {

PDEVICE_EXTENSION deviceExtension;

//
// The file system has registered as an active file system. If it
is
// a disk-based file system attach to it.
//

status = IoCreateDevice(
MyFsDriverObject,
sizeof( DEVICE_EXTENSION ),
(PUNICODE_STRING) NULL,
FILE_DEVICE_DISK_FILE_SYSTEM,
0,
FALSE,
&myDeviceObject
);

if (NT_SUCCESS( status )) {

deviceExtension = myDeviceObject->DeviceExtension;

fsDevice = deviceExtension->FileSystemDeviceObject =
IoAttachDeviceToDeviceStack(myDeviceObject,
DeviceObject);

} else {

//
// Error
//

return;
}

//
// Hook Release first: to hook we have to remember the old
dispatch and set the new one atomically.
// This is achieved by using an interlocked compare exchange.
//

ASSERT ((DeviceObject->DriverObject->FastIoDispatch != NULL) &&

(DeviceObject->DriverObject->FastIoDispatch->SizeOfFastIoDispatch >
FIELD_OFFSET(FAST_IO_DISPATCH,
ReleaseFileForNtCreateSection)) &&

(DeviceObject->DriverObject->FastIoDispatch->ReleaseFileForNtCreateSecti
on != NULL));

do {

//
// Save the old dispatch
//

deviceExtension-> OldReleaseForNtCreateSection =
DeviceObject->DriverObject->FastIoDispatch->ReleaseFileForNtCreateSectio
n;

} while ( InterlockedCompareExchangePointer(

&(DeviceObject->DriverObject->FastIoDispatch->ReleaseFileForNtCreateSect
ion),
MyFastIoReleaseFileForNtCreateSection,
deviceExtension->OldReleaseForNtCreateSection) !=
deviceExtension-> OldReleaseForNtCreateSection );

//
// Hook in our FastIoDispatch for FsRtlAcquireFileExclusive
callback
//

ASSERT ((DeviceObject->DriverObject->FastIoDispatch != NULL) &&

(DeviceObject->DriverObject->FastIoDispatch->SizeOfFastIoDispatch >
FIELD_OFFSET(FAST_IO_DISPATCH,
AcquireFileForNtCreateSection)) &&

(DeviceObject->DriverObject->FastIoDispatch->AcquireFileForNtCreateSecti
on != NULL));
do {

//
// Save the old dispatch
//

deviceExtension->OldAcquireForNtCreateSection =
DeviceObject->DriverObject->FastIoDispatch->AcquireFileForNtCreateSectio
n;

} while ( InterlockedCompareExchangePointer(

&(DeviceObject->DriverObject->FastIoDispatch->AcquireFileForNtCreateSect
ion),
MyFastIoAcquireFileForNtCreateSection,
deviceExtension->OldAcquireForNtCreateSection ) !=
deviceExtension->OldAcquireForNtCreateSection );

}


}

Here’s an example of how to locate your device object in the hooked
routine & also how to pass the call down to the filter below you.
Note that this method of locating your device object is only needed for
the AcquireForNtCreateSection/ReleaseForNtCreateSection callbacks.
The rest of the callbacks have the device object passed in, which can be
directly used.

VOID
MyFastIoAcquireFileForNtCreateSection(
IN PFILE_OBJECT FileObject
)
{
PDEVICE_OBJECT deviceObject, fsDeviceObject;
BOOLEAN found = FALSE;

fsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
for (deviceObject = fsDeviceObject->AttachedDevice;
deviceObject != NULL ; deviceObject = deviceObject->AttachedDevice) {
//
// Assume that driver object for this filter is stored
in a global MyDriverObject
//

if (deviceObject->DriverObject == MyDriverObject) {

//
// Located our device object
//
found = TRUE;
break;
}

}

//
// Since this filter only filters local filesystems we should
ALWAYS be able to locate our device object
// on the stack. Hence the assert below. For remote filesystems it
is possible that the base fs device
// object is owned by MUP instead of the RDR that we actually
attached to, hence this assert can fire
// if this filter attempted to ever filter a remote file system.
Which this filter is not designed to…

ASSERT( found == TRUE );
ASSERT( deviceObject->DeviceExtension != NULL );

//
// deviceObject now points to our device.
// Do any relevant pre-processing
//

return deviceObject->DeviceExtension->
OldAcquireForNtCreateSection( FileObject );
}

This posting is provided “AS IS” with no warranties, and confers no
rights. You assume all risk for your use

-----Original Message-----
From: Nicholas Ryan [mailto:xxxxx@secretseal.com]
Sent: Monday, January 14, 2002 6:41 PM
To: File Systems Developers
Subject: [ntfsd] RE: Hook CreateFileMapping

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-ntfsd-
xxxxx@lists.osr.com] On Behalf Of Maxim S. Shatskih
Sent: Monday, January 14, 2002 5:17 PM
To: File Systems Developers
Subject: [ntfsd] RE: Hook CreateFileMapping

> An alternative is to use the www.sysinternals.com method for hooking
NT
> system services, then hook NtCreateSection and NtMapViewOfSection to

> track all (user-mode initiated) file mappings. (BTW sports fans, is
> there a semi/documented way to turn a virtual address into the file
> object for the file mapping it represents?)

Two great tasks:

  • getting the control area pointer from the VAD.
  • then getting the file object from the control area.

Ouch… but then again my only alternative is to hook both
NtCreateSection and NtMapViewOfSection and maintain a massive private
data structure tracking all mapped views in all processes (I’m only
interested in user-mode initiated mappings).

> object itself to cause the AcquireFileForNtCreateSection callback to

This call is always called in base FS device object, the filter’s
routine is never used.

But if you actually modify the AcquireFileForNtCreateSection member of
the fast-I/O dispatch table of the driver object of the FSD to point to
your routine, then your routine will be called, right? You just have to
be careful to chain down to the original routine.

  • Nicholas Ryan

You are currently subscribed to ntfsd as: xxxxx@windows.microsoft.com
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com