🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.nativeapi
3 messages
3 total messages Started by Jeff Fri, 23 May 2008 20:18
Hard Drive Access
#271
Author: Jeff
Date: Fri, 23 May 2008 20:18
8 lines
1170 bytes
What is the best method for programmatically tracking hard drive access? 
I'm using BDS 2006 and trying to detect reads and writes to the hard drive. 
I've used the DiskMon program from Microsoft but it doesn't come with source 
code.  I read some stuff about the performance monitor data but how do I 
read that?

Thanks,
Jeff 
Re: Hard Drive Access
#273
Author: Bruce Larrabee
Date: Sun, 25 May 2008 14:34
1324 lines
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)&currentIrpStack->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)&currentIrpStack->Parameters)->ByteOffset.LowPart,
                                        ((PLOCK_CONTROL)&currentIrpStack->Parameters)->Length ?
                                        ((PLOCK_CONTROL)&currentIrpStack->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 );
}


//----------------------------------------------------------------------
Re: Hard Drive Access
#280
Author: Mike Collins
Date: Tue, 27 May 2008 15:39
21 lines
1738 bytes
I might be total wrong here, but I'm sure that when i looked at FileMon (the 
most recent version) it use a file system driver that it unpacks on the fly. 
Not sure where Bruces code came from - maybe he was one of those guys who 
was lucky enough to grab a copy before SysInternals when over to Microsoft 
and they stopped giving source code.

Anyway, as Bruce has said, you can construct an api hooking system which 
should give you some notification of when certain *file* related api's are 
called.

I can give you some direction on this if you wish to e-mail me directly.  We 
use DLL injection and APi hooking quite a lot and i'd be happy to point you 
in the right direction.  If you want to e-mail me my address is mike (at) 
kid-safe (dot) co (uk)

Best regards,

Mike


> 
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads