Article View: borland.public.cppbuilder.nativeapi
Article #273Re: Hard Drive Access
From: Bruce Larrabee
Date: Sun, 25 May 2008 14:34
Date: Sun, 25 May 2008 14:34
1324 lines
50851 bytes
50851 bytes
Hi Jeff,
Here's some samples from the filemon.exe program's
source code (filemon.exe is a precursor to the more
versatile (and contemporary...) program you refer to
I assume you are referring to Process Monitor?
(Procmon.exe) not performance monitor...
Here are some sample functions related to your needs:
The last three look particularly interesting... :)
If you have any questions you can email me
(bruce_l @ westbrookent . com) or you post
here...
This function comes from the source for the windows executable
version of file monitor, filemon.exe:
/************************************************************
*
* FUNCTION: HookDrives
*
* PURPOSE: Hook the currently selected drives,
* updating menu checks
*
************************************************************/
DWORD HookDrives( HMENU DriveMenu, DWORD MaxDriveSet, DWORD CurDriveSet )
{
DWORD nb;
DWORD drive;
// Tell device driver which drives to monitor
if(!DeviceIoControl(
SysHandle,
IOCTL_FILEMON_SETDRIVES,
&CurDriveSet, sizeof CurDriveSet,
&CurDriveSet, sizeof CurDriveSet,
&nb, NULL
))
{
return 0;
}
// Update menu items
for( drive = 0; drive < 32; ++drive )
{
if ( MaxDriveSet & (1<<drive) )
{
if ( CurDriveSet & (1<<drive) )
{
CheckMenuItem( DriveMenu, IDC_DRIVE+drive, MF_BYCOMMAND|MF_CHECKED );
}
else
{
CheckMenuItem( DriveMenu, IDC_DRIVE+drive, MF_BYCOMMAND|MF_UNCHECKED );
}
}
}
return CurDriveSet;
}
This code is from the source code for the 'vxd' version of file monitor:
//----------------------------------------------------------------------
// H O O K R O U T I N E
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
// FilemonHookProc
//
// All (most) IFS functions come through this routine for us to look
// at.
//
//----------------------------------------------------------------------
int _cdecl FilemonHookProc( pIFSFunc pfn, int fn, int Drive, int ResType, int CodePage, pioreq pir)
{
int retval;
char fullpathname[MAXPATHLEN];
char processname[64];
char data[MAXPATHLEN];
char drivestring[4];
ifsreq origifsr;
pioreq origir;
_WIN32_FIND_DATA *finddata;
struct srch_entry *search;
BOOLEAN log;
_QWORD result;
DWORD timelo, timehi;
DWORD timelo1, timehi1;
DWORD dostime, dosdate;
DWORD datetimelo, datetimehi;
int i, j;
//
// Inititlize default data.
//
data[0] = 0;
//
// Save original iorequest because some entries get modified.
//
origir = (pioreq) &origifsr;
memcpy( &origifsr, pir, sizeof( ifsreq ));
//
// Get the current process name.
//
FilemonGetProcess( processname );
//
// Get the time
//
VTD_Get_Real_Time( &timehi, &timelo );
datetimehi = IFSMgr_Get_DOSTime( &datetimelo );
dostime = VTD_Get_Date_And_Time( &dosdate );
datetimelo = dostime - ((datetimehi >> 11)& 0x1F)*60*60*1000 -
((datetimehi >> 5) & 0x3F)*60*1000 -
((datetimehi & 0x1F)*2000);
//
// Special case for close call, since after the file's closed
// we can't query its name
//
if( fn == IFSFN_CLOSE ) {
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
}
//
// Call the previous hooker first, to get the return code.
//
retval = (*PrevIFSHookProc)(pfn, fn, Drive, ResType, CodePage, pir);
//
// Now extract parameters based on the function type.
//
dprintf("%d: %d\n", fn, pir->ir_fh );
switch( fn ) {
case IFSFN_OPEN:
FilemonConvertPath( CONVERT_STANDARD, Drive, origir, CodePage, fullpathname );
TIME_DIFF();
sprintf(data,"");
if( origir->ir_options & ACTION_CREATENEW ) strcat(data,"CREATENEW ");
if( origir->ir_options & ACTION_OPENEXISTING ) strcat(data,"OPENEXISTING ");
if( origir->ir_options & ACTION_REPLACEEXISTING ) strcat(data,"REPLACEEXISTING ");
switch (origir->ir_flags & ACCESS_MODE_MASK) {
case ACCESS_READONLY:
strcat(data,"READONLY ");
break;
case ACCESS_WRITEONLY:
strcat(data,"WRITEONLY ");
break;
case ACCESS_READWRITE:
strcat(data,"READWRITE ");
break;
case ACCESS_EXECUTE:
strcat(data,"EXECUTE ");
break;
default:
break;
}
switch (origir->ir_flags & SHARE_MODE_MASK) {
case SHARE_COMPATIBILITY:
strcat(data,"COMPATIBILITY ");
break;
case SHARE_DENYREADWRITE:
strcat(data,"DENYREADWRITE ");
break;
case SHARE_DENYWRITE:
strcat(data,"DENYWRITE ");
break;
case SHARE_DENYREAD:
strcat(data,"DENYREAD ");
break;
case SHARE_DENYNONE:
strcat(data,"DENYNONE ");
break;
default:
break;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tOpen\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
FilemonLogHash( Drive, pir->ir_fh, fullpathname );
break;
case IFSFN_READ:
case IFSFN_WRITE:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( ((fn == IFSFN_READ && FilterDef.logreads ) ||
(fn == IFSFN_WRITE && FilterDef.logwrites )) ) {
TIME_DIFF();
sprintf( data, "Offset: %ld Length: %ld", origir->ir_pos, origir->ir_length );
LogRecord( timelo, datetimelo, datetimehi, "%s\t%s\t%s\t%s\t%s",
processname, fn == IFSFN_READ? "Read" : "Write",
fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_CLOSE:
if( FilterDef.logreads ) {
TIME_DIFF();
switch( origir->ir_flags ) {
case CLOSE_HANDLE: sprintf(data, "CLOSE_HANDLE"); break;
case CLOSE_FOR_PROCESS: sprintf(data, "CLOSE_FOR_PROCESS"); break;
case CLOSE_FINAL: sprintf(data, "CLOSE_FINAL"); break;
default: sprintf(data,"0x%02X",origir->ir_flags); break;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tClose\t%s\t%s\t%s", processname,
fullpathname, data, ErrorString( retval ));
}
if( origir->ir_flags == CLOSE_FINAL ) FilemonFreeHashEntry( Drive, origir->ir_fh);
break;
case IFSFN_DIR:
//
// This works around a special case I've seen when hiting the "browse" button in the
// "have disk" dialog of the hardware wizard
//
if( origir->ir_flags != 0xFF ) {
FilemonConvertPath( CONVERT_STANDARD, Drive, origir, CodePage, fullpathname );
} else {
sprintf( fullpathname, "%c:", Drive+'A'-1);
}
TIME_DIFF();
log = FALSE;
switch( origir->ir_flags ) {
case CREATE_DIR:
sprintf(data, "CREATE");
if( FilterDef.logwrites ) log = TRUE;
break;
case DELETE_DIR:
sprintf(data,"DELETE");
if( FilterDef.logwrites ) log = TRUE;
break;
case CHECK_DIR:
sprintf(data,"CHECK");
if( FilterDef.logreads ) log = TRUE;
break;
default:
sprintf(data,"QUERY");
if( FilterDef.logreads ) log = TRUE;
break;
}
if( log ) {
LogRecord( timelo, datetimelo, datetimehi, "%s\tDirectory\t%s\t%s\t%s",
processname,
fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_SEEK:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( FilterDef.logreads) {
TIME_DIFF();
sprintf(data, "%s Offset: %ld / New offset: %ld",
origir->ir_flags == FILE_BEGIN ? "Beginning" : "End",
origir->ir_pos, origir->ir_pos );
LogRecord( timelo, datetimelo, datetimehi, "%s\tSeek\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_COMMIT:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( FilterDef.logwrites) {
TIME_DIFF();
sprintf(data, "%s", origir->ir_options == FILE_COMMIT_ASYNC ?
"ASYNC" : "NOACCESSUPDATE" );
LogRecord( timelo, datetimelo, datetimehi, "%s\tCommit\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_FILELOCKS:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( FilterDef.logreads) {
TIME_DIFF();
sprintf(data, "Offset: %ld Length:%ld", origir->ir_pos, origir->ir_locklen );
LogRecord( timelo, datetimelo, datetimehi, "%s\t%s\t%s\t%s\t%s",
processname, origir->ir_flags == LOCK_REGION ? "Lock" : "Unlock",
fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_FINDOPEN:
if( FilterDef.logreads) {
FilemonConvertPath( CONVERT_FINDOPEN, Drive, origir, CodePage, fullpathname );
TIME_DIFF();
if( !retval ) {
finddata = (_WIN32_FIND_DATA *) origir->ir_data;
UniToBCS( data, finddata->cFileName, wstrlen(finddata->cFileName), MAXPATHLEN-1, BCS_WANSI, &result );
data[ result.ddLower ] = 0;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tFindOpen\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
}
FilemonLogHash( Drive, pir->ir_fh, fullpathname );
break;
case IFSFN_FINDNEXT:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( FilterDef.logreads) {
TIME_DIFF();
if( !retval ) {
finddata = (_WIN32_FIND_DATA *) origir->ir_data;
UniToBCS( data, finddata->cFileName, wstrlen(finddata->cFileName), MAXPATHLEN-1, BCS_WANSI, &result );
data[ result.ddLower ] = 0;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tFindNext\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_FINDCLOSE:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( FilterDef.logreads) {
TIME_DIFF();
LogRecord( timelo, datetimelo, datetimehi, "%s\tFindClose\t%s\t\t%s",
processname, fullpathname,
ErrorString( retval ));
}
FilemonFreeHashEntry( Drive, origir->ir_fh );
break;
case IFSFN_FILEATTRIB:
if( FilterDef.logreads) {
FilemonConvertPath( CONVERT_STANDARD, Drive, origir, CodePage, fullpathname );
TIME_DIFF();
switch(origir->ir_flags ) {
case GET_ATTRIBUTES:
sprintf(data,"GetAttributes");
break;
case SET_ATTRIBUTES:
sprintf(data, "SetAttributes" );
break;
case GET_ATTRIB_COMP_FILESIZE:
sprintf(data, "GET_ATTRIB_COMP_FILESIZE" );
break;
case SET_ATTRIB_MODIFY_DATETIME:
sprintf(data, "SET_ATTRIB_MODIFY_DATETIME");
break;
case SET_ATTRIB_LAST_ACCESS_DATETIME:
sprintf(data, "SET_ATTRIB_LAST_ACCESS_DATETIME");
break;
case GET_ATTRIB_LAST_ACCESS_DATETIME:
sprintf(data, "GET_ATTRIB_LAST_ACCESS_DATETIME");
break;
case SET_ATTRIB_CREATION_DATETIME:
sprintf(data, "SET_ATTRIB_CREATION_DATETIME");
break;
case GET_ATTRIB_CREATION_DATETIME:
sprintf(data, "GET_ATTRIB_CREATION_DATETIME");
break;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tAttributes\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_FILETIMES:
FilemonGetFullPath( origir->ir_fh, fullpathname, Drive, ResType, CodePage, origir );
if( FilterDef.logreads) {
TIME_DIFF();
switch( origir->ir_flags ) {
case GET_MODIFY_DATETIME:
sprintf(data, "Get Modify");
break;
case SET_MODIFY_DATETIME:
sprintf(data, "Set Modify");
break;
case GET_LAST_ACCESS_DATETIME:
sprintf(data, "Get Access");
break;
case SET_LAST_ACCESS_DATETIME:
sprintf(data, "Set Access");
break;
case GET_CREATION_DATETIME:
sprintf(data, "Get Creation");
break;
case SET_CREATION_DATETIME:
sprintf(data, "Set Creation");
break;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tAttributes\t%s\t%s\t%s",
processname, fullpathname,
data, ErrorString( retval ));
}
break;
case IFSFN_FLUSH:
if( FilterDef.logwrites) {
TIME_DIFF();
LogRecord( timelo, datetimelo, datetimehi, "%s\tFlushVolume\t\t\t%s",
processname, ErrorString( retval ));
}
break;
case IFSFN_DELETE:
if( FilterDef.logwrites) {
FilemonConvertPath( CONVERT_STANDARD, Drive, origir, CodePage, fullpathname );
TIME_DIFF();
LogRecord( timelo, datetimelo, datetimehi, "%s\tDelete\t%s\t\t%s",
processname, fullpathname, ErrorString( retval ));
}
FilemonFreeHashEntry( Drive, origir->ir_fh );
break;
case IFSFN_SEARCH:
if( FilterDef.logreads ) {
if( origir->ir_flags == SEARCH_FIRST )
FilemonConvertPath( CONVERT_STANDARD, Drive, origir, CodePage, fullpathname );
else
sprintf(fullpathname, "SearchNext" );
TIME_DIFF();
if( !retval ) {
j = 0;
if( origir->ir_attr & FILE_ATTRIBUTE_LABEL ) {
sprintf(data, "VolumeLabel: " );
j = strlen( data );
}
search = (struct srch_entry *) origir->ir_data;
for( i = 0; i < 13; i++ )
if( search->se_name[i] != ' ' ) data[j++] = search->se_name[i];
data[j] = 0;
}
LogRecord( timelo, datetimelo, datetimehi, "%s\tSearch\t%s\t%s\t%s",
processname, fullpathname, data, ErrorString( retval ));
}
break;
case IFSFN_GETDISKINFO:
if( FilterDef.logreads ) {
TIME_DIFF();
if( !retval ) sprintf(data, "Free Space");
drivestring[0] = Drive+'A'-1;
drivestring[1] = ':';
drivestring[2] = 0;
LogRecord( timelo, datetimelo, datetimehi, "%s\tGetDiskInfo\t%s\t%s\t%s",
processname, drivestring, data, ErrorString( retval ));
}
break;
case IFSFN_RENAME:
if( FilterDef.logwrites) {
FilemonConvertPath( CONVERT_RENAME_SOURCE, Drive, origir, CodePage, fullpathname );
TIME_DIFF();
LogRecord( timelo, datetimelo, datetimehi, "%s\tRename\t%s\t%s\t%s",
processname, fullpathname,
FilemonConvertPath( CONVERT_RENAME_TARGET, Drive, origir, CodePage, data ),
ErrorString( retval ));
}
break;
case IFSFN_IOCTL16DRIVE:
if( FilterDef.logreads || FilterDef.logwrites) {
TIME_DIFF();
sprintf(data, "Subfunction: %02Xh", origir->ir_flags );
drivestring[0] = Drive+'A'-1;
drivestring[1] = ':';
drivestring[2] = 0;
LogRecord( timelo, datetimelo, datetimehi, "%s\tIoctl\t%s\t%s\t%s",
processname, drivestring, data, ErrorString( retval ));
}
break;
}
dprintf("==>%d\n", fn );
return retval;
}
//----------------------------------------------------------------------
//
// OnSysDynamicDeviceInit
//
// Dynamic init. Install a file system filter hook.
//
//----------------------------------------------------------------------
BOOL OnSysDynamicDeviceInit( VOID)
{
int i;
MEMHANDLE hLog;
//
// Initialize the locks.
//
LogMutex = Create_Semaphore(1);
HashMutex = Create_Semaphore(1);
FilterMutex = Create_Semaphore(1);
//
// Zero hash table.
//
for(i = 0; i < NUMHASH; i++ ) HashTable[i] = NULL;
//
// Allocate the initial output buffer.
//
PageAllocate(LOGBUFPAGES, PG_SYS, 0, 0, 0, 0, NULL, PAGELOCKED,
(PMEMHANDLE) &hLog, (PVOID) &Log );
Log->Handle = hLog;
Log->Len = 0;
Log->Next = NULL;
NumLog = 1;
//
// Hook IFS functions.
//
PrevIFSHookProc = IFSMgr_InstallFileSystemApiHook(FilemonHookProc);
return TRUE;
}
//----------------------------------------------------------------------
//
// OnSysDynamicDeviceExit
//
// Dynamic exit. Unhook everything.
//
//----------------------------------------------------------------------
BOOL OnSysDynamicDeviceExit( VOID)
{
//
// Unhook IFS functions.
//
IFSMgr_RemoveFileSystemApiHook(FilemonHookProc);
//
// Free all memory.
//
FilemonHashCleanup();
FilemonFreeLog();
FilemonFreeFilters();
return TRUE;
}
//----------------------------------------------------------------------
This code is from the file monitor 'sys' version source code:
//----------------------------------------------------------------------
//
// 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;
}
}
//----------------------------------------------------------------------
//
// 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 );
}
//----------------------------------------------------------------------
Technical Details
Message-ID:
<483978fa@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!feeder.news-service.com!newsfeed00.sul.t-online.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mailReferences:
<483726af$1@newsgroups.borland.com>Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,c374cfd4b7ac43da X-Google-Attributes: gid10cc67,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Date: Sun, 25 May 2008 07:34:25 -0700 From: Bruce Larrabee <bruce_l@westbrookent.com> User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.2) Gecko/20040804 Netscape/7.2 (ax) X-Accept-Language: en-us, en MIME-Version: 1.0 Newsgroups: borland.public.cppbuilder.nativeapi Subject: Re: Hard Drive Access References: <483726af$1@newsgroups.borland.com> In-Reply-To: <483726af$1@newsgroups.borland.com> Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit NNTP-Posting-Host: 208.100.240.149 Message-ID: <483978fa@newsgroups.borland.com> X-Trace: newsgroups.borland.com 1211726074 208.100.240.149 (25 May 2008 07:34:34 -0700) Lines: 1325 Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!feeder.news-service.com!newsfeed00.sul.t-online.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail