现在的位置: 首页 > 综合 > 正文

FileMon.c

2013年05月12日 ⁄ 综合 ⁄ 共 44025字 ⁄ 字号 评论关闭
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[] = "<INSUFFICIENT MEMORY>";

//
// 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: <Out of Memory>", NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) sprintf( fullPathName, "%s: <Out of Memory>", MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C: <Out of Memory>", 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: <Out of Memory>", NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) sprintf( fullPathName, "%s: <Out of Memory>", MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C: <Out of Memory>", 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, " <Name Too Long>" );

}
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'v

抱歉!评论已关闭.