Thread View: borland.public.cppbuilder.nativeapi
3 messages
3 total messages
Started by Jeff
Fri, 23 May 2008 20:18
Hard Drive Access
Author: Jeff
Date: Fri, 23 May 2008 20:18
Date: Fri, 23 May 2008 20:18
8 lines
1170 bytes
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
Author: 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 );
}
//----------------------------------------------------------------------
Re: Hard Drive Access
Author: Mike Collins
Date: Tue, 27 May 2008 15:39
Date: Tue, 27 May 2008 15:39
21 lines
1738 bytes
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