http://blog.csdn.net/xiongwjw/article/category/1081656
//======================================================================
//
// Filemon.c
//
// Sysinternals - www.sysinternals.com
// Copyright (C) 1996-2001 Mark Russinovich and Bryce Cogswell
//
// Passthrough file system filter device driver.
//
// Notes: The reason that we use NonPagedPool even though the driver
// only accesses allocated buffer at PASSIVE_LEVEL, is that touching
// a paged pool buffer can generate a page fault, and if the paging
// file is on a drive that filemon is monitoring filemon would be
// reentered to handle the page fault. We want to avoid that and so
// we only use nonpaged pool.
//
//======================================================================
#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "..\exe\ioctlcmd.h"
#include "filemon.h"
//----------------------------------------------------------------------
// F O R W A R D D E C L A R A T I O N S
//----------------------------------------------------------------------
//
// These are prototypes for Filemon's Fast I/O hooks. The originals
// prototypes can be found in NTDDK.H
//
BOOLEAN
FilemonFastIoCheckifPossible(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
IN BOOLEAN CheckForReadOperation,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoRead(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
IN PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoQueryBasicInfo(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
OUT PFILE_BASIC_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoQueryStandardInfo(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
OUT PFILE_STANDARD_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoLock(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PLARGE_INTEGER Length,
PEPROCESS ProcessId,
ULONG Key,
BOOLEAN FailImmediately,
BOOLEAN ExclusiveLock,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoUnlockSingle(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PLARGE_INTEGER Length,
PEPROCESS ProcessId,
ULONG Key,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoUnlockAll(
IN PFILE_OBJECT FileObject,
PEPROCESS ProcessId,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoUnlockAllByKey(
IN PFILE_OBJECT FileObject,
PEPROCESS ProcessId, ULONG Key,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoDeviceControl(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutbufBuffer,
IN ULONG OutputBufferLength,
IN ULONG IoControlCode,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
VOID
FilemonFastIoAcquireFile(
PFILE_OBJECT FileObject
);
VOID
FilemonFastIoReleaseFile(
PFILE_OBJECT FileObject
);
VOID
FilemonFastIoDetachDevice(
PDEVICE_OBJECT SourceDevice,
PDEVICE_OBJECT TargetDevice
);
//
// These are new NT 4.0 Fast I/O calls
//
BOOLEAN
FilemonFastIoQueryNetworkOpenInfo(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
OUT struct _FILE_NETWORK_OPEN_INFORMATION *Buffer,
OUT struct _IO_STATUS_BLOCK *IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
NTSTATUS
FilemonFastIoAcquireForModWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER EndingOffset,
OUT struct _ERESOURCE **ResourceToRelease,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoMdlRead(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoMdlReadComplete(
IN PFILE_OBJECT FileObject,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoPrepareMdlWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoMdlWriteComplete(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoReadCompressed(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
OUT struct _COMPRESSED_DATA_INFO *CompressedDataInfo,
IN ULONG CompressedDataInfoLength,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoWriteCompressed(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
IN PVOID Buffer,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
IN struct _COMPRESSED_DATA_INFO *CompressedDataInfo,
IN ULONG CompressedDataInfoLength,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoMdlReadCompleteCompressed(
IN PFILE_OBJECT FileObject,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoMdlWriteCompleteCompressed(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
FilemonFastIoQueryOpen(
IN struct _IRP *Irp,
OUT PFILE_NETWORK_OPEN_INFORMATION NetworkInformation,
IN PDEVICE_OBJECT DeviceObject
);
NTSTATUS
FilemonFastIoReleaseForModWrite(
IN PFILE_OBJECT FileObject,
IN struct _ERESOURCE *ResourceToRelease,
IN PDEVICE_OBJECT DeviceObject
);
NTSTATUS
FilemonFastIoAcquireForCcFlush(
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject
);
NTSTATUS
FilemonFastIoReleaseForCcFlush(
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject
);
BOOLEAN
ApplyFilters(
PCHAR Text
);
//
// Unload routine (debug builds only)
//
VOID
FilemonUnload(
IN PDRIVER_OBJECT DriverObject
);
//----------------------------------------------------------------------
// G L O B A L S
//----------------------------------------------------------------------
//
// This is our Driver Object
//
PDRIVER_OBJECT FilemonDriver;
//
// Indicates if the GUI wants activity to be logged
//
BOOLEAN FilterOn = FALSE;
//
// Global filter (sent to us by the GUI)
//
FILTER FilterDef;
//
// This lock protects access to the filter array
//
ERESOURCE FilterResource;
//
// Array of process and path filters
//
ULONG NumIncludeFilters = 0;
PCHAR IncludeFilters[MAXFILTERS];
ULONG NumExcludeFilters = 0;
PCHAR ExcludeFilters[MAXFILTERS];
//
// Once a load is initiated, this flag prevents the processing of
// further IRPs. This is required because an unload can only take
// place if there are any IRP's for which an IoCompletion has
// been registered that has not actually completed.
//
BOOLEAN UnloadInProgress = FALSE;
//
// This is the offset into a KPEB of the current process name. This is determined
// dynamically by scanning the process block belonging to the GUI for the name
// of the system process, in who's context we execute in DriverEntry
//
ULONG ProcessNameOffset;
//
// This variable keeps track of the outstanding IRPs (ones for which
// a completion routine has been registered, but that have not yet
// passed through the completion routine), which is used in
// the unload determination logic. The CountMutex protects data
// races on updating the count.
//
#if DBG
ULONG OutstandingIRPCount = 0;
#endif // DBG
KSPIN_LOCK CountMutex;
//
// Table of our hook devices for each drive letter. This makes it
// easy to look up the device object that was created to hook a
// particular drive.
//
PDEVICE_OBJECT DriveHookDevices[26];
//
// Current bitmask of hooked drives
//
ULONG CurrentDriveSet = 0;
//
// The special file system hook devices
//
PDEVICE_OBJECT NamedPipeHookDevice = NULL;
PDEVICE_OBJECT MailSlotHookDevice = NULL;
//
// Hash table for keeping names around. This is necessary because
// at any time the name information in the fileobjects that we
// see can be deallocated and reused. If we want to print accurate
// names, we need to keep them around ourselves.
//
PHASH_ENTRY HashTable[NUMHASH];
//
// Reader/Writer lock to protect hash table.
//
ERESOURCE HashResource;
//
// The current output buffer
//
PLOG_BUF CurrentLog = NULL;
//
// Each IRP is given a sequence number. This allows the return status
// of an IRP, which is obtained in the completion routine, to be
// associated with the IRPs parameters that were extracted in the Dispatch
// routine.
//
ULONG Sequence = 0;
//
// This mutex protects the output buffer
//
FAST_MUTEX LogMutex;
//
// Filemon keeps track of the number of distinct output buffers that
// have been allocated, but not yet uploaded to the GUI, and caps
// the amount of memory (which is in non-paged pool) it takes at
// 1MB.
//
ULONG NumLog = 0;
ULONG MaxLog = (1024 * 1024) / LOGBUFSIZE;
//
// Full path name lookaside for dispatch entry
//
NPAGED_LOOKASIDE_LIST FullPathLookaside;
//
// We use this string for a path name when we're out of resources
//
CHAR InsufficientResources[] = "";
//
// These are the text representations of the classes of IRP_MJ_SET/GET_INFORMATION
// calls
//
CHAR *FileInformation[] =
{
"",
"FileDirectoryInformation",
"FileFullDirectoryInformation",
"FileBothDirectoryInformation",
"FileBasicInformation",
"FileStandardInformation",
"FileInternalInformation",
"FileEaInformation",
"FileAccessInformation",
"FileNameInformation",
"FileRenameInformation",
"FileLinkInformation",
"FileNamesInformation",
"FileDispositionInformation",
"FilePositionInformation",
"FileFullEaInformation",
"FileModeInformation",
"FileAlignmentInformation",
"FileAllInformation",
"FileAllocationInformation",
"FileEndOfFileInformation",
"FileAlternateNameInformation",
"FileStreamInformation",
"FilePipeInformation",
"FilePipeLocalInformation",
"FilePipeRemoteInformation",
"FileMailslotQueryInformation",
"FileMailslotSetInformation",
"FileCompressionInformation",
"FileCopyOnWriteInformation",
"FileCompletionInformation",
"FileMoveClusterInformation",
"FileOleClassIdInformation",
"FileOleStateBitsInformation",
"FileNetworkOpenInformation",
"FileObjectIdInformation",
"FileOleAllInformation",
"FileOleDirectoryInformation",
"FileContentIndexInformation",
"FileInheritContentIndexInformation",
"FileOleInformation",
"FileMaximumInformation",
};
//
// These are textual representations of the IRP_MJ_SET/GET_VOLUME_INFORMATION
// classes
//
CHAR *VolumeInformation[] =
{
"",
"FileFsVolumeInformation",
"FileFsLabelInformation",
"FileFsSizeInformation",
"FileFsDeviceInformation",
"FileFsAttributeInformation",
"FileFsQuotaQueryInformation",
"FileFsQuotaSetInformation",
"FileFsControlQueryInformation",
"FileFsControlSetInformation",
"FileFsMaximumInformation",
};
//
// These are Win2K Plug-and-Play minor IRP codes
//
CHAR *PnpMinorCode[] =
{
"IRP_MN_START_DEVICE",
"IRP_MN_QUERY_REMOVE_DEVICE",
"IRP_MN_REMOVE_DEVICE",
"IRP_MN_CANCEL_REMOVE_DEVICE",
"IRP_MN_STOP_DEVICE",
"IRP_MN_QUERY_STOP_DEVICE",
"IRP_MN_CANCEL_STOP_DEVICE",
"IRP_MN_QUERY_DEVICE_RELATIONS",
"IRP_MN_QUERY_INTERFACE",
"IRP_MN_QUERY_CAPABILITIES",
"IRP_MN_QUERY_RESOURCES",
"IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
"IRP_MN_QUERY_DEVICE_TEXT",
"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
"IRP_MN_READ_CONFIG",
"IRP_MN_WRITE_CONFIG",
"IRP_MN_EJECT",
"IRP_MN_SET_LOCK",
"IRP_MN_QUERY_ID",
"IRP_MN_QUERY_PNP_DEVICE_STATE",
"IRP_MN_QUERY_BUS_INFORMATION",
"IRP_MN_DEVICE_USAGE_NOTIFICATION",
"IRP_MN_SURPRISE_REMOVAL",
"IRP_MN_QUERY_LEGACY_BUS_INFORMATION",
};
#define MAX_NTFS_METADATA_FILE 11
CHAR *NtfsMetadataFileNames[] =
{
"$Mft",
"$MftMirr",
"$LogFile",
"$Volume",
"$AttrDef",
"$Root",
"$Bitmap",
"$Boot",
"$BadClus",
"$Secure",
"$UpCase",
"$Extend"
};
//
// This Filemon's Fast I/O dispatch table. Note that NT assumes that
// file system drivers support some Fast I/O calls, so this table must
// be present for an file system filter driver
//
FAST_IO_DISPATCH FastIOHook =
{
sizeof(FAST_IO_DISPATCH),
FilemonFastIoCheckifPossible,
FilemonFastIoRead,
FilemonFastIoWrite,
FilemonFastIoQueryBasicInfo,
FilemonFastIoQueryStandardInfo,
FilemonFastIoLock,
FilemonFastIoUnlockSingle,
FilemonFastIoUnlockAll,
FilemonFastIoUnlockAllByKey,
FilemonFastIoDeviceControl,
FilemonFastIoAcquireFile,
FilemonFastIoReleaseFile,
FilemonFastIoDetachDevice,
//
// new for NT 4.0
//
FilemonFastIoQueryNetworkOpenInfo,
FilemonFastIoAcquireForModWrite,
FilemonFastIoMdlRead,
FilemonFastIoMdlReadComplete,
FilemonFastIoPrepareMdlWrite,
FilemonFastIoMdlWriteComplete,
FilemonFastIoReadCompressed,
FilemonFastIoWriteCompressed,
FilemonFastIoMdlReadCompleteCompressed,
FilemonFastIoMdlWriteCompleteCompressed,
FilemonFastIoQueryOpen,
FilemonFastIoReleaseForModWrite,
FilemonFastIoAcquireForCcFlush,
FilemonFastIoReleaseForCcFlush
};
//----------------------------------------------------------------------
// P A T T E R N M A T C H I N G R O U T I N E S
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// MatchOkay
//
// Only thing left after compare is more mask. This routine makes
// sure that its a valid wild card ending so that its really a match.
//
//----------------------------------------------------------------------
BOOLEAN
MatchOkay(
PCHAR Pattern
)
{
//
// If pattern isn't empty, it must be a wildcard
//
if( *Pattern && *Pattern != '*' )
{
return FALSE;
}
//
// Matched
//
return TRUE;
}
//----------------------------------------------------------------------
//
// MatchWithPattern
//
// Performs nifty wildcard comparison.
//
//----------------------------------------------------------------------
BOOLEAN
MatchWithPattern(
PCHAR Pattern,
PCHAR Name
)
{
char matchchar;
//
// End of pattern?
//
if( !*Pattern )
{
return FALSE;
}
//
// If we hit a wild card, do recursion
//
if( *Pattern == '*' )
{
Pattern++;
while( *Name && *Pattern )
{
matchchar = *Name;
if( matchchar >= 'a' &&
matchchar <= 'z' )
{
matchchar -= 'a' - 'A';
}
//
// See if this substring matches
//
if( *Pattern == matchchar )
{
if( MatchWithPattern( Pattern + 1, Name + 1 ))
{
return TRUE;
}
}
//
// Try the next substring
//
Name++;
}
//
// See if match condition was met
//
return MatchOkay( Pattern );
}
//
// Do straight compare until we hit a wild card
//
while( *Name && *Pattern != '*' )
{
matchchar = *Name;
if( matchchar >= 'a' &&
matchchar <= 'z' )
{
matchchar -= 'a' - 'A';
}
if( *Pattern == matchchar )
{
Pattern++;
Name++;
}
else
{
return FALSE;
}
}
//
// If not done, recurse
//
if( *Name )
{
return MatchWithPattern( Pattern, Name );
}
//
// Make sure its a match
//
return MatchOkay( Pattern );
}
//----------------------------------------------------------------------
// B U F F E R M A N A G E M E N T
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// FilemonFreeLog
//
// Frees all the data output buffers that we have currently allocated.
//
//----------------------------------------------------------------------
VOID
FilemonFreeLog(
VOID
)
{
PLOG_BUF prev;
//
// Just traverse the list of allocated output buffers
//
while( CurrentLog )
{
prev = CurrentLog->Next;
ExFreePool( CurrentLog );
CurrentLog = prev;
}
}
//----------------------------------------------------------------------
//
// FilemonAllocateLog
//
// Called when the current buffer has filled up. This allocates a new
// buffer and stick it at the head (newest) entry of our buffer list.
//
//----------------------------------------------------------------------
void
FilemonAllocateLog(
VOID
)
{
PLOG_BUF prev = CurrentLog, newLog;
//
// If we've already allocated the allowed number of buffers, just
// reuse the current one. This will result in output records being
// lost, but it takes ALOT of file system activity to cause this.
//
if( MaxLog == NumLog )
{
DbgPrint(("Filemon ***** Dropping records at sequence number %d\n", Sequence ));
CurrentLog->Len = 0;
return;
}
DbgPrint(("FilemonAllocateLog: num: %d max: %d\n", NumLog, MaxLog ));
//
// If the output buffer we currently are using is empty, just
// use it.
//
if( !CurrentLog->Len )
{
return;
}
//
// Allocate a new output buffer
//
newLog = ExAllocatePool( NonPagedPool, sizeof(*CurrentLog) );
if( newLog )
{
//
// Allocation was successful so add the buffer to the list
// of allocated buffers and increment the buffer count.
//
CurrentLog = newLog;
CurrentLog->Len = 0;
CurrentLog->Next = prev;
NumLog++;
}
else
{
//
// The allocation failed - just reuse the current buffer
//
CurrentLog->Len = 0;
}
}
//----------------------------------------------------------------------
//
// FilemonGetOldestLog
//
// Traverse the list of allocated buffers to find the last one, which
// will be the oldest (as we want to return the oldest data to the GUI
// first).
//
//----------------------------------------------------------------------
PLOG_BUF
FilemonGetOldestLog(
VOID
)
{
PLOG_BUF ptr = CurrentLog, prev = NULL;
//
// Traverse the list
//
while( ptr->Next )
{
ptr = (prev = ptr)->Next;
}
//
// Remove the buffer from the list
//
if( prev )
{
prev->Next = NULL;
NumLog--;
}
return ptr;
}
//----------------------------------------------------------------------
//
// FilemonResetLog
//
// When a GUI instance has close communication (exited), but the driver
// can't unload due to oustdanding IRPs, all the output buffers except
// one are all deallocated so that the memory footprint is shrunk as much
// as possible.
//
//----------------------------------------------------------------------
VOID
FilemonResetLog(
VOID
)
{
PLOG_BUF current, next;
ExAcquireFastMutex( &LogMutex );
//
// Traverse the list of output buffers
//
current = CurrentLog->Next;
while( current )
{
//
// Free the buffer
//
next = current->Next;
ExFreePool( current );
current = next;
}
//
// Move the output pointer in the buffer that's being kept
// the start of the buffer.
//
NumLog = 1;
CurrentLog->Len = 0;
CurrentLog->Next = NULL;
ExReleaseFastMutex( &LogMutex );
}
//----------------------------------------------------------------------
//
// LogRecord
//
// This "printfs" a string into an output buffer.
//
//----------------------------------------------------------------------
BOOLEAN
LogRecord(
BOOLEAN ProcessFilters,
PULONG SeqNum,
PLARGE_INTEGER dateTime,
PLARGE_INTEGER perfTime,
const CHAR *format,
...
)
{
PENTRY Entry;
int len;
ULONG recordSequence;
va_list arg_ptr;
static CHAR text[MAXPATHLEN];
BOOLEAN passedFilters = FALSE;
//
// If no GUI is there to receive the output or if no filtering is desired, don't bother
//
if( !FilterOn )
{
return FALSE;
}
//
// Lock the output buffer and Log.
//
ExAcquireFastMutex( &LogMutex );
//
// Send text out as debug output This is x86 specific.
//
#define A (&format)
DbgPrint(( (char *)format, A[1], A[2], A[3], A[4], A[5], A[6] ));
DbgPrint(( "\n" ));
#undef A
//
// Vsprintf to determine the length of the buffer
//
va_start( arg_ptr, format );
len = vsprintf( text, format, arg_ptr );
va_end( arg_ptr );
//
// ULONG align for Alpha
//
len += 4;
len &= 0xFFFFFFFC;
//
// Only log it if it passes the filters. Note that IRP completion
// passes a false for ProcessFilters because if we've logged
// the initial action, we have to go ahead and log the completion.
//
passedFilters = !ProcessFilters || ApplyFilters( text );
if( passedFilters )
{
//
// Assign a sequence number if we weren't passed one
//
if( !SeqNum || (SeqNum && *SeqNum == (ULONG) - 1))
{
recordSequence = InterlockedIncrement( &Sequence );
if( SeqNum ) *SeqNum = recordSequence;
}
else
{
recordSequence = *SeqNum;
}
//
// If the current output buffer is near capacity, move to a new
// output buffer
//
if( CurrentLog->Len + len + sizeof(ENTRY) + 1 >= LOGBUFSIZE )
{
FilemonAllocateLog();
}
//
// Log the entry
//
Entry = (void *)(CurrentLog->Data + CurrentLog->Len);
Entry->seq = recordSequence;
Entry->datetime.QuadPart = 0;
Entry->perftime.QuadPart = 0;
if( dateTime ) Entry->datetime = *dateTime;
if( perfTime ) Entry->perftime = *perfTime;
memcpy( Entry->text, text, len );
//
// Log the length of the string, plus 1 for the terminating
// NULL
//
CurrentLog->Len += ((ULONG) (Entry->text - (PCHAR) Entry )) + len;
}
//
// Release the output buffer lock
//
ExReleaseFastMutex( &LogMutex );
return passedFilters;
}
//----------------------------------------------------------------------
// H A S H T A B L E M A N A G E M E N T
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// FilemonHashCleanup
//
// Called when we are unloading to free any memory that we have
// in our possession.
//
//----------------------------------------------------------------------
VOID
FilemonHashCleanup(
VOID
)
{
PHASH_ENTRY hashEntry, nextEntry;
ULONG i;
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite( &HashResource, TRUE );
//
// Free the hash table entries
//
for( i = 0; i < NUMHASH; i++ )
{
hashEntry = HashTable[i];
while( hashEntry )
{
nextEntry = hashEntry->Next;
ExFreePool( hashEntry );
hashEntry = nextEntry;
}
HashTable[i] = NULL;
}
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
}
//----------------------------------------------------------------------
//
// FilemonFreeHashEntry
//
// When we see a file close, we can free the string we had associated
// with the fileobject being closed since we know it won't be used
// again.
//
//----------------------------------------------------------------------
VOID
FilemonFreeHashEntry(
PFILE_OBJECT fileObject
)
{
PHASH_ENTRY hashEntry, prevEntry;
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite( &HashResource, TRUE );
//
// Look-up the entry
//
hashEntry = HashTable[ HASHOBJECT( fileObject ) ];
prevEntry = NULL;
while( hashEntry && hashEntry->FileObject != fileObject )
{
prevEntry = hashEntry;
hashEntry = hashEntry->Next;
}
//
// If we fall of the hash list without finding what we're looking
// for, just return.
//
if( !hashEntry )
{
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
return;
}
//
// Got it! Remove it from the list
//
if( prevEntry )
{
prevEntry->Next = hashEntry->Next;
}
else
{
HashTable[ HASHOBJECT( fileObject )] = hashEntry->Next;
}
//
// Free the entry's memory
//
ExFreePool( hashEntry );
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
}
//----------------------------------------------------------------------
// P A T H A N D P R O C E S S N A M E R O U T I N E S
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// FilemonFreeFilters
//
// Fress storage we allocated for filter strings.
//
//----------------------------------------------------------------------
VOID
FilemonFreeFilters(
VOID
)
{
ULONG i;
for( i = 0; i < NumIncludeFilters; i++ )
{
ExFreePool( IncludeFilters[i] );
}
for( i = 0; i < NumExcludeFilters; i++ )
{
ExFreePool( ExcludeFilters[i] );
}
NumIncludeFilters = 0;
NumExcludeFilters = 0;
}
//----------------------------------------------------------------------
//
// MakeFilterArray
//
// Takes a filter string and splits into components (a component
// is seperated with a ';')
//
//----------------------------------------------------------------------
VOID
MakeFilterArray(
PCHAR FilterString,
PCHAR FilterArray[],
PULONG NumFilters
)
{
PCHAR filterStart;
ULONG filterLength;
CHAR saveChar;
//
// Scan through the process filters
//
filterStart = FilterString;
while( *filterStart )
{
filterLength = 0;
while( filterStart[filterLength] &&
filterStart[filterLength] != ';' )
{
filterLength++;
}
//
// Ignore zero-length components
//
if( filterLength )
{
//
// Conservatively allocate so that we can prepend and append
// wildcards
//
FilterArray[ *NumFilters ] =
ExAllocatePool( PagedPool, filterLength + 1 + 2 * sizeof('*') );
//
// Only fill this in if there's enough memory
//
if( FilterArray[ *NumFilters] )
{
saveChar = *(filterStart + filterLength );
*(filterStart + filterLength) = 0;
sprintf( FilterArray[ *NumFilters ], "%s%s%s",
*filterStart == '*' ? "" : "*",
filterStart,
*(filterStart + filterLength - 1 ) == '*' ? "" : "*" );
*(filterStart + filterLength) = saveChar;
(*NumFilters)++;
}
}
//
// Are we done?
//
if( !filterStart[filterLength] ) break;
//
// Move to the next component (skip over ';')
//
filterStart += filterLength + 1;
}
}
//----------------------------------------------------------------------
//
// FilemonUpdateFilters
//
// Takes a new filter specification and updates the filter
// arrays with them.
//
//----------------------------------------------------------------------
VOID
FilemonUpdateFilters(
VOID
)
{
//
// Free old filters (if any)
//
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite( &FilterResource, TRUE );
FilemonFreeFilters();
//
// Create new filter arrays
//
MakeFilterArray( FilterDef.includefilter,
IncludeFilters, &NumIncludeFilters );
MakeFilterArray( FilterDef.excludefilter,
ExcludeFilters, &NumExcludeFilters );
ExReleaseResourceLite( &FilterResource );
KeLeaveCriticalRegion();
}
//----------------------------------------------------------------------
//
// ApplyFilters
//
// If the name matches the exclusion mask, we do not log it. Else if
// it doesn't match the inclusion mask we do not log it.
//
//----------------------------------------------------------------------
BOOLEAN
ApplyFilters(
PCHAR Text
)
{
ULONG i;
//
// If no GUI or no filename return FALSE
//
if( !Text ) return FALSE;
//
// If it matches the exclusion string, do not log it
//
KeEnterCriticalRegion();
ExAcquireResourceSharedLite( &FilterResource, TRUE );
for( i = 0; i < NumExcludeFilters; i++ )
{
if( MatchWithPattern( ExcludeFilters[i], Text ) )
{
ExReleaseResourceLite( &FilterResource );
KeLeaveCriticalRegion();
return FALSE;
}
}
//
// If it matches an include filter then log it
//
for( i = 0; i < NumIncludeFilters; i++ )
{
if( MatchWithPattern( IncludeFilters[i], Text ))
{
ExReleaseResourceLite( &FilterResource );
KeLeaveCriticalRegion();
return TRUE;
}
}
//
// It didn't match any include filters so don't log
//
ExReleaseResourceLite( &FilterResource );
KeLeaveCriticalRegion();
return FALSE;
}
//----------------------------------------------------------------------
//
// FilemonQueryFileComplete
//
// This routine is used to handle I/O completion for our self-generated
// IRP that is used to query a file's name or number.
//
//----------------------------------------------------------------------
NTSTATUS
FilemonQueryFileComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
//
// Copy the status information back into the "user" IOSB.
//
*Irp->UserIosb = Irp->IoStatus;
if( !NT_SUCCESS(Irp->IoStatus.Status) )
{
DbgPrint((" ERROR ON IRP: %x\n", Irp->IoStatus.Status ));
}
//
// Set the user event - wakes up the mainline code doing this.
//
KeSetEvent(Irp->UserEvent, 0, FALSE);
//
// Free the IRP now that we are done with it.
//
IoFreeIrp(Irp);
//
// We return STATUS_MORE_PROCESSING_REQUIRED because this "magic" return value
// tells the I/O Manager that additional processing will be done by this driver
// to the IRP - in fact, it might (as it is in this case) already BE done - and
// the IRP cannot be completed.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
//----------------------------------------------------------------------
//
// FilemonQueryFile
//
// This function retrieves the "standard" information for the
// underlying file system, asking for the filename in particular.
//
//----------------------------------------------------------------------
BOOLEAN
FilemonQueryFile(
PDEVICE_OBJECT DeviceObject,
PFILE_OBJECT FileObject,
FILE_INFORMATION_CLASS FileInformationClass,
PVOID FileQueryBuffer,
ULONG FileQueryBufferLength
)
{
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION ioStackLocation;
DbgPrint(("Getting file name for %x\n", FileObject));
//
// Initialize the event
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
//
// Allocate an irp for this request. This could also come from a
// private pool, for instance.
//
irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if(!irp)
{
//
// Failure!
//
return FALSE;
}
//
// Build the IRP's main body
//
irp->AssociatedIrp.SystemBuffer = FileQueryBuffer;
irp->UserEvent = &event;
irp->UserIosb = &IoStatusBlock;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->RequestorMode = KernelMode;
irp->Flags = 0;
//
// Set up the I/O stack location.
//
ioStackLocation = IoGetNextIrpStackLocation(irp);
ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
ioStackLocation->DeviceObject = DeviceObject;
ioStackLocation->FileObject = FileObject;
ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength;
ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass;
//
// Set the completion routine.
//
IoSetCompletionRoutine(irp, FilemonQueryFileComplete, 0, TRUE, TRUE, TRUE);
//
// Send it to the FSD
//
(void) IoCallDriver(DeviceObject, irp);
//
// Wait for the I/O
//
KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
//
// Done! Note that since our completion routine frees the IRP we cannot
// touch the IRP now.
//
return NT_SUCCESS( IoStatusBlock.Status );
}
//----------------------------------------------------------------------
//
// FilemonGetFullPath
//
// Takes a fileobject and filename and returns a canonical path,
// nicely formatted, in fullpathname.
//
//----------------------------------------------------------------------
VOID
FilemonGetFullPath(
BOOLEAN createPath,
PFILE_OBJECT fileObject,
PHOOK_EXTENSION hookExt,
PCHAR fullPathName
)
{
ULONG pathLen, prefixLen, slashes;
PCHAR pathOffset, ptr;
BOOLEAN gotPath;
PFILE_OBJECT relatedFileObject;
PHASH_ENTRY hashEntry, newEntry;
ANSI_STRING fileName;
ANSI_STRING relatedName;
PFILE_NAME_INFORMATION fileNameInfo;
FILE_INTERNAL_INFORMATION fileInternalInfo;
UNICODE_STRING fullUniName;
ULONGLONG mftIndex;
//
// Only do this if a GUI is active and filtering is on
//
if( fullPathName ) fullPathName[0] = 0;
if( !FilterOn || !hookExt || !hookExt->Hooked || !fullPathName)
{
return;
}
//
// Lookup the object in the hash table to see if a name
// has already been generated for it
//
KeEnterCriticalRegion();
ExAcquireResourceSharedLite( &HashResource, TRUE );
hashEntry = HashTable[ HASHOBJECT( fileObject ) ];
while( hashEntry && hashEntry->FileObject != fileObject )
{
hashEntry = hashEntry->Next;
}
//
// Did we find an entry?
//
if( hashEntry )
{
//
// Yes, so get the name from the entry.
//
strcpy( fullPathName, hashEntry->FullPathName );
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
return;
}
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
//
// We didn't find the name in the hash table so let's either ask
// the file system for it or construct it from the file objects.
//
//
// Calculate prefix length
//
switch( hookExt->Type )
{
case NPFS:
prefixLen = NAMED_PIPE_PREFIX_LENGTH;
break;
case MSFS:
prefixLen = MAIL_SLOT_PREFIX_LENGTH;
break;
default:
if( !fileObject ||
fileObject->DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM )
{
prefixLen = 0;
}
else
{
prefixLen = 2; // "C:"
}
break;
}
//
// If there's no file object, we can't even ask for a name.
//
if( !fileObject )
{
if( hookExt->Type == NPFS ) strcpy( fullPathName, NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) strcpy( fullPathName, MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
return;
}
//
// Initialize variables
//
fileName.Buffer = NULL;
relatedName.Buffer = NULL;
gotPath = FALSE;
//
// Check for special case first: NTFS volume and a file object
// with no name. It might be a metadata file that we "know" the name of. This
// special case also stops us from querying NTFS for the name of a metadata
// file on versions of NTFS prior to Whistler, which is a good thing since
// that causes hangs and crashes. On Whistler metadata files have file names.
//
if( !fileObject->FileName.Buffer && hookExt->FsAttributes &&
!memcmp( hookExt->FsAttributes->FileSystemName, L"NTFS", sizeof(L"NTFS") - sizeof(WCHAR)))
{
//
// The only file that is opened without a name is a volume
//
if( createPath )
{
sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
//
// Return right here without inserting this into the hash table, since this might
// be the cleanup path of a metadata file and we can retrieve the metada's index
// at a later point.
//
return;
}
else if( FilemonQueryFile( hookExt->FileSystem, fileObject, FileInternalInformation,
&fileInternalInfo, sizeof( fileInternalInfo )))
{
//
// Use the name in the metadata name index
//
mftIndex = fileInternalInfo.IndexNumber.QuadPart & ~0xF0000000;
if( mftIndex <= MAX_NTFS_METADATA_FILE )
{
sprintf( fullPathName, "%C:\\%s", hookExt->LogicalDrive, NtfsMetadataFileNames[ mftIndex ] );
gotPath = TRUE;
}
}
}
//
// If we are not in the create path, we can ask the file system for the name. If we
// are in the create path, we can't ask the file system for the name of the file object, since
// the file system driver hasn't even seen the file object yet.
//
if( !gotPath && !createPath )
{
//
// Ask the file system for the name of the file, which its required to be
// able to provide for the Win32 filename query function. We could use the
// undocumented ObQueryNameString, but then we'd have to worry about
// re-entrancy issues, since that call generates the IRP that we create
// manually here. Since we send the IRP to the FSD below us, we don't need
// to worry about seeing the IRP in our dispatch entry point. This can fail
// in some cases, so we fall back on constructing the name ourselves if
// we have to.
//
fileNameInfo = (PFILE_NAME_INFORMATION) ExAllocatePool( NonPagedPool,
MAXPATHLEN * sizeof(WCHAR) );
if( fileNameInfo &&
FilemonQueryFile(hookExt->FileSystem, fileObject, FileNameInformation,
fileNameInfo, (MAXPATHLEN - prefixLen - 1)*sizeof(WCHAR) ))
{
fullUniName.Length = (SHORT) fileNameInfo->FileNameLength;
fullUniName.Buffer = fileNameInfo->FileName;
if( NT_SUCCESS( RtlUnicodeStringToAnsiString( &fileName, &fullUniName, TRUE )))
{
fullPathName[ fileName.Length + prefixLen ] = 0;
if( hookExt->Type == NPFS )
{
strcpy( fullPathName, NAMED_PIPE_PREFIX );
}
else if( hookExt->Type == MSFS )
{
strcpy( fullPathName, MAIL_SLOT_PREFIX );
}
else if( fileObject->DeviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM )
{
sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
}
else
{
//
// No prefix for network devices
//
}
memcpy( &fullPathName[prefixLen], fileName.Buffer, fileName.Length );
gotPath = TRUE;
RtlFreeAnsiString( &fileName );
fileName.Buffer = NULL;
}
}
if( fileNameInfo ) ExFreePool( fileNameInfo );
}
//
// If we don't have a name yet then we are in the create path, or we failed
// when we asked the file system for the name. In that case we'll go ahead
// and construct the name based on file object names.
//
if( !gotPath )
{
//
// If there is no file name at this point, just return "DEVICE" to indicate
// raw access to a device
//
if( !fileObject->FileName.Buffer )
{
if( hookExt->Type == NPFS ) strcpy( fullPathName, NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) strcpy( fullPathName, MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
return;
}
//
// Create the full path name. First, calculate the length taking into
// account space for seperators and the leading prefix
//
if( !NT_SUCCESS( RtlUnicodeStringToAnsiString( &fileName, &fileObject->FileName, TRUE )))
{
if( hookExt->Type == NPFS ) sprintf( fullPathName, "%s: ", NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) sprintf( fullPathName, "%s: ", MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C: ", hookExt->LogicalDrive );
return;
}
pathLen = fileName.Length + prefixLen;
relatedFileObject = fileObject->RelatedFileObject;
//
// Only look at related file object if this is a relative name
//
if( fileObject->FileName.Buffer[0] != L'\\' &&
relatedFileObject && relatedFileObject->FileName.Length )
{
if( !NT_SUCCESS( RtlUnicodeStringToAnsiString( &relatedName, &relatedFileObject->FileName, TRUE )))
{
if( hookExt->Type == NPFS ) sprintf( fullPathName, "%s: ", NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) sprintf( fullPathName, "%s: ", MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C: ", hookExt->LogicalDrive );
RtlFreeAnsiString( &fileName );
return;
}
pathLen += relatedName.Length + 1;
}
//
// Add the drive letter first at the front of the name
//
if( hookExt->Type == NPFS ) strcpy( fullPathName, NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) strcpy( fullPathName, MAIL_SLOT_PREFIX );
else if( fileObject->DeviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM )
{
sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
}
//
// If the name is too long, quit now
//
if( pathLen >= MAXPATHLEN )
{
strcat( fullPathName, " " );
}
else
{
//
// Now we can build the path name
//
fullPathName[ pathLen ] = 0;
pathOffset = fullPathName + pathLen - fileName.Length;
memcpy( pathOffset, fileName.Buffer, fileName.Length + 1 );
if( fileObject->FileName.Buffer[0] != L'\\' &&
relatedFileObject && relatedFileObject->FileName.Length )
{
//
// Copy the component, adding a slash separator
//
*(pathOffset - 1) = '\\';
pathOffset -= relatedName.Length + 1;
memcpy( pathOffset, relatedName.Buffer, relatedName.Length );
//
// If we've got to slashes at the front zap one
//
if( pathLen > 3 && fullPathName[2] == '\\' && fullPathName[3] == '\\' )
{
strcpy( fullPathName + 2, fullPathName + 3 );
}
}
}
}
if( fileName.Buffer ) RtlFreeAnsiString( &fileName );
if( relatedName.Buffer ) RtlFreeAnsiString( &relatedName );
//
// Network redirector names already specify a share name that we
// have to strip:
//
// \X:\computer\share\realpath
//
// And we want to present:
//
// X:\realpath
//
// to the user.
//
if( fileObject->DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM &&
strlen( fullPathName ) >= strlen("\\X:\\") )
{
//
// If this is Win2k the name is specified like this:
//
// \;X:0\computer\share\realpath
//
// so we have to handle that case as well
//
if( fullPathName[1] == ';' )
{
//
// Win2K-style name. Grab the drive letter
// and skip over the share
//
fullPathName[0] = fullPathName[2];
fullPathName[1] = ':';
fullPathName[2] = '\\';
//
// The third slash after the drive is the
// start of the real path (we start scanning
// at the ':' since we don't want to make assumptions
// about the length of the number).
//
slashes = 0;
ptr = &fullPathName[3];
while( *ptr && slashes != 3 )
{
if( *ptr == '\\' ) slashes++;
ptr++;
}
strcpy( &fullPathName[3], ptr );
}
else if( fullPathName[2] == ':' )
{
//
// NT 4-style name. Skip the share name
//
fullPathName[0] = fullPathName[1];
fullPathName[1] = ':';
fullPathName[2] = '\\';
//
// The second slash after the drive's slash (x:\)
// is the start of the real path
//
slashes = 0;
ptr = &fullPathName[3];
while( *ptr && slashes != 3 )
{
if( *ptr == '\\' ) slashes++;
ptr++;
}
strcpy( &fullPathName[3], ptr );
}
else
{
//
// Its a UNC path, so add a leading slash
//
RtlMoveMemory( &fullPathName[1], fullPathName, strlen( fullPathName ) + 1);
fullPathName[0] = '\\';
}
}
//
// Allocate a hash entry
//
newEntry = ExAllocatePool( NonPagedPool,
sizeof(HASH_ENTRY ) + strlen( fullPathName ) + 1);
//
// If no memory for a new entry, oh well.
//
if( newEntry )
{
//
// Fill in the new entry
//
newEntry->FileObject = fileObject;
strcpy( newEntry->FullPathName, fullPathName );
//
// Put it in the hash table
//
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite( &HashResource, TRUE );
newEntry->Next = HashTable[ HASHOBJECT(fileObject) ];
HashTable[ HASHOBJECT(fileObject) ] = newEntry;
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
}
}
//----------------------------------------------------------------------
//
// FilemonGetProcessNameOffset
//
// In an effort to remain version-independent, rather than using a
// hard-coded into the KPEB (Kernel Process Environment Block), we
// scan the KPEB looking for the name, which should match that
// of the system process. This is because we are in the system process'
// context in DriverEntry, where this is called.
//
//----------------------------------------------------------------------
ULONG
FilemonGetProcessNameOffset(
VOID
)
{
PEPROCESS curproc;
int i;
curproc = PsGetCurrentProcess();
//
// Scan for 12KB, hoping the KPEB never grows that big!
//
for( i = 0; i < 3 * PAGE_SIZE; i++ )
{
if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) ))
{
return i;
}
}
//
// Name not found - oh, well
//
return 0;
}
//----------------------------------------------------------------------
//
// FilemonGetProcess
//
// Uses undocumented data structure offsets to obtain the name of the
// currently executing process.
//
//----------------------------------------------------------------------
PCHAR
FilemonGetProcess(
PCHAR ProcessName
)
{
PEPROCESS curproc;
char *nameptr;
ULONG i;
//
// We only do this if we determined the process name offset
//
if( ProcessNameOffset )
{
//
// Get a pointer to the current process block
//
curproc = PsGetCurrentProcess();
//
// Dig into it to extract the name. Make sure to leave enough room
// in the buffer for the appended process ID.
//
nameptr = (PCHAR) curproc + ProcessNameOffset;
strncpy( ProcessName, nameptr, NT_PROCNAMELEN - 1 );
ProcessName[NT_PROCNAMELEN-1] = 0;
#if defined(_IA64_)
sprintf( ProcessName + strlen(ProcessName), ":%I64d", PsGetCurrentProcessId());
#else
sprintf( ProcessName + strlen(ProcessName), ":%d", PsGetCurrentProcessId());
#endif
}
else
{
strcpy( ProcessName, "???" );
}
return ProcessName;
}
//----------------------------------------------------------------------
// H O O K / U N H O O K R O U T I N E S
//----------------------------------------------------------------------
#if DBG
//----------------------------------------------------------------------
//
// UnloadDetach
//
// Detaches from all devices for an unload
//
//----------------------------------------------------------------------
VOID
UnloadDetach(
VOID
)
{
ULONG drive, i;
PDEVICE_OBJECT device;
PHOOK_EXTENSION hookExt;
//
// Detach from file system devices
//
for( drive = 0; drive < 26; drive++ )
{
if( DriveHookDevices[drive] )
{
device = DriveHookDevices[drive];
hookExt = device->DeviceExtension;
IoDetachDevice( hookExt->FileSystem );
IoDeleteDevice( device );
for( i = 0; i < 26; i++ )
{
if( DriveHookDevices[i] == device )
{
DriveHookDevices[i] = NULL;
}
}
}
}
//
// Detach from special devices
//
if( NamedPipeHookDevice )
{
IoDetachDevice( NamedPipeHookDevice );
IoDeleteDevice( NamedPipeHookDevice );
}
if( MailSlotHookDevice )
{
IoDetachDevice( MailSlotHookDevice );
IoDeleteDevice( MailSlotHookDevice );
}
}
#endif // DBG
//----------------------------------------------------------------------
//
// HookSpecialFs
//
// Hook the named pipe or mail slot file system.
//
//----------------------------------------------------------------------
BOOLEAN
HookSpecialFs(
IN PDRIVER_OBJECT DriverObject,
FILE_SYSTEM_TYPE FsType
)
{
IO_STATUS_BLOCK ioStatus;
HANDLE ntFileHandle;
OBJECT_ATTRIBUTES objectAttributes;
PDEVICE_OBJECT fileSysDevice;
PDEVICE_OBJECT topAttachDevice;
PDEVICE_OBJECT hookDevice;
UNICODE_STRING fileNameUnicodeString;
WCHAR npfsFilename[] = L"\\Device\\NamedPipe";
WCHAR msfsFilename[] = L"\\Device\\MailSlot";
NTSTATUS ntStatus;
ULONG i;
PFILE_OBJECT fileObject;
PHOOK_EXTENSION hookExtension;
//
// If we've already hooked it, just return success
//
if( FsType == NPFS && NamedPipeHookDevice ) return TRUE;
if( FsType == MSFS && MailSlotHookDevice ) return TRUE;
//
// We have to figure out what device to hook - first open the volume's
// root directory
//
if( FsType == NPFS ) RtlInitUnicodeString( &fileNameUnicodeString, npfsFilename );
else RtlInitUnicodeString( &fileNameUnicodeString, msfsFilename );
InitializeObjectAttributes( &objectAttributes, &fileNameUnicodeString,
OBJ_CASE_INSENSITIVE, NULL, NULL );
ntStatus = ZwCreateFile( &ntFileHandle, SYNCHRONIZE | FILE_ANY_ACCESS,
&objectAttributes, &ioStatus, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE,
NULL, 0 );
if( !NT_SUCCESS( ntStatus ) )
{
DbgPrint(("Filemon: Could not open %s\n", FsType == NPFS ? "NPFS" : "MSFS", ntStatus ));
return FALSE;
}
DbgPrint(("Filemon: opened the root directory!!! handle: %x\n", ntFileHandle));
//
// Got the file handle, so now look-up the file-object it refers to
//
ntStatus = ObReferenceObjectByHandle( ntFileHandle, FILE_READ_DATA,
NULL, KernelMode, &fileObject, NULL );
if( !NT_SUCCESS( ntStatus ))
{
DbgPrint(("Filemon: Could not get fileobject from %s handle: %x\n",
FsType == NPFS ? "NPFS" : "MSFS", ntStatus ));
ZwClose( ntFileHandle );
return FALSE;
}
//
// Next, find out what device is associated with the file object by getting its related
// device object
//
fileSysDevice = IoGetRelatedDeviceObject( fileObject );
if( ! fileSysDevice )
{
DbgPrint(("Filemon: Could not get related device object for %s: %x\n",
FsType == NPFS ? "NPFS" : "MSFS", ntStatus ));
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
return FALSE;
}
//
// The file system's device hasn't been hooked already, so make a hooking device
// object that will be attached to it.
//
ntStatus = IoCreateDevice( DriverObject,
sizeof(HOOK_EXTENSION),
NULL,
fileSysDevice->DeviceType,
0,
FALSE,
&hookDevice );
if( !NT_SUCCESS(ntStatus) )
{
DbgPrint(("Filemon: failed to create associated device %s: %x\n",
FsType == NPFS ? "NPFS" : "MSFS", ntStatus ));
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
return FALSE;
}
//
// Clear the device's init flag as per NT DDK KB article on creating device
// objects from a dispatch routine
//
hookDevice->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Finally, attach to the device. The second we're successfully attached, we may
// start receiving IRPs targetted at the device we've hooked.
//
topAttachDevice = IoAttachDeviceToDeviceStack( hookDevice, fileSysDevice );
if( !topAttachDevice )
{
//
// Couldn' attach for some reason
//
DbgPrint(("Filemon: Connect with Filesystem failed: %s (%x) =>%x\n",
FsType == NPFS ? "NPFS" : "MSFS", fileSysDevice, ntStatus ));
//
// Derefence the object and get out
//
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
return FALSE;
}
else
{
DbgPrint(("Filemon: Successfully connected to Filesystem device %s\n",
FsType == NPFS ? "NPFS" : "MSFS" ));
}
//
// Setup the device extensions. The drive letter and file system object are stored
// in the extension.
//
hookExtension = hookDevice->DeviceExtension;
hookExtension->LogicalDrive = '\\';
hookExtension->FileSystem = topAttachDevice;
hookExtension->Hooked = TRUE;
hookExtension->Type = FsType;
//
// Close the file and update the hooked drive list by entering a
// pointer to the hook device object in it.
//
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
if( FsType == NPFS ) NamedPipeHookDevice = hookDevice;
else MailSlotHookDevice = hookDevice;
return TRUE;
}
//----------------------------------------------------------------------
//
// UnhookSpecialFs
//
// Unhook the named pipe file or mail slot system.
//
//----------------------------------------------------------------------
VOID
UnhookSpecialFs(
FILE_SYSTEM_TYPE FsType
)
{
PHOOK_EXTENSION hookExt;
if( FsType == NPFS && NamedPipeHookDevice )
{
hookExt = NamedPipeHookDevice->DeviceExtension;
hookExt->Hooked = FALSE;
NamedPipeHookDevice = NULL;
}
else if( FsType == MSFS && MailSlotHookDevice )
{
hookExt = MailSlotHookDevice->DeviceExtension;
hookExt->Hooked = FALSE;
MailSlotHookDevice = NULL;
}
}
//----------------------------------------------------------------------
//
// HookDrive
//
// Hook the drive specified by determining which device object to
// attach to. The algorithm used here is similar to the one used
// internally by NT to determine which device object a file system request
// is directed at.
//
//----------------------------------------------------------------------
BOOLEAN
HookDrive(
IN ULONG Drive,
IN PDRIVER_OBJECT DriverObject
)
{
IO_STATUS_BLOCK ioStatus;
HANDLE ntFileHandle;
OBJECT_ATTRIBUTES objectAttributes;
PDEVICE_OBJECT fileSysDevice;
PDEVICE_OBJECT hookDevice;
UNICODE_STRING fileNameUnicodeString;
PFILE_FS_ATTRIBUTE_INFORMATION fileFsAttributes;
ULONG fileFsAttributesSize;
WCHAR filename[] = L"\\DosDevices\\A:\\";
NTSTATUS ntStatus;
ULONG i;
PFILE_OBJECT fileObject;
PHOOK_EXTENSION hookExtension;
//
// Is it a legal drive letter?
//
if( Drive >= 26 )
{
return FALSE;
}
//
// Has this drive already been hooked?
//
if( DriveHookDevices[Drive] == NULL )
{
//
// Frob the name to make it refer to the drive specified in the input
// parameter.
//
filename[12] = (CHAR) ('A' + Drive);
//
// We have to figure out what device to hook - first open the volume's
// root directory
//
RtlInitUnicodeString( &fileNameUnicodeString, filename );
InitializeObjectAttributes( &objectAttributes, &fileNameUnicodeString,
OBJ_CASE_INSENSITIVE, NULL, NULL );
ntStatus = ZwCreateFile( &ntFileHandle, SYNCHRONIZE | FILE_ANY_ACCESS,
&objectAttributes, &ioStatus, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE,
NULL, 0 );
if( !NT_SUCCESS( ntStatus ) )
{
DbgPrint(("Filemon: Could not open drive %c: %x\n", 'A' + Drive, ntStatus ));
return FALSE;
}
DbgPrint(("Filemon: opened the root directory!!! handle: %x\n", ntFileHandle));
//
// Got the file handle, so now look-up the file-object it refers to
//
ntStatus = ObReferenceObjectByHandle( ntFileHandle, FILE_READ_DATA,
NULL, KernelMode, &fileObject, NULL );
if( !NT_SUCCESS( ntStatus ))
{
DbgPrint(("Filemon: Could not get fileobject from handle: %c\n", 'A' + Drive ));
ZwClose( ntFileHandle );
return FALSE;
}
//
// Next, find out what device is associated with the file object by getting its related
// device object
//
fileSysDevice = IoGetRelatedDeviceObject( fileObject );
if( ! fileSysDevice )
{
DbgPrint(("Filemon: Could not get related device object: %c\n", 'A' + Drive ));
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
return FALSE;
}
//
// Check the device list to see if we've already attached to this particular device.
// This can happen when more than one drive letter is being handled by the same network
// redirecter
//
for( i = 0; i < 26; i++ )
{
if( DriveHookDevices[i] == fileSysDevice )
{
//
// If we're already watching it, associate this drive letter
// with the others that are handled by the same network driver. This
// enables us to intelligently update the hooking menus when the user
// specifies that one of the group should not be watched -we mark all
// of the related drives as unwatched as well
//
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
DriveHookDevices[ Drive ] = fileSysDevice;
return TRUE;
}
}
//
// The file system's device hasn't been hooked already, so make a hooking device
// object that will be attached to it.
//
ntStatus = IoCreateDevice( DriverObject,
sizeof(HOOK_EXTENSION),
NULL,
fileSysDevice->DeviceType,
0,
FALSE,
&hookDevice );
if( !NT_SUCCESS(ntStatus) )
{
DbgPrint(("Filemon: failed to create associated device: %c\n", 'A' + Drive ));
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
return FALSE;
}
//
// Clear the device's init flag as per NT DDK KB article on creating device
// objects from a dispatch routine
//
hookDevice->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Setup the device extensions. The drive letter and file system object are stored
// in the extension.
//
hookExtension = hookDevice->DeviceExtension;
hookExtension->LogicalDrive = 'A' + Drive;
hookExtension->FileSystem = fileSysDevice;
hookExtension->Hooked = TRUE;
hookExtension->Type = STANDARD;
//
// Finally, attach to the device. The second we're successfully attached, we may
// start receiving IRPs targetted at the device we've hooked.
//
ntStatus = IoAttachDeviceByPointer( hookDevice, fileSysDevice );
if( !NT_SUCCESS(ntStatus) )
{
//
// Couldn' attach for some reason
//
DbgPrint(("Filemon: Connect with Filesystem failed: %c (%x) =>%x\n",
'A' + Drive, fileSysDevice, ntStatus ));
//
// Derefence the object and get out
//
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
return FALSE;
}
else
{
//
// Make a new drive group for the device,l if it does not have one
// already
//
DbgPrint(("Filemon: Successfully connected to Filesystem device %c\n", 'A' + Drive ));
}
//
// Determine if this is a NTFS drive
//
fileFsAttributesSize = sizeof( FILE_FS_ATTRIBUTE_INFORMATION) + MAXPATHLEN;
hookExtension->FsAttributes = (PFILE_FS_ATTRIBUTE_INFORMATION) ExAllocatePool( NonPagedPool,
fileFsAttributesSize );
if( hookExtension->FsAttributes &&
!NT_SUCCESS( IoQueryVolumeInformation( fileObject, FileFsAttributeInformation,
fileFsAttributesSize, hookExtension->FsAttributes,
&fileFsAttributesSize )))
{
//
// On failure, we just don't have attributes for this file system
//
ExFreePool( hookExtension->FsAttributes );
hookExtension->FsAttributes = NULL;
}
//
// Close the file and update the hooked drive list by entering a
// pointer to the hook device object in it.
//
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle );
DriveHookDevices[Drive] = hookDevice;
}
else
{
hookExtension = DriveHookDevices[Drive]->DeviceExtension;
hookExtension->Hooked = TRUE;
}
return TRUE;
}
//----------------------------------------------------------------------
//
// UnhookDrive
//
// Unhook a previously hooked drive.
//
//----------------------------------------------------------------------
VOID
UnhookDrive(
IN ULONG Drive
)
{
PHOOK_EXTENSION hookExt;
//
// If the drive has been hooked, unhook it and delete the hook
// device object
//
if( DriveHookDevices[Drive] )
{
hookExt = DriveHookDevices[Drive]->DeviceExtension;
hookExt->Hooked = FALSE;
}
}
//----------------------------------------------------------------------
//
// HookDriveSet
//
// Hook/Unhook a set of drives specified by user. Return the set
// that is currently hooked.
//
//----------------------------------------------------------------------
ULONG
HookDriveSet(
IN ULONG DriveSet,
IN PDRIVER_OBJECT DriverObject
)
{
PHOOK_EXTENSION hookExt;
ULONG drive, i;
ULONG bit;
//
// Scan the drive table, looking for hits on the DriveSet bitmask
//
for ( drive = 0; drive < 26; ++drive )
{
bit = 1 << drive;
//
// Are we supposed to hook this drive?
//
if( (bit & DriveSet) &&
!(bit & CurrentDriveSet) )
{
//
// Try to hook drive
//
if( !HookDrive( drive, DriverObject ) )
{
//
// Remove from drive set if can't be hooked
//
DriveSet &= ~bit;
}
else
{
//
// hook drives in same drive group
//
for( i = 0; i < 26; i++ )
{
if( DriveHookDevices[i] == DriveHookDevices[ drive ] )
{
DriveSet |= ( 1 << i );
}
}
}
}
else if( !(bit & DriveSet) &&
(bit & CurrentDriveSet) )
{
//
// Unhook this drive and all in the group
//
for( i = 0; i < 26; i++ )
{
if( DriveHookDevices[i] == DriveHookDevices[ drive ] )
{
UnhookDrive( i );
DriveSet &= ~(1 << i);
}
}
}
}
//
// Return set of drives currently hooked
//
CurrentDriveSet = DriveSet;
return DriveSet;
}
//----------------------------------------------------------------------
//
// ControlCodeString
//
// Takes a control code and sees if we know what it is.
//
//----------------------------------------------------------------------
PCHAR
ControlCodeString(
PIO_STACK_LOCATION IrpSp,
ULONG ControlCode,
PCHAR Buffer,
PCHAR Other
)
{
Other[0] = 0;
switch( ControlCode )
{
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
strcpy( Buffer, "FSCTL_REQUEST_OPLOCK_LEVEL_1" );
break;
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
strcpy( Buffer, "FSCTL_REQUEST_OPLOCK_LEVEL_2" );
break;
case FSCTL_REQUEST_BATCH_OPLOCK:
strcpy( Buffer, "FSCTL_REQUEST_BATCH_OPLOCK" );
break;
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
strcpy( Buffer, "FSCTL_OPLOCK_BREAK_ACKNOWLEDGE" );
break;
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
strcpy( Buffer, "FSCTL_OPBATCH_ACK_CLOSE_PENDING" );
break;
case FSCTL_OPLOCK_BREAK_NOTIFY:
strcpy( Buffer, "FSCTL_OPLOCK_BREAK_NOTIFY" );
break;
case FSCTL_LOCK_VOLUME:
strcpy( Buffer, "FSCTL_LOCK_VOLUME" );
break;
case FSCTL_UNLOCK_VOLUME:
strcpy( Buffer, "FSCTL_UNLOCK_VOLUME" );
break;
case FSCTL_DISMOUNT_VOLUME:
strcpy( Buffer, "FSCTL_DISMOUNT_VOLUME" );
break;
case FSCTL_IS_VOLUME_MOUNTED:
strcpy( Buffer, "FSCTL_IS_VOLUME_MOUNTED" );
break;
case FSCTL_IS_PATHNAME_VALID:
strcpy( Buffer, "FSCTL_IS_PATHNAME_VALID" );
break;
case FSCTL_MARK_VOLUME_DIRTY:
strcpy( Buffer, "FSCTL_MARK_VOLUME_DIRTY" );
break;
case FSCTL_QUERY_RETRIEVAL_POINTERS:
strcpy( Buffer, "FSCTL_QUERY_RETRIEVAL_POINTERS" );
break;
case FSCTL_GET_COMPRESSION:
strcpy( Buffer, "FSCTL_GET_COMPRESSION" );
break;
case FSCTL_SET_COMPRESSION:
strcpy( Buffer, "FSCTL_SET_COMPRESSION" );
break;
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
strcpy( Buffer, "FSCTL_OPLOCK_BREAK_ACK_NO_2" );
break;
case FSCTL_QUERY_FAT_BPB:
strcpy( Buffer, "FSCTL_QUERY_FAT_BPB" );
break;
case FSCTL_REQUEST_FILTER_OPLOCK:
strcpy( Buffer, "FSCTL_REQUEST_FILTER_OPLOCK" );
break;
case FSCTL_FILESYSTEM_GET_STATISTICS:
strcpy( Buffer, "FSCTL_FILESYSTEM_GET_STATISTICS" );
break;
case FSCTL_GET_NTFS_VOLUME_DATA:
strcpy( Buffer, "FSCTL_GET_NTFS_VOLUME_DATA" );
break;
case FSCTL_GET_NTFS_FILE_RECORD:
strcpy( Buffer, "FSCTL_GET_NTFS_FILE_RECORD" );
break;
case FSCTL_GET_VOLUME_BITMAP:
strcpy( Buffer, "FSCTL_GET_VOLUME_BITMAP" );
break;
case FSCTL_GET_RETRIEVAL_POINTERS:
strcpy( Buffer, "FSCTL_GET_RETRIEVAL_POINTERS" );
break;
case FSCTL_MOVE_FILE:
strcpy( Buffer, "FSCTL_MOVE_FILE" );
break;
case FSCTL_IS_VOLUME_DIRTY:
strcpy( Buffer, "FSCTL_IS_VOLUME_DIRTY" );
break;
case FSCTL_ALLOW_EXTENDED_DASD_IO:
strcpy( Buffer, "FSCTL_ALLOW_EXTENDED_DASD_IO" );
break;
//
// *** new to Win2K (NT 5.0)
//
case FSCTL_READ_PROPERTY_DATA:
strcpy( Buffer, "FSCTL_READ_PROPERTY_DATA" );
break;
case FSCTL_WRITE_PROPERTY_DATA:
strcpy( Buffer, "FSCTL_WRITE_PROPERTY_DATA" );
break;
case FSCTL_FIND_FILES_BY_SID:
strcpy( Buffer, "FSCTL_FIND_FILES_BY_SID" );
break;
case FSCTL_DUMP_PROPERTY_DATA:
strcpy( Buffer, "FSCTL_DUMP_PROPERTY_DATA" );
break;
case FSCTL_SET_OBJECT_ID:
strcpy( Buffer, "FSCTL_SET_OBJECT_ID" );
break;
case FSCTL_GET_OBJECT_ID:
strcpy( Buffer, "FSCTL_GET_OBJECT_ID" );
break;
case FSCTL_DELETE_OBJECT_ID:
strcpy( Buffer, "FSCTL_DELETE_OBJECT_ID" );
break;
case FSCTL_SET_REPARSE_POINT:
strcpy( Buffer, "FSCTL_SET_REPARSE_POINT" );
break;
case FSCTL_GET_REPARSE_POINT:
strcpy( Buffer, "FSCTL_GET_REPARSE_POINT" );
break;
case FSCTL_DELETE_REPARSE_POINT:
strcpy( Buffer, "FSCTL_DELETE_REPARSE_POINT" );
break;
case FSCTL_ENUM_USN_DATA:
strcpy( Buffer, "FSCTL_ENUM_USN_DATA" );
break;
case FSCTL_SECURITY_ID_CHECK:
strcpy( Buffer, "FSCTL_SECURITY_ID_CHECK" );
break;
case FSCTL_READ_USN_JOURNAL:
strcpy( Buffer, "FSCTL_READ_USN_JOURNAL" );
break;
case FSCTL_SET_OBJECT_ID_EXTENDED:
strcpy( Buffer, "FSCTL_SET_OBJECT_ID_EXTENDED" );
break;
case FSCTL_CREATE_OR_GET_OBJECT_ID:
strcpy( Buffer, "FSCTL_CREATE_OR_GET_OBJECT_ID" );
break;
case FSCTL_SET_SPARSE:
strcpy( Buffer, "FSCTL_SET_SPARSE" );
break;
case FSCTL_SET_ZERO_DATA:
strcpy( Buffer, "FSCTL_SET_ZERO_DATA" );
break;
case FSCTL_QUERY_ALLOCATED_RANGES:
strcpy( Buffer, "FSCTL_QUERY_ALLOCATED_RANGES" );
break;
case FSCTL_ENABLE_UPGRADE:
strcpy( Buffer, "FSCTL_ENABLE_UPGRADE" );
break;
case FSCTL_SET_ENCRYPTION:
strcpy( Buffer, "FSCTL_SET_ENCRYPTION" );
break;
case FSCTL_ENCRYPTION_FSCTL_IO:
strcpy( Buffer, "FSCTL_ENCRYPTION_FSCTL_IO" );
break;
case FSCTL_WRITE_RAW_ENCRYPTED:
strcpy( Buffer, "FSCTL_WRITE_RAW_ENCRYPTED" );
break;
case FSCTL_READ_RAW_ENCRYPTED:
strcpy( Buffer, "FSCTL_READ_RAW_ENCRYPTED" );
break;
case FSCTL_CREATE_USN_JOURNAL:
strcpy( Buffer, "FSCTL_CREATE_USN_JOURNAL" );
break;
case FSCTL_READ_FILE_USN_DATA:
strcpy( Buffer, "FSCTL_READ_FILE_USN_DATA" );
break;
case FSCTL_WRITE_USN_CLOSE_RECORD:
strcpy( Buffer, "FSCTL_WRITE_USN_CLOSE_RECORD" );
break;
case FSCTL_EXTEND_VOLUME:
strcpy( Buffer, "FSCTL_EXTEND_VOLUME" );
break;
//
// Named pipe file system controls
// (these are all undocumented)
//
case FSCTL_PIPE_DISCONNECT:
strcpy( Buffer, "FSCTL_PIPE_DISCONNECT" );
break;
case FSCTL_PIPE_ASSIGN_EVENT:
strcpy( Buffer, "FSCTL_PIPE_ASSIGN_EVENT" );
break;
case FSCTL_PIPE_QUERY_EVENT:
strcpy( Buffer, "FSCTL_PIPE_QUERY_EVENT" );
break;
case FSCTL_PIPE_LISTEN:
strcpy( Buffer, "FSCTL_PIPE_LISTEN" );
break;
case FSCTL_PIPE_IMPERSONATE:
strcpy( Buffer, "FSCTL_PIPE_IMPERSONATE" );
break;
case FSCTL_PIPE_WAIT:
strcpy( Buffer, "FSCTL_PIPE_WAIT" );
break;
case FSCTL_PIPE_QUERY_CLIENT_PROCESS:
strcpy( Buffer, "FSCTL_QUERY_CLIENT_PROCESS" );
break;
case FSCTL_PIPE_SET_CLIENT_PROCESS:
strcpy( Buffer, "FSCTL_PIPE_SET_CLIENT_PROCESS");
break;
case FSCTL_PIPE_PEEK:
strcpy( Buffer, "FSCTL_PIPE_PEEK" );
break;
case FSCTL_PIPE_INTERNAL_READ:
strcpy( Buffer, "FSCTL_PIPE_INTERNAL_READ" );
sprintf( Other, "ReadLen: %d",
IrpSp->Parameters.DeviceIoControl.InputBufferLength );
break;
case FSCTL_PIPE_INTERNAL_WRITE:
strcpy( Buffer, "FSCTL_PIPE_INTERNAL_WRITE" );
sprintf( Other, "WriteLen: %d",
IrpSp->Parameters.DeviceIoControl.InputBufferLength );
break;
case FSCTL_PIPE_TRANSCEIVE:
strcpy( Buffer, "FSCTL_PIPE_TRANSCEIVE" );
sprintf( Other, "WriteLen: %d ReadLen: %d",
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength );
break;
case FSCTL_PIPE_INTERNAL_TRANSCEIVE:
strcpy( Buffer, "FSCTL_PIPE_INTERNAL_TRANSCEIVE" );
sprintf( Other, "WriteLen: %d ReadLen: %d",
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength );
break;
//
// Mail slot file system controls
// (these are all undocumented)
//
case FSCTL_MAILSLOT_PEEK:
strcpy( Buffer, "FSCTL_MAILSLOT_PEEK" );
break;
//
// Undocumented network redirector controls
//
case FSCTL_NETWORK_GET_CONNECTION_INFO:
strcpy( Buffer, "FSCTL_NETWORK_GET_CONNECTION_INFO" );
break;
case FSCTL_NETWORK_ENUMERATE_CONNECTIONS:
strcpy( Buffer, "FSCTL_NETWORK_ENUMERATE_CONNECTIONS");
break;
case FSCTL_NETWORK_DELETE_CONNECTION:
strcpy( Buffer, "FSCTL_NETWORK_DELETE_CONNECTION" );
break;
case FSCTL_NETWORK_SET_CONFIGURATION_INFO:
strcpy( Buffer, "FSCTL_NETWORK_SET_CONFIGURATION_INFO" );
break;
case FSCTL_NETWORK_GET_CONFIGURATION_INFO:
strcpy( Buffer, "FSCTL_NETWORK_GET_CONFIGURATION_INFO" );
break;
case FSCTL_NETWORK_GET_STATISTICS:
strcpy( Buffer, "FSCTL_NETWORK_GET_STATISTICS" );
break;
case FSCTL_NETWORK_SET_DOMAIN_NAME:
strcpy( Buffer, "FSCTL_NETWORK_SET_DOMAIN_NAME" );
break;
case FSCTL_NETWORK_REMOTE_BOOT_INIT_SCRT:
strcpy( Buffer, "FSCTL_NETWORK_REMOTE_BOOT_INIT_SCRT" );
break;
default:
sprintf( Buffer, "IOCTL: 0x%X", ControlCode );
break;
}
return Buffer;
}
//----------------------------------------------------------------------
//
// ErrorString
//
// Returns string representing the passed error condition.
//
//----------------------------------------------------------------------
PCHAR
ErrorString(
NTSTATUS RetStat,
PCHAR Buffer
)
{
switch( RetStat )
{
case STATUS_SUCCESS:
strcpy( Buffer, "SUCCESS" );
break;
case STATUS_CRC_ERROR:
strcpy( Buffer, "CRC ERROR" );
break;
case STATUS_NOT_IMPLEMENTED:
strcpy( Buffer, "NOT IMPLEMENTED" );
break;
case STATUS_EAS_NOT_SUPPORTED:
strcpy( Buffer, "EAS NOT SUPPORTED" );
break;
case STATUS_EA_TOO_LARGE:
strcpy( Buffer, "EA TOO LARGE");
break;
case STATUS_NONEXISTENT_EA_ENTRY:
strcpy( Buffer, "NONEXISTENT EA ENTRY");
break;
case STATUS_BAD_NETWORK_NAME:
strcpy( Buffer, "BAD NETWORK NAME" );
break;
case STATUS_NOTIFY_ENUM_DIR:
strcpy( Buffer, "NOTIFY ENUM DIR" );
break;
case STATUS_FILE_CORRUPT_ERROR:
strcpy( Buffer, "FILE CORRUPT" );
break;
case STATUS_DISK_CORRUPT_ERROR:
strcpy( Buffer, "DISK CORRUPT" );
break;
case STATUS_RANGE_NOT_LOCKED:
strcpy( Buffer, "RANGE NOT LOCKED" );
break;
case STATUS_FILE_CLOSED:
strcpy( Buffer, "FILE CLOSED" );
break;
case STATUS_IN_PAGE_ERROR:
strcpy( Buffer, "IN PAGE ERROR" );
break;
case STATUS_CANCELLED:
strcpy( Buffer, "CANCELLED" );
break;
case STATUS_QUOTA_EXCEEDED:
strcpy( Buffer, "QUOTA EXCEEDED" );
break;
case STATUS_NOT_SUPPORTED:
strcpy( Buffer, "NOT SUPPORTED" );
break;
case STATUS_NO_MORE_FILES:
strcpy( Buffer, "NO MORE FILES" );
break;
case STATUS_BUFFER_TOO_SMALL:
strcpy( Buffer, "BUFFER TOO SMALL" );
break;
case STATUS_OBJECT_NAME_INVALID:
strcpy( Buffer, "NAME INVALID" );
break;
case STATUS_OBJECT_NAME_NOT_FOUND:
strcpy( Buffer, "FILE NOT FOUND" );
break;
case STATUS_NOT_A_DIRECTORY:
strcpy( Buffer, "NOT A DIRECTORY" );
break;
case STATUS_NO_SUCH_FILE:
strcpy( Buffer, "NO SUCH FILE" );
break;
case STATUS_OBJECT_NAME_COLLISION:
strcpy( Buffer, "NAME COLLISION" );
break;
case STATUS_NONEXISTENT_SECTOR:
strcpy( Buffer, "NONEXISTENT SECTOR" );
break;
case STATUS_BAD_NETWORK_PATH:
strcpy( Buffer, "BAD NETWORK PATH" );
break;
case STATUS_OBJECT_PATH_NOT_FOUND:
strcpy( Buffer, "PATH NOT FOUND" );
break;
case STATUS_NO_SUCH_DEVICE:
strcpy( Buffer, "INVALID PARAMETER" );
break;
case STATUS_END_OF_FILE:
strcpy( Buffer, "END OF FILE" );
break;
case STATUS_NOTIFY_CLEANUP:
strcpy( Buffer, "NOTIFY CLEANUP" );
break;
case STATUS_BUFFER_OVERFLOW:
strcpy( Buffer, "BUFFER OVERFLOW" );
break;
case STATUS_NO_MORE_ENTRIES:
strcpy( Buffer, "NO MORE ENTRIES" );
break;
case STATUS_ACCESS_DENIED:
strcpy( Buffer, "ACCESS DENIED" );
break;
case STATUS_SHARING_VIOLATION:
strcpy( Buffer, "SHARING VIOLATION" );
break;
case STATUS_INVALID_PARAMETER:
strcpy( Buffer, "INVALID PARAMETER" );
break;
case STATUS_OPLOCK_BREAK_IN_PROGRESS:
strcpy( Buffer, "OPLOCK BREAK" );
break;
case STATUS_OPLOCK_NOT_GRANTED:
strcpy( Buffer, "OPLOCK NOT GRANTED" );
break;
case STATUS_FILE_LOCK_CONFLICT:
strcpy( Buffer, "FILE LOCK CONFLICT" );
break;
case STATUS_PENDING:
strcpy( Buffer, "PENDING" );
break;
case STATUS_REPARSE:
strcpy( Buffer, "REPARSE" );
break;
case STATUS_MORE_ENTRIES:
strcpy( Buffer, "MORE" );
break;
case STATUS_DELETE_PENDING:
strcpy( Buffer, "DELETE PEND" );
break;
case STATUS_CANNOT_DELETE:
strcpy( Buffer, "CANNOT DELETE" );
break;
case STATUS_LOCK_NOT_GRANTED:
strcpy( Buffer, "NOT GRANTED" );
break;
case STATUS_FILE_IS_A_DIRECTORY:
strcpy( Buffer, "IS DIRECTORY" );
break;
case STATUS_ALREADY_COMMITTED:
strcpy( Buffer, "ALREADY COMMITTED" );
break;
case STATUS_INVALID_EA_FLAG:
strcpy( Buffer, "INVALID EA FLAG" );
break;
case STATUS_INVALID_INFO_CLASS:
strcpy( Buffer, "INVALID INFO CLASS" );
break;
case STATUS_INVALID_HANDLE:
strcpy( Buffer, "INVALID HANDLE" );
break;
case STATUS_INVALID_DEVICE_REQUEST:
strcpy( Buffer, "INVALID DEVICE REQUEST" );
break;
case STATUS_WRONG_VOLUME:
strcpy( Buffer, "WRONG VOLUME" );
break;
case STATUS_UNEXPECTED_NETWORK_ERROR:
strcpy( Buffer, "NETWORK ERROR" );
break;
case STATUS_DFS_UNAVAILABLE:
strcpy( Buffer, "DFS UNAVAILABLE" );
break;
case STATUS_LOG_FILE_FULL:
strcpy( Buffer, "LOG FILE FULL" );
break;
case STATUS_INVALID_DEVICE_STATE:
strcpy( Buffer, "INVALID DEVICE STATE" );
break;
case STATUS_NO_MEDIA_IN_DEVICE:
strcpy( Buffer, "NO MEDIA");
break;
case STATUS_DISK_FULL:
strcpy( Buffer, "DISK FULL");
break;
case STATUS_DIRECTORY_NOT_EMPTY:
strcpy( Buffer, "NOT EMPTY");
break;
//
// Named pipe errors
//
case STATUS_INSTANCE_NOT_AVAILABLE:
strcpy( Buffer, "INSTANCE NOT AVAILABLE" );
break;
case STATUS_PIPE_NOT_AVAILABLE:
strcpy( Buffer, "PIPE NOT AVAILABLE" );
break;
case STATUS_INVALID_PIPE_STATE:
strcpy( Buffer, "INVALID PIPE STATE" );
break;
case STATUS_PIPE_BUSY:
strcpy( Buffer, "PIPE BUSY" );
break;
case STATUS_PIPE_DISCONNECTED:
strcpy( Buffer, "PIPE DISCONNECTED" );
break;
case STATUS_PIPE_CLOSING:
strcpy( Buffer, "PIPE CLOSING" );
break;
case STATUS_PIPE_CONNECTED:
strcpy( Buffer, "PIPE CONNECTED" );
break;
case STATUS_PIPE_LISTENING:
strcpy( Buffer, "PIPE LISTENING" );
break;
case STATUS_INVALID_READ_MODE:
strcpy( Buffer, "INVALID READ MODE" );
break;
case STATUS_PIPE_EMPTY:
strcpy( Buffer, "PIPE EMPTY" );
break;
case STATUS_PIPE_BROKEN:
strcpy( Buffer, "PIPE BROKEN" );
break;
case STATUS_IO_TIMEOUT:
strcpy( Buffer, "IO TIMEOUT" );
break;
default:
sprintf( Buffer, "* 0x%X", RetStat );
break;
}
return Buffer;
}
//----------------------------------------------------------------------
//
// CreateOptionsString
//
// Takes the options mask and returns a string that represents
// the settings.
//
//----------------------------------------------------------------------
PCHAR
CreateOptionsString(
ULONG Options,
PCHAR Buffer
)
{
ULONG disposition;
Buffer[0] = 0;
disposition = (Options >> 24) & 0xFF;
switch( disposition )
{
case FILE_SUPERSEDE:
strcat( Buffer, "Supersede " );
break;
case FILE_CREATE:
strcat( Buffer, "Create " );
break;
case FILE_OPEN_IF:
strcat( Buffer, "OpenIf " );
break;
case FILE_OPEN:
strcat( Buffer, "Open " );
break;
case FILE_OVERWRITE:
strcat( Buffer, "Overwrite " );
break;
case FILE_OVERWRITE_IF:
strcat( Buffer, "OverwriteIf " );
break;
}
if( Options & FILE_DIRECTORY_FILE )
strcat( Buffer, "Directory " );
if( Options & FILE_WRITE_THROUGH )
strcat( Buffer, "WriteThrough " );
if( Options & FILE_SEQUENTIAL_ONLY )
strcat( Buffer, "Sequential " );
if( Options & FILE_NO_INTERMEDIATE_BUFFERING )
strcat( Buffer, "NoBuffer" );
if( Options & FILE_OPEN_BY_FILE_ID )
strcat( Buffer, "ByID");
return Buffer;
}
//----------------------------------------------------------------------
//
// CreateAttributesString
//
// Take attributes and return a string that represents them.
//
//----------------------------------------------------------------------
PCHAR
CreateAttributesString(
USHORT Attributes,
PCHAR Buffer
)
{
Buffer[0] = 0;
if( !Attributes )
{
strcat( Buffer, "Any" );
return Buffer;
}
if( Attributes & FILE_ATTRIBUTE_COMPRESSED) strcat( Buffer, "C" );
if( Attributes & FILE_ATTRIBUTE_TEMPORARY) strcat( Buffer, "T" );
if( Attributes & FILE_ATTRIBUTE_DIRECTORY) strcat( Buffer, "D" );
if( Attributes & FILE_ATTRIBUTE_READONLY) strcat( Buffer, "R" );
if( Attributes & FILE_ATTRIBUTE_HIDDEN ) strcat( Buffer, "H" );
if( Attributes & FILE_ATTRIBUTE_SYSTEM ) strcat( Buffer, "S" );
if( Attributes & FILE_ATTRIBUTE_ARCHIVE ) strcat( Buffer, "A" );
if( Attributes & FILE_ATTRIBUTE_NORMAL ) strcat( Buffer, "N" );
return Buffer;
}
//----------------------------------------------------------------------
// F A S T I O R O U T I N E S
//
// NOTE: There is no need for us to worry about accessing fastio
// parameters within try/except because the I/O manager has either
// probed the validity of the arguments or calls within its own
// try/except block (it doesn't trust us anyway :-) ).
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// FilemonFastIoCheckIfPossible
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoCheckifPossible(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
IN BOOLEAN CheckForReadOperation,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoCheckIfPossible ) )
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoCheckIfPossible(
FileObject, FileOffset, Length,
Wait, LockKey, CheckForReadOperation, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_CHECK_IF_POSSIBLE\t%s\t%s Offset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
CheckForReadOperation ? "Read:" : "Write:",
FileOffset->LowPart, Length,
retval ? "SUCCESS" : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoRead
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoRead(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoRead ) )
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoRead(
FileObject, FileOffset, Length,
Wait, LockKey, Buffer, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_READ\t%s\tOffset: %d Length: %ld\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset->LowPart, Length,
retval ? ErrorString( IoStatus->Status, errorBuf) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoWrite
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
IN PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoWrite ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoWrite(
FileObject, FileOffset, Length, Wait, LockKey,
Buffer, IoStatus, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_WRITE\t%s\tOffset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset->LowPart, Length,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoQueryBasicinfo
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoQueryBasicInfo(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
OUT PFILE_BASIC_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
CHAR attributeString[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoQueryBasicInfo ) )
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoQueryBasicInfo(
FileObject, Wait, Buffer, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
if( retval )
{
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_QUERY_BASIC_INFO\t%s\tAttributes: %s\t%s",
FilemonGetProcess( name ), fullPathName,
NT_SUCCESS(IoStatus->Status) ?
CreateAttributesString((USHORT)((PFILE_BASIC_INFORMATION) Buffer)->FileAttributes,
attributeString ) :
"Error",
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
else
{
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_QUERY_BASIC_INFO\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, retval ? "SUCCESS" : "FAILURE" );
}
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoQueryStandardInfo
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoQueryStandardInfo(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
OUT PFILE_STANDARD_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoQueryStandardInfo ) )
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoQueryStandardInfo(
FileObject, Wait, Buffer, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
if( retval )
{
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_QUERY_STANDARD_INFO\t%s\tSize: %d\t%s",
FilemonGetProcess( name ), fullPathName,
((PFILE_STANDARD_INFORMATION) Buffer)->EndOfFile.LowPart,
retval ? "SUCCESS" : "FAILURE" );
}
else
{
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_QUERY_STANDARD_INFO\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoLock
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoLock(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PLARGE_INTEGER Length,
PEPROCESS ProcessId,
ULONG Key,
BOOLEAN FailImmediately,
BOOLEAN ExclusiveLock,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoLock ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoLock(
FileObject, FileOffset, Length, ProcessId, Key, FailImmediately,
ExclusiveLock, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_LOCK\t%s\tExcl: %s Offset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
ExclusiveLock ? "Yes" : "No", FileOffset ? FileOffset->LowPart : 0,
Length ? Length->LowPart : 0, retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoUnlockSingle
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoUnlockSingle(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PLARGE_INTEGER Length,
PEPROCESS ProcessId,
ULONG Key,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoUnlockSingle ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoUnlockSingle(
FileObject, FileOffset, Length, ProcessId, Key,
IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_UNLOCK\t%s\tOffset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset ? FileOffset->LowPart : 0, Length ? Length->LowPart : 0,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoUnlockAll
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoUnlockAll(
IN PFILE_OBJECT FileObject,
PEPROCESS ProcessId,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT(hookExt, FastIoUnlockAll ) )
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoUnlockAll(
FileObject, ProcessId, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_UNLOCK_ALL\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoUnlockAllByKey
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoUnlockAllByKey(
IN PFILE_OBJECT FileObject,
PEPROCESS ProcessId,
ULONG Key,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoUnlockAllByKey ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoUnlockAllByKey(
FileObject, ProcessId, Key, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_UNLOCK_ALL_BY_KEY\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName,
retval ? ErrorString( IoStatus->Status, errorBuf) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoQueryNetworkOpenInfo
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoQueryNetworkOpenInfo(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
OUT struct _FILE_NETWORK_OPEN_INFORMATION *Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoQueryNetworkOpenInfo ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoQueryNetworkOpenInfo(
FileObject, Wait, Buffer, IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_QUERY_NETWORK_OPEN_INFO\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoAcquireForModWrite
//
//----------------------------------------------------------------------
NTSTATUS
FilemonFastIoAcquireForModWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER EndingOffset,
OUT struct _ERESOURCE **ResourceToRelease,
IN PDEVICE_OBJECT DeviceObject
)
{
NTSTATUS retval = STATUS_NOT_IMPLEMENTED;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, errval[ERRORLEN], name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, AcquireForModWrite ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->AcquireForModWrite(
FileObject, EndingOffset, ResourceToRelease, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_ACQUIRE_FOR_MOD_WRITE\t%s\tEndOffset: %d\t%s",
FilemonGetProcess( name ), fullPathName, EndingOffset,
ErrorString( retval, errval ) );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoMdlRead
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoMdlRead(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, MdlRead ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->MdlRead(
FileObject, FileOffset, Length, LockKey, MdlChain,
IoStatus, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_MDL_READ\t%s\tOffset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset->LowPart, Length,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoMdlReadComplete
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoMdlReadComplete(
IN PFILE_OBJECT FileObject,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, MdlReadComplete ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = (BOOLEAN) hookExt->FileSystem->DriverObject->FastIoDispatch->MdlReadComplete( FileObject,
MdlChain, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_MDL_READ_COMPLETE\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, "OK" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoPrepareMdlWrite
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoPrepareMdlWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
IoStatus->Status = STATUS_NOT_IMPLEMENTED;
IoStatus->Information = 0;
if( FASTIOPRESENT( hookExt, PrepareMdlWrite ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->PrepareMdlWrite(
FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus,
hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_PREPARE_MDL_WRITE\t%s\tOffset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset->LowPart, Length,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoMdlWriteComplete
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoMdlWriteComplete(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, MdlWriteComplete ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->MdlWriteComplete(
FileObject, FileOffset, MdlChain, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_MDL_WRITE_COMPLETE\t%s\tOffset: %d\tOK",
FilemonGetProcess( name ), fullPathName, FileOffset->LowPart );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoReadCompressed
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoReadCompressed(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
OUT struct _COMPRESSED_DATA_INFO *CompressedDataInfo,
IN ULONG CompressedDataInfoLength,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoReadCompressed ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoReadCompressed(
FileObject, FileOffset, Length, LockKey, Buffer, MdlChain, IoStatus,
CompressedDataInfo, CompressedDataInfoLength, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_READ_COMPRESSED\t%s\tOffset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset->LowPart, Length,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoWriteCompressed
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoWriteCompressed(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PMDL *MdlChain,
OUT PIO_STATUS_BLOCK IoStatus,
OUT struct _COMPRESSED_DATA_INFO *CompressedDataInfo,
IN ULONG CompressedDataInfoLength,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN], errorBuf[ERRORLEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoWriteCompressed ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoWriteCompressed(
FileObject, FileOffset, Length, LockKey, Buffer, MdlChain, IoStatus,
CompressedDataInfo, CompressedDataInfoLength, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_WRITE_COMPRESSED\t%s\tOffset: %d Length: %d\t%s",
FilemonGetProcess( name ), fullPathName,
FileOffset->LowPart, Length,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoMdlReadCompleteCompressed
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoMdlReadCompleteCompressed(
IN PFILE_OBJECT FileObject,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, MdlReadCompleteCompressed ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->MdlReadCompleteCompressed(
FileObject, MdlChain, hookExt->FileSystem );
if( FilterDef.logreads && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_MDL_READ_COMPLETE_COMPRESSED\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, "OK" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoMdlWriteCompleteCompressed
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoMdlWriteCompleteCompressed(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PMDL MdlChain,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, MdlWriteCompleteCompressed ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->MdlWriteCompleteCompressed(
FileObject, FileOffset, MdlChain, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_MDL_WRITE_COMPLETE_COMPRESSED\t%s\tOffset: %d\t%s",
FilemonGetProcess( name ), fullPathName, FileOffset->LowPart, "OK" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoQueryOpen
//
// This call actually passes an IRP!
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoQueryOpen(
IN PIRP Irp,
OUT PFILE_NETWORK_OPEN_INFORMATION NetworkInformation,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
PHOOK_EXTENSION hookExt;
PFILE_OBJECT FileObject;
CHAR *fullPathName, name[PROCNAMELEN];
PIO_STACK_LOCATION currentIrpStack;
PIO_STACK_LOCATION nextIrpStack;
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoQueryOpen ))
{
currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
nextIrpStack = IoGetNextIrpStackLocation(Irp);
FileObject = currentIrpStack->FileObject;
//
// copy parameters down to next level in the stack
//
*nextIrpStack = *currentIrpStack;
nextIrpStack->DeviceObject = hookExt->FileSystem;
IoSetNextIrpStackLocation( Irp );
//
// Get path and timestamp
//
GETPATHNAME(TRUE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoQueryOpen(
Irp, NetworkInformation, hookExt->FileSystem );
//
// Reset the stack location because pre-NT 5.0 checked builds complain
//
Irp->CurrentLocation++;
Irp->Tail.Overlay.CurrentStackLocation++;
if( FilterDef.logreads && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_QUERY_OPEN\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, retval ? "SUCCESS" : "FAILURE" );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoReleaseForModWrite
//
//----------------------------------------------------------------------
NTSTATUS
FilemonFastIoReleaseForModWrite(
IN PFILE_OBJECT FileObject,
IN struct _ERESOURCE *ResourceToRelease,
IN PDEVICE_OBJECT DeviceObject
)
{
NTSTATUS retval = STATUS_NOT_IMPLEMENTED;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, errval[ERRORLEN], name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return STATUS_NOT_IMPLEMENTED;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, ReleaseForModWrite ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->ReleaseForModWrite(
FileObject, ResourceToRelease, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_RELEASE_FOR_MOD_WRITE\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, ErrorString( retval, errval ));
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoAcquireForCcFlush
//
//----------------------------------------------------------------------
NTSTATUS
FilemonFastIoAcquireForCcFlush(
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject
)
{
NTSTATUS retval = STATUS_NOT_IMPLEMENTED;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, errval[ERRORLEN], name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return STATUS_NOT_IMPLEMENTED;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, AcquireForCcFlush ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->AcquireForCcFlush(
FileObject, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_ACQUIRE_FOR_CC_FLUSH\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, ErrorString( retval, errval));
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoReleaseForCcFlush
//
//----------------------------------------------------------------------
NTSTATUS
FilemonFastIoReleaseForCcFlush(
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject
)
{
NTSTATUS retval = STATUS_NOT_IMPLEMENTED;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, errval[ERRORLEN], name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
if( !DeviceObject ) return STATUS_NOT_IMPLEMENTED;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, ReleaseForCcFlush ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->ReleaseForCcFlush(
FileObject, hookExt->FileSystem );
if( FilterDef.logwrites && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL,
&dateTime, &timeResult,
"%s\tFASTIO_RELEASE_FOR_CC_FLUSH\t%s\t\t%s",
FilemonGetProcess( name ), fullPathName, ErrorString( retval, errval) );
}
FREEPATHNAME();
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoDeviceControl
//
//----------------------------------------------------------------------
BOOLEAN
FilemonFastIoDeviceControl(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength,
IN ULONG IoControlCode,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
BOOLEAN retval = FALSE;
BOOLEAN logMutexReleased;
PHOOK_EXTENSION hookExt;
PLOG_BUF oldLog, savedCurrentLog;
CHAR fullPathName[MAXPATHLEN], name[PROCNAMELEN], errorBuf[ERRORLEN];
KIRQL oldirql;
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
hookExt = DeviceObject->DeviceExtension;
if( hookExt->Type == GUIINTERFACE )
{
//
// Its a message from our GUI!
//
IoStatus->Status = STATUS_SUCCESS; // Assume success
IoStatus->Information = 0; // Assume nothing returned
switch ( IoControlCode )
{
case IOCTL_FILEMON_VERSION:
//
// Version #
//
if( OutputBufferLength >= sizeof(ULONG))
{
*(ULONG *)OutputBuffer = FILEMONVERSION;
IoStatus->Information = sizeof(ULONG);
}
else
{
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
}
break;
case IOCTL_FILEMON_SETDRIVES:
//
// Hook and/or unhook drives
//
DbgPrint (("Filemon: set drives\n"));
if( InputBufferLength >= sizeof(ULONG) &&
OutputBufferLength >= sizeof(ULONG))
{
*(ULONG *)OutputBuffer = HookDriveSet( *(ULONG *)InputBuffer, DeviceObject->DriverObject );
IoStatus->Information = sizeof(ULONG);
}
else
{
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
}
break;
case IOCTL_FILEMON_HOOKSPECIAL:
if( InputBufferLength >= sizeof(FILE_SYSTEM_TYPE ))
{
if( !HookSpecialFs( DeviceObject->DriverObject, *(PFILE_SYSTEM_TYPE) InputBuffer ))
{
IoStatus->Status = STATUS_UNSUCCESSFUL;
}
}
else
{
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
}
break;
case IOCTL_FILEMON_UNHOOKSPECIAL:
if( InputBufferLength >= sizeof(FILE_SYSTEM_TYPE ))
{
UnhookSpecialFs( *(PFILE_SYSTEM_TYPE) InputBuffer );
}
else
{
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
}
break;
case IOCTL_FILEMON_STOPFILTER:
//
// Turn off logging
//
DbgPrint(("Filemon: stop logging\n"));
FilterOn = FALSE;
break;
case IOCTL_FILEMON_STARTFILTER:
//
// Turn on logging
//
DbgPrint(("Filemon: start logging\n"));
FilterOn = TRUE;
break;
case IOCTL_FILEMON_SETFILTER:
//
// Gui is updating the filter functions
//
DbgPrint(("Filemon: set filter\n"));
if( InputBufferLength >= sizeof(FILTER) )
{
FilterDef = *(PFILTER) InputBuffer;
FilemonUpdateFilters();
}
else
{
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
}
break;
case IOCTL_FILEMON_UNLOADQUERY:
#if DBG
//
// Is it possible to unload?
//
KeAcquireSpinLock( &CountMutex, &oldirql );
IoStatus->Information = OutstandingIRPCount;
//
// Any outstanding Irps?
//
if( !OutstandingIRPCount )
{
//
// Nope, so don't process anymore
//
UnloadInProgress = TRUE;
KeReleaseSpinLock( &CountMutex, oldirql );
//
// Stop capturing drives
//
HookDriveSet( 0, DeviceObject->DriverObject );
UnhookSpecialFs( NPFS );
UnhookSpecialFs( MSFS );
//
// Detach from all devices
//
UnloadDetach();
}
else
{
KeReleaseSpinLock( &CountMutex, oldirql );
}
#else // DBG
IoStatus->Information = 1;
#endif // DBG
break;
case IOCTL_FILEMON_ZEROSTATS:
//
// Reset all output buffers
//
DbgPrint (("Filemon: zero stats\n"));
ExAcquireFastMutex( &LogMutex );
while( CurrentLog->Next )
{
//
// Free all but the first output buffer
//
oldLog = CurrentLog->Next;
CurrentLog->Next = oldLog->Next;
ExFreePool( oldLog );
NumLog--;
}
//
// Set the output pointer to the start of the output buffer
//
CurrentLog->Len = 0;
Sequence = 0;
ExReleaseFastMutex( &LogMutex );
break;
case IOCTL_FILEMON_GETSTATS:
//
// Copy the oldest output buffer to the caller
//
DbgPrint (("Filemon: get stats\n"));
//
// If the output buffer is too large to fit into the caller's buffer
//
if( LOGBUFSIZE > OutputBufferLength )
{
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
return FALSE;
}
//
// Probe the output buffer
//
try
{
ProbeForWrite( OutputBuffer,
OutputBufferLength,
sizeof( UCHAR ));
}
except( EXCEPTION_EXECUTE_HANDLER )
{
IoStatus->Status = STATUS_INVALID_PARAMETER;
return FALSE;
}
//
// We're okay, lock the buffer pool
//
ExAcquireFastMutex( &LogMutex );
if( CurrentLog->Len || CurrentLog->Next )
{
//
// Start output to a new output buffer
//
FilemonAllocateLog();
//
// Fetch the oldest to give to user
//
oldLog = FilemonGetOldestLog();
if( oldLog != CurrentLog )
{
logMutexReleased = TRUE;
ExReleaseFastMutex( &LogMutex );
}
else
{
logMutexReleased = FALSE;
}
//
// Copy it to the caller's buffer
//
memcpy( OutputBuffer, oldLog->Data, oldLog->Len );
//
// Return length of copied info
//
IoStatus->Information = oldLog->Len;
//
// Deallocate buffer - unless its the last one
//
if( logMutexReleased )
{
ExFreePool( oldLog );
}
else
{
CurrentLog->Len = 0;
ExReleaseFastMutex( &LogMutex );
}
}
else
{
//
// There is no unread data
//
ExReleaseFastMutex( &LogMutex );
IoStatus->Information = 0;
}
break;
default:
//
// Unknown control
//
DbgPrint (("Filemon: unknown IRP_MJ_DEVICE_CONTROL\n"));
IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
retval = TRUE;
}
else
{
//
// Its a call for a file system, so pass it through
//
if( FASTIOPRESENT( hookExt, FastIoDeviceControl ) )
{
FilemonGetFullPath( FALSE, FileObject, hookExt, fullPathName );
TIMESTAMPSTART();
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoDeviceControl(
FileObject, Wait, InputBuffer, InputBufferLength, OutputBuffer,
OutputBufferLength, IoControlCode, IoStatus, hookExt->FileSystem );
if(hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL, &dateTime, &timeResult,
"%s\tFASTIO_DEVICE_CONTROL\t%s\tIOCTL: 0x%X\t%s",
FilemonGetProcess( name ), fullPathName,
IoControlCode,
retval ? ErrorString( IoStatus->Status, errorBuf ) : "FAILURE" );
}
}
}
return retval;
}
//----------------------------------------------------------------------
//
// FilemonFastIoAcquireFile
//
//----------------------------------------------------------------------
VOID
FilemonFastIoAcquireFile(
PFILE_OBJECT FileObject
)
{
PDEVICE_OBJECT deviceObject, checkDevice;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
//
// We've got to locate our own device object
//
checkDevice = FileObject->DeviceObject->Vpb->DeviceObject;
while( checkDevice )
{
if( checkDevice->DriverObject == FilemonDriver )
{
//
// Found it
//
deviceObject = checkDevice;
hookExt = deviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, AcquireFileForNtCreateSection ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
hookExt->FileSystem->DriverObject->FastIoDispatch->AcquireFileForNtCreateSection(
FileObject );
if( FilterDef.logreads && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL, &dateTime, &timeResult,
"%s\tFASTIO_ACQUIRE_FILE\t%s\t\tOK", FilemonGetProcess( name ),
fullPathName );
}
FREEPATHNAME();
}
return;
}
checkDevice = checkDevice->AttachedDevice;
}
}
//----------------------------------------------------------------------
//
// FilemonFastIoReleaseFile
//
//----------------------------------------------------------------------
VOID
FilemonFastIoReleaseFile(
PFILE_OBJECT FileObject
)
{
PDEVICE_OBJECT deviceObject, checkDevice;
PHOOK_EXTENSION hookExt;
CHAR *fullPathName, name[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
//
// We've got to locate our own device object
//
checkDevice = FileObject->DeviceObject->Vpb->DeviceObject;
while( checkDevice )
{
if( checkDevice->DriverObject == FilemonDriver )
{
deviceObject = IoGetRelatedDeviceObject( FileObject );
hookExt = deviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, ReleaseFileForNtCreateSection ))
{
GETPATHNAME(FALSE);
TIMESTAMPSTART();
hookExt->FileSystem->DriverObject->FastIoDispatch->ReleaseFileForNtCreateSection( FileObject );
if( FilterDef.logreads && hookExt->Hooked)
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL, &dateTime, &timeResult,
"%s\tFASTIO_RELEASE_FILE\t%s\t\tOK", FilemonGetProcess( name ),
fullPathName );
}
FREEPATHNAME();
}
return;
}
checkDevice = checkDevice->AttachedDevice;
}
}
//----------------------------------------------------------------------
//
// FilemonFastIoDetachDevice
//
// We get this call when a device that we have hooked is being deleted.
// This happens when, for example, a floppy is formatted. We have
// to detach from it and delete our device. We should notify the GUI
// that the hook state has changed, but its not worth the trouble.
//
//----------------------------------------------------------------------
VOID
FilemonFastIoDetachDevice(
PDEVICE_OBJECT SourceDevice,
PDEVICE_OBJECT TargetDevice
)
{
PHOOK_EXTENSION hookExt;
ULONG i;
CHAR name[PROCNAMELEN], drive[PROCNAMELEN];
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
LARGE_INTEGER dateTime;
//
// See if a device (like a floppy) is being removed out from under us. If so,
// we have to detach from it before it disappears
//
for( i = 0; i < 26; i++ )
{
if( SourceDevice == DriveHookDevices[i] )
{
//
// We've hooked it, so we must detach
//
hookExt = SourceDevice->DeviceExtension;
DbgPrint(("Filemon: Detaching from drive: %c\n",
hookExt->LogicalDrive ));
TIMESTAMPSTART();
sprintf( drive, "%c:", hookExt->LogicalDrive );
if( hookExt->Hooked )
{
TIMESTAMPSTOP();
LogRecord( TRUE, NULL, &dateTime, &timeResult,
"%s\tFASTIO_DETACH_DEVICE\t%s\t\tOK",
FilemonGetProcess( name ), drive );
}
IoDetachDevice( TargetDevice );
IoDeleteDevice( SourceDevice );
DriveHookDevices[i] = NULL;
return;
}
}
//
// It wasn't for us, so pass it on.
//
hookExt = SourceDevice->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastIoDetachDevice ))
{
hookExt->FileSystem->DriverObject->FastIoDispatch->FastIoDetachDevice(
SourceDevice, TargetDevice );
}
}
//----------------------------------------------------------------------
// D I S P A T C H A N D H O O K E N T R Y P O I N T S
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// FilemonHookDoneWork
//
// Worker routine that simply calls update Log. Since we want
// to avoid using spin locks in order to improve SMP performance
// we need to do everything at passive. When our completion routine
// is called at dispatch, we queue the update off to a worker thread.
//
//----------------------------------------------------------------------
VOID
FilemonHookDoneWork(
PVOID Context
)
{
PFILEMON_WORK filemonWork = (PFILEMON_WORK) Context;
DbgPrint(("HookWorkRoutine\n"));
LogRecord( FALSE, &filemonWork->Sequence,
NULL,
&filemonWork->TimeResult,
filemonWork->ErrString );
ExFreePool( filemonWork );
}
//----------------------------------------------------------------------
//
// FilemonHookDone
//
// Gets control after a filesystem operation has completed so that
// we can get return status information about it.
//
//----------------------------------------------------------------------
NTSTATUS
FilemonHookDone(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PIO_STACK_LOCATION IrpSp;
#if defined(_IA64_)
ULONG seqNum = (ULONG) ((ULONG_PTR)Context);
#else
ULONG seqNum = (ULONG) Context;
#endif
CHAR errval[ERRORLEN], errString[ERRORLEN];
KIRQL oldirql;
LARGE_INTEGER timeStampStart, timeStampComplete, timeResult;
PFILEMON_WORK filemonWorkContext;
//
// A request completed - look at the result
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Log the return status in the output buffer. Tag it with the
// sequence number so that the GUI can match it with the IRP input information.
//
if( FilterOn )
{
//
// Quick, get the completion time
//
timeStampStart = IrpSp->Parameters.Read.ByteOffset;
timeStampComplete = KeQueryPerformanceCounter(NULL);
timeResult.QuadPart = timeStampComplete.QuadPart - timeStampStart.QuadPart;
//
// Queue off to a worker thread if we have to
//
if( KeGetCurrentIrql() == DISPATCH_LEVEL )
{
filemonWorkContext = ExAllocatePool( NonPagedPool, sizeof(FILEMON_WORK));
if( filemonWorkContext )
{
filemonWorkContext->Sequence = seqNum;
filemonWorkContext->TimeResult = timeResult;
sprintf( filemonWorkContext->ErrString, "\t\t\t\t%s",
ErrorString( Irp->IoStatus.Status, errval ));
ExInitializeWorkItem( &filemonWorkContext->WorkItem,
FilemonHookDoneWork, filemonWorkContext );
ExQueueWorkItem( &filemonWorkContext->WorkItem, CriticalWorkQueue );
}
}
else
{
sprintf( errString, "\t\t\t\t%s", ErrorString( Irp->IoStatus.Status, errval ));
LogRecord( FALSE, &seqNum, NULL, &timeResult, errString );
}
}
#if DBG
//
// We have finished processing an IRP so decrement oustanding IRP count
//
KeAcquireSpinLock( &CountMutex, &oldirql );
OutstandingIRPCount--;
DbgPrint(("-%d: %x\n", OutstandingIRPCount, Irp ));;
if( !OutstandingIRPCount ) FilemonDriver->DriverUnload = FilemonUnload;
KeReleaseSpinLock( &CountMutex, oldirql );
#endif
//
// Now we have to mark Irp as pending if necessary
//
if( Irp->PendingReturned )
{
IoMarkIrpPending( Irp );
}
return Irp->IoStatus.Status;
}
//----------------------------------------------------------------------
//
// FilemonHookRoutine
//
// This routine is the main hook routine where we figure out what
// calls are being sent to the file system.
//
//----------------------------------------------------------------------
NTSTATUS
FilemonHookRoutine(
PDEVICE_OBJECT HookDevice,
IN PIRP Irp
)
{
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
PMOVE_FILE_DATA moveFile;
PQUERY_DIRECTORY queryDirectory;
PFILE_OBJECT FileObject;
PHOOK_EXTENSION hookExt;
LARGE_INTEGER dateTime;
LARGE_INTEGER perfTime;
PCHAR fullPathName = NULL;
BOOLEAN hookCompletion, createPath;
CHAR controlCodeBuffer[ERRORLEN];
CHAR attributeString[ERRORLEN];
CHAR optionString[ERRORLEN];
CHAR name[PROCNAMELEN];
ULONG i;
ANSI_STRING directoryFilter;
PCHAR queryFilter;
ULONG seqNum;
KIRQL oldirql;
//
// Extract the file object from the IRP
//
FileObject = currentIrpStack->FileObject;
//
// Point at the device extension, which contains information on which
// file system this IRP is headed for
//
hookExt = HookDevice->DeviceExtension;
//
// We note open cases so that when we query the file name
// we don't ask the file system for the name (since it won't
// have seen the file object yet).
//
if( currentIrpStack->MajorFunction == IRP_MJ_CREATE ||
currentIrpStack->MajorFunction == IRP_MJ_CREATE_NAMED_PIPE ||
currentIrpStack->MajorFunction == IRP_MJ_CREATE_MAILSLOT )
{
//
// Clear any existing fileobject/name association stored in the
// hash table
//
FilemonFreeHashEntry( FileObject );
createPath = TRUE;
}
else if( currentIrpStack->MajorFunction == IRP_MJ_CLOSE )
{
//
// We treat close as a special case of create for name querying
// since calling into NTFS during a close can result in a deadlock.
//
createPath = TRUE;
}
else if( currentIrpStack->MajorFunction == IRP_MJ_CLEANUP &&
FileObject->Flags & FO_STREAM_FILE )
{
//
// Treat cleanup of stream file objects as special create case, because
// querying them causes NTFS to screwup on NT 4
//
createPath = TRUE;
}
else
{
createPath = FALSE;
}
//
// Allocate a buffer and get the name only if we have to
//
if( FilterOn && hookExt->Hooked )
{
GETPATHNAME( createPath );
}
//
// Only log it if it passes the filter
//
if( hookExt->Hooked && fullPathName )
{
//
// If measuring absolute time go and get the timestamp.
//
KeQuerySystemTime( &dateTime );
perfTime = KeQueryPerformanceCounter( NULL );
//
// We want to watch this IRP complete
//
seqNum = (ULONG) - 1;
hookCompletion = FALSE;
//
// Determine what function we're dealing with
//
FilemonGetProcess( name );
switch( currentIrpStack->MajorFunction )
{
case IRP_MJ_CREATE:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_CREATE\t%s\tAttributes: %s Options: %s",
name, fullPathName,
CreateAttributesString( currentIrpStack->Parameters.Create.FileAttributes,
attributeString ),
CreateOptionsString( currentIrpStack->Parameters.Create.Options,
optionString ));
//
// If its an open-by-id we free the hash entry now so that on the next access to
// the file we'll pick up the file's real name.
//
if( currentIrpStack->Parameters.Create.Options & FILE_OPEN_BY_FILE_ID )
{
FilemonFreeHashEntry( FileObject );
}
break;
case IRP_MJ_CREATE_NAMED_PIPE:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_CREATE_NAMED_PIPE\t%s\tAttributes: %s Options: %s",
name, fullPathName,
CreateAttributesString( currentIrpStack->Parameters.Create.FileAttributes,
attributeString ),
CreateOptionsString( currentIrpStack->Parameters.Create.Options,
optionString ));
break;
case IRP_MJ_CREATE_MAILSLOT:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_CREATE_MAILSLOT\t%s\tAttributes: %s Options: %s",
name, fullPathName,
CreateAttributesString( currentIrpStack->Parameters.Create.FileAttributes,
attributeString ),
CreateOptionsString( currentIrpStack->Parameters.Create.Options,
optionString ));
break;
case IRP_MJ_READ:
if( FilterDef.logreads )
{
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_READ%c\t%s\tOffset: %d Length: %d",
name,
(Irp->Flags & IRP_PAGING_IO) ||
(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) ? '*' : ' ',
fullPathName,
currentIrpStack->Parameters.Read.ByteOffset.LowPart,
currentIrpStack->Parameters.Read.Length );
}
break;
case IRP_MJ_WRITE:
if( FilterDef.logwrites )
{
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_WRITE%c\t%s\tOffset: %d Length: %d",
name,
(Irp->Flags & IRP_PAGING_IO) ||
(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) ? '*' : ' ',
fullPathName,
currentIrpStack->Parameters.Write.ByteOffset.LowPart,
currentIrpStack->Parameters.Write.Length );
}
break;
case IRP_MJ_CLOSE:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_CLOSE%c\t%s\t",
name,
(Irp->Flags & IRP_PAGING_IO) ||
(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) ? '*' : ' ',
fullPathName );
//
// This fileobject/name association can be discarded now.
//
FilemonFreeHashEntry( FileObject );
break;
case IRP_MJ_FLUSH_BUFFERS:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_FLUSH\t%s\t", name, fullPathName );
break;
case IRP_MJ_QUERY_INFORMATION:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_QUERY_INFORMATION\t%s\t%s",
name, fullPathName,
FileInformation[currentIrpStack->Parameters.QueryFile.FileInformationClass] );
break;
case IRP_MJ_SET_INFORMATION:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_SET_INFORMATION%c\t%s\t%s",
name,
(Irp->Flags & IRP_PAGING_IO) ||
(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) ? '*' : ' ',
fullPathName,
FileInformation[currentIrpStack->Parameters.SetFile.FileInformationClass] );
//
// If its a rename, cleanup the name association.
//
if( currentIrpStack->Parameters.SetFile.FileInformationClass ==
FileRenameInformation )
{
FilemonFreeHashEntry( FileObject );
}
break;
case IRP_MJ_QUERY_EA:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_QUERY_EA\t%s\t", name, fullPathName );
break;
case IRP_MJ_SET_EA:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_SET_EA\t%s\t", name, fullPathName );
break;
case IRP_MJ_QUERY_VOLUME_INFORMATION:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_QUERY_VOLUME_INFORMATION\t%s\t%s",
name, fullPathName,
VolumeInformation[currentIrpStack->Parameters.QueryVolume.FsInformationClass] );
break;
case IRP_MJ_SET_VOLUME_INFORMATION:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_SET_VOLUME_INFORMATION\t%s\t%s",
name, fullPathName,
VolumeInformation[currentIrpStack->Parameters.QueryVolume.FsInformationClass] );
break;
case IRP_MJ_DIRECTORY_CONTROL:
switch( currentIrpStack->MinorFunction )
{
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_DIRECTORY_CONTROL\t%s\tChange Notify",
name, fullPathName );
break;
case IRP_MN_QUERY_DIRECTORY:
queryDirectory = (PQUERY_DIRECTORY)¤tIrpStack->Parameters;
queryFilter = NULL;
if( queryDirectory->FileName )
{
if( NT_SUCCESS( RtlUnicodeStringToAnsiString( &directoryFilter,
queryDirectory->FileName, TRUE )))
{
queryFilter = ExAllocatePool( PagedPool, directoryFilter.Length + 1 );
if( queryFilter )
{
memcpy( queryFilter, directoryFilter.Buffer, directoryFilter.Length );
queryFilter[ directoryFilter.Length ] = 0;
//
// Massage DOS-internal wildcards
//
for( i = 0; i < strlen( queryFilter ); i++ )
{
if( queryFilter[i] == '<' ) queryFilter[i] = '*';
else if( queryFilter[i] == '>' ) queryFilter[i] = '?';
}
}
RtlFreeAnsiString( &directoryFilter );
}
}
if( queryFilter )
{
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_DIRECTORY_CONTROL\t%s\t%s: %s",
name, fullPathName,
FileInformation[queryDirectory->FileInformationClass],
queryFilter );
ExFreePool( queryFilter );
}
else
{
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_DIRECTORY_CONTROL\t%s\t%s",
name, fullPathName,
FileInformation[queryDirectory->FileInformationClass] );
}
break;
default:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_DIRECTORY_CONTROL\t%s\t",
name, fullPathName );
break;
}
break;
case IRP_MJ_FILE_SYSTEM_CONTROL:
switch( currentIrpStack->Parameters.DeviceIoControl.IoControlCode )
{
case FSCTL_MOVE_FILE:
moveFile = (PMOVE_FILE_DATA) Irp->AssociatedIrp.SystemBuffer;
sprintf( optionString, "Vcn: %d Len: %d Target: %d",
moveFile->StartingVcn.LowPart,
moveFile->ClusterCount,
moveFile->StartingLcn.LowPart );
ObReferenceObjectByHandle( moveFile->FileHandle, 0, NULL, KernelMode, &FileObject, NULL );
FilemonGetFullPath( FALSE, FileObject, hookExt, fullPathName );
ObDereferenceObject( FileObject );
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tFSCTL_MOVE_FILE\t%s\t%s",
name, fullPathName, optionString );
break;
default:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\t%s\t%s\t%s",
name,
ControlCodeString( currentIrpStack,
currentIrpStack->Parameters.DeviceIoControl.IoControlCode,
controlCodeBuffer, optionString ),
fullPathName, optionString );
}
break;
case IRP_MJ_SHUTDOWN:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_SHUTDOWN\t\t", name );
break;
case IRP_MJ_LOCK_CONTROL:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_LOCK_CONTROL\t%s\tOffset: %d Length: %d",
name, fullPathName,
((PLOCK_CONTROL)¤tIrpStack->Parameters)->ByteOffset.LowPart,
((PLOCK_CONTROL)¤tIrpStack->Parameters)->Length ?
((PLOCK_CONTROL)¤tIrpStack->Parameters)->Length->LowPart : 0 );
break;
case IRP_MJ_CLEANUP:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_CLEANUP\t%s\t", name, fullPathName );
break;
case IRP_MJ_DEVICE_CONTROL:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_DEVICE_CONTROL\t%s\tIOCTL: 0x%X", name,
fullPathName, currentIrpStack->Parameters.DeviceIoControl.IoControlCode );
break;
case IRP_MJ_QUERY_SECURITY:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_QUERY_SECURITY\t%s\t",
name, fullPathName );
break;
case IRP_MJ_SET_SECURITY:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_SET_SECURITY\t%s\t",
name, fullPathName );
break;
case IRP_MJ_POWER:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_POWER\t%s\tMinor: %x",
name, fullPathName,
currentIrpStack->MinorFunction );
break;
case IRP_MJ_PNP:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\tIRP_MJ_PNP\t%s\t%s",
name, fullPathName,
currentIrpStack->MinorFunction <= IRP_MN_QUERY_LEGACY_BUS_INFORMATION ?
PnpMinorCode[currentIrpStack->MinorFunction] : "New minor code" );
break;
default:
hookCompletion = LogRecord( TRUE, &seqNum, &dateTime, NULL,
"%s\t*UNKNOWN* 0x%X\t\t", name, currentIrpStack->MajorFunction );
break;
}
}
else
{
//
// We don't care about this IRP's completion
//
hookCompletion = FALSE;
//
// Do name processing for the sake of keeping the hash table current
//
switch( currentIrpStack->MajorFunction )
{
case IRP_MJ_CLOSE:
//
// This fileobject/name association can be discarded now.
//
FilemonFreeHashEntry( FileObject );
break;
}
}
//
// Free the buffer if we have one
//
if( fullPathName && fullPathName != InsufficientResources )
{
ExFreeToNPagedLookasideList( &FullPathLookaside, fullPathName );
}
//
// Copy parameters down to next level in the stack for the driver below us
//
*nextIrpStack = *currentIrpStack;
#if DBG
//
// If an unload isn't in progress, we should register a completion callback
// so that the IRP's return status can be examined.
//
KeAcquireSpinLock( &CountMutex, &oldirql );
#endif
if( !UnloadInProgress && hookCompletion )
{
#if DBG
//
// Increment the outstanding IRP count since this IRP will be headed
// for our completion routine
//
FilemonDriver->DriverUnload = NULL;
OutstandingIRPCount++;
DbgPrint(("+%d: %x\n", OutstandingIRPCount, Irp ));;
#endif // DBG
//
// Grab the time stamp and Log it in the current stack location. This
// is legal since the stack location is ours, and we're done looking at
// the parameters. This makes it easy to pass this to the completion routine. The
// DiskPerf example in the NT DDK uses this trick.
//
currentIrpStack->Parameters.Read.ByteOffset = perfTime;
#if defined(_IA64_)
IoSetCompletionRoutine( Irp, FilemonHookDone, (PVOID) (ULONG_PTR) seqNum, TRUE, TRUE, TRUE );
#else
IoSetCompletionRoutine( Irp, FilemonHookDone, (PVOID) seqNum, TRUE, TRUE, TRUE );
#endif
}
else
{
//
// Set no completion routine
//
IoSetCompletionRoutine( Irp, FilemonHookDone, NULL, FALSE, FALSE, FALSE );
}
#if DBG
KeReleaseSpinLock( &CountMutex, oldirql );
#endif
//
// Return the results of the call to the caller
//
return IoCallDriver( hookExt->FileSystem, Irp );
}
//----------------------------------------------------------------------
//
// FilemonDeviceRoutine
//
// In this routine we handle requests to our own device. The only
// requests we care about handling explicitely are IOCTL commands that
// we will get from the GUI. We also expect to get Create and Close
// commands when the GUI opens and closes communications with us.
//
//----------------------------------------------------------------------
NTSTATUS
FilemonDeviceRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION irpStack;
PVOID inputBuffer;
PVOID outputBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
//
// Go ahead and set the request up as successful
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
//
// Get a pointer to the current location in the Irp. This is where
// the function codes and parameters are located.
//
irpStack = IoGetCurrentIrpStackLocation (Irp);
//
// Get the pointer to the input/output buffer and its length
//
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
switch (irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
DbgPrint(("Filemon: IRP_MJ_CREATE\n"));
//
// Start the sequence number at 0
//
Sequence = 0;
break;
case IRP_MJ_CLOSE:
DbgPrint(("Filemon: IRP_MJ_CLOSE\n"));
//
// A GUI is closing communication
//
FilterOn = FALSE;
//
// If the GUI has no more references to us, reset the output
// buffers and hash table.
//
FilemonResetLog();
FilemonHashCleanup();
//
// Stop capturing drives
//
HookDriveSet( 0, DeviceObject->DriverObject );
UnhookSpecialFs( NPFS );
UnhookSpecialFs( MSFS );
break;
case IRP_MJ_DEVICE_CONTROL:
//
// This path will never execute because we have registered a
// fast I/O path for device control. That means that the fast I/O entry
// point will ALWAYS be called for Device Control operations
//
DbgPrint (("Filemon: IRP_MJ_DEVICE_CONTROL\n"));
//
// Get output buffer if its passed as an MDL
//
if( Irp->MdlAddress )
{
outputBuffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
}
//
// Its a request from the GUI. Simply call our fast handler.
//
FilemonFastIoDeviceControl( irpStack->FileObject, TRUE,
inputBuffer, inputBufferLength,
outputBuffer, outputBufferLength,
ioControlCode, &Irp->IoStatus, DeviceObject );
break;
}
//
// Complete the IRP
//
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------
//
// FilemonDispatch
//
// Based on which device the Irp is destined for we call either the
// filesystem filter function, or our own device handling routine.
//
//----------------------------------------------------------------------
NTSTATUS
FilemonDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// Determine if its a request from the GUI to us, or one that is
// directed at a file system driver that we've hooked
//
if( ((PHOOK_EXTENSION) DeviceObject->DeviceExtension)->Type == GUIINTERFACE )
{
return FilemonDeviceRoutine( DeviceObject, Irp );
}
else
{
return FilemonHookRoutine( DeviceObject, Irp );
}
}
//----------------------------------------------------------------------
//
// FilemonUnload
//
// Our job is done - time to leave. Note that this function is
// only called when debugging is on, since in reality it is not safe
// to detach filter devices or unload a filter driver.
//
//----------------------------------------------------------------------
VOID
FilemonUnload(
IN PDRIVER_OBJECT DriverObject
)
{
WCHAR deviceLinkBuffer[] = L"\\DosDevices\\Filemon";
UNICODE_STRING deviceLinkUnicodeString;
//
// Delete the symbolic link for our GUI device
//
RtlInitUnicodeString( &deviceLinkUnicodeString, deviceLinkBuffer );
IoDeleteSymbolicLink( &deviceLinkUnicodeString );
DbgPrint(("Filemon.SYS: unloading\n"));
//
// The only device object left should be the GUI device, since
// we delete devices in our unload check.
//
IoDeleteDevice( DriverObject->DeviceObject );
DbgPrint(("Filemon.SYS: deleted devices\n"));
//
// Now we can free any memory that is allocated
//
FilemonFreeFilters();
FilemonHashCleanup();
FilemonFreeLog();
ExDeleteNPagedLookasideList( &FullPathLookaside );
//
// Delete the resources
//
ExDeleteResourceLite( &FilterResource );
ExDeleteResourceLite( &HashResource );
DbgPrint(("Filemon.SYS: freed memory\n"));
}
//----------------------------------------------------------------------
//
// DriverEntry
//
// Installable driver initialization. Here we just set ourselves up.
//
//----------------------------------------------------------------------
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
NTSTATUS ntStatus;
PDEVICE_OBJECT guiDevice;
WCHAR deviceNameBuffer[] = L"\\Device\\Filemon";
UNICODE_STRING deviceNameUnicodeString;
WCHAR deviceLinkBuffer[] = L"\\DosDevices\\Filemon";
UNICODE_STRING deviceLinkUnicodeString;
ULONG i;
DbgPrint (("Filemon.SYS: entering DriverEntry\n"));
FilemonDriver = DriverObject;
//
// Setup the device name
//
RtlInitUnicodeString (&deviceNameUnicodeString,
deviceNameBuffer );
//
// Create the device used for GUI communications
//
ntStatus = IoCreateDevice ( DriverObject,
sizeof(HOOK_EXTENSION),
&deviceNameUnicodeString,
FILE_DEVICE_FILEMON,
0,
TRUE,
&guiDevice );
//
// If successful, make a symbolic link that allows for the device
// object's access from Win32 programs
//
if(NT_SUCCESS(ntStatus))
{
//
// Mark this as our GUI device
//
((PHOOK_EXTENSION) guiDevice->DeviceExtension)->Type = GUIINTERFACE;
//
// Create a symbolic link that the GUI can specify to gain access
// to this driver/device
//
RtlInitUnicodeString (&deviceLinkUnicodeString,
deviceLinkBuffer );
ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
&deviceNameUnicodeString );
if(!NT_SUCCESS(ntStatus))
{
DbgPrint (("Filemon.SYS: IoCreateSymbolicLink failed\n"));
IoDeleteDevice( guiDevice );
return ntStatus;
}
//
// Create dispatch points for all routines that must be handled.
// All entry points are registered since we might filter a
// file system that processes all of them.
//
for( i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++ )
{
DriverObject->MajorFunction[i] = FilemonDispatch;
}
#if DBG
//
// Driver unload is only set if we are debugging Filemon. This is
// because unloading a filter is not really safe - threads could
// be in our fastio routines (or about to enter them), for example,
// and there is no way to tell. When debugging, we can risk the
// occasional unload crash as a trade-off for not having to
// reboot as often.
//
// DriverObject->DriverUnload = FilemonUnload;
#endif // DBG
//
// Set up the Fast I/O dispatch table
//
DriverObject->FastIoDispatch = &FastIOHook;
}
else
{
//
// If something went wrong, cleanup the device object and don't load
//
DbgPrint(("Filemon: Failed to create our device!\n"));
return ntStatus;
}
//
// Initialize the name hash table
//
for(i = 0; i < NUMHASH; i++ ) HashTable[i] = NULL;
//
// Find the process name offset
//
ProcessNameOffset = FilemonGetProcessNameOffset();
//
// Initialize the synchronization objects
//
#if DBG
KeInitializeSpinLock( &CountMutex );
#endif
ExInitializeFastMutex( &LogMutex );
ExInitializeResourceLite( &FilterResource );
ExInitializeResourceLite( &HashResource );
//
// Initialize a lookaside for file names
//
ExInitializeNPagedLookasideList( &FullPathLookaside, NULL, NULL,
0, MAXPATHLEN, 'mliF', 256 );
//
// Allocate the first output buffer
//
CurrentLog = ExAllocatePool( NonPagedPool, sizeof(*CurrentLog) );
if( !CurrentLog )
{
//
// Oops - we can't do anything without at least one buffer
//
IoDeleteSymbolicLink( &deviceLinkUnicodeString );
IoDeleteDevice( guiDevice );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Set the buffer pointer to the start of the buffer just allocated
//
CurrentLog->Len = 0;
CurrentLog->Next = NULL;
NumLog = 1;
return STATUS_SUCCESS;
}