Click here to Skip to main content
15,887,585 members
Articles / Programming Languages / C++

USB & CD/DVD Blocking: One Way to Keep Data Free of Theft

Rate me:
Please Sign up or sign in to vote.
3.65/5 (14 votes)
4 May 2011CPOL2 min read 35.6K   4.5K   45   8
An example of blocking access to USB Memory and CD

Sample Image - maximum width is 600 pixels

Sample Image - maximum width is 600 pixels

Introduction

Have you ever thought of blocking access to USB Memory and CD? I will introduce an example of this. Perhaps some of you will not be interested in this, but I think this technique will be useful for more large-scale projects. I referenced a sample in Microsoft Windows DDK. This sample is implemented by file system filter driver. As you know, File System Filter Driver is commonly used in Anti-Virus and it can be used for some other purposes. In this sample, we can not only block access, but log the file path written to USB.

How to Use

This sample consists of 2 sysfiles and a DLL file. In order to test this sample, first execute install.exe in 1_install folder. You can uninstall this by executing uninstall.exe in 3_uninstall folder.
As seen above, click OK button first and then test the functions.

Using the Code

Here I would explain the file system filter driver. There are two ways of developing file system filter driver. One is to use filter function supported by FLTLIB.DLL in system32 directory. In this case, we can communicate with driver by using FilterConnectCommunicationPort() function and FilterSendMessage() function. Another one is to get file system driver's pointer and attach our driver to it by using IoAttachDeviceToDeviceStack() function.

DriverEntry

DriverEntry() function should be written like below:

C++
NTSTATUS
DriverEntry (
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    )
{
    PSECURITY_DESCRIPTOR sd;
    OBJECT_ATTRIBUTES oa;
    UNICODE_STRING uniString;
    NTSTATUS status;
	PFLT_VOLUME fltvolume;
	HANDLE handle = (PVOID)-1;
	PROCESS_DEVICEMAP_INFORMATION ldrives;
	ULONG           drive, bit;
	STRING			ansiString, ansiVolString;
	UNICODE_STRING	unString, unVolString;
	CHAR			szDrv[20];
	ULONG			sizeneeded;
	HANDLE hThread;
	OBJECT_ATTRIBUTES oaThread;
	KIRQL irql;

	ULONG i;

    try {
	ACDrvData.LogSequenceNumber = 0;
        	ACDrvData.MaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE;
        	ACDrvData.RecordsAllocated = 0;
       	ACDrvData.NameQueryMethod = DEFAULT_NAME_QUERY_METHOD;

        ACDrvData.DriverObject = DriverObject;

        InitializeListHead( &ACDrvData.OutputBufferList );
        KeInitializeSpinLock( &ACDrvData.OutputBufferLock );


#if ACDRV_LONGHORN

        //
        //  Dynamically import FilterMgr APIs for transaction support
        //

        ACDrvData.PFltSetTransactionContext = 
		FltGetRoutineAddress( "FltSetTransactionContext" );
        ACDrvData.PFltGetTransactionContext = 
		FltGetRoutineAddress( "FltGetTransactionContext" );
        ACDrvData.PFltEnlistInTransaction = 
		FltGetRoutineAddress( "FltEnlistInTransaction" );

#endif

		SpyReadDriverParameters(RegistryPath);

		status = FltRegisterFilter( DriverObject,
					&FilterRegistration,
					&ACDrvData.Filter );
        if (!NT_SUCCESS( status )) {

           leave;
        }

        status  = FltBuildDefaultSecurityDescriptor( &sd,
                                                     FLT_PORT_ALL_ACCESS );

        if (!NT_SUCCESS( status )) {
            leave;
        }

        RtlInitUnicodeString( &uniString, ACDRV_PORT_NAME );

        InitializeObjectAttributes( &oa,
                                    &uniString,
                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
                                    NULL,
                                    sd );

        status = FltCreateCommunicationPort( ACDrvData.Filter,
                                             &ACDrvData.ServerPort,
                                             &oa,
                                             NULL,
                                             SpyConnect,
                                             SpyDisconnect,
                                             SpyMessage,
                                             1 );

        FltFreeSecurityDescriptor( sd );

        if (!NT_SUCCESS( status )) {
            leave;
        }
		...
		
		status = FltStartFiltering( ACDrvData.Filter );

		...
    } finally {

        if (!NT_SUCCESS( status ) ) {

             if (NULL != ACDrvData.ServerPort) {
                 FltCloseCommunicationPort( ACDrvData.ServerPort );
             }

             if (NULL != ACDrvData.Filter) {
                 FltUnregisterFilter( ACDrvData.Filter );
             }

             ExDeleteNPagedLookasideList( &ACDrvData.FreeBufferList );
        }
    }
    return status;
}

After FltStartFiltering() function is called, call FltAttachVolume() function to monitor the specific Disk drive or CD drive.

Unload Routine

Unload Routine should be written as below:

C++
NTSTATUS
SpyFilterUnload (
    __in FLT_FILTER_UNLOAD_FLAGS Flags
    )
{
	PFLT_VOLUME		fltvolume;
	UNICODE_STRING	uniString;
	STRING			AnsiString;
	ULONG			i;
	KIRQL			irql;
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

	...
	for(i = 0; i < 26; i++)
	{
		fltvolume = NULL;
		RtlInitAnsiString(&AnsiString, DrvInfo[i].szDrvName);
		RtlAnsiStringToUnicodeString(&uniString, &AnsiString, TRUE);
		FltGetVolumeFromName( ACDrvData.Filter, &uniString, &fltvolume);
		FltDetachVolume( ACDrvData.Filter, fltvolume, NULL);
		RtlFreeUnicodeString(&uniString);
	}
    
	FltCloseCommunicationPort( ACDrvData.ServerPort );
    FltUnregisterFilter( ACDrvData.Filter );
	ExDeleteNPagedLookasideList( &ACDrvData.FreeBufferList );
    
	return STATUS_SUCCESS;
}

FilterRegistration Variable Definition

FilterRegistration variable should be formatted like this:

C++
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_CREATE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_CREATE_NAMED_PIPE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_CLOSE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_READ,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_QUERY_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_SET_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_QUERY_EA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_SET_EA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_FLUSH_BUFFERS,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_QUERY_VOLUME_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_SET_VOLUME_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_DIRECTORY_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_FILE_SYSTEM_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_DEVICE_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_INTERNAL_DEVICE_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_SHUTDOWN,
      0,
      SpyPreOperationCallback,
      NULL },                           //post operation callback not supported

    { IRP_MJ_LOCK_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_CLEANUP,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_CREATE_MAILSLOT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_QUERY_SECURITY,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_SET_SECURITY,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_QUERY_QUOTA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_SET_QUOTA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_PNP,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_ACQUIRE_FOR_MOD_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_RELEASE_FOR_MOD_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_ACQUIRE_FOR_CC_FLUSH,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_RELEASE_FOR_CC_FLUSH,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

/*    { IRP_MJ_NOTIFY_STREAM_FILE_OBJECT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },*/

    { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_NETWORK_QUERY_OPEN,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_MDL_READ,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_MDL_READ_COMPLETE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_PREPARE_MDL_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_MDL_WRITE_COMPLETE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_VOLUME_MOUNT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_VOLUME_DISMOUNT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },

    { IRP_MJ_OPERATION_END }
};

const FLT_CONTEXT_REGISTRATION Contexts[] = {

#if ACDRV_LONGHORN

    { FLT_TRANSACTION_CONTEXT,
      0,
      SpyDeleteTxfContext,
      sizeof(ACDRV_TRANSACTION_CONTEXT),
      'ypsM' },

#endif // ACDRV_LONGHORN

    { FLT_CONTEXT_END }
};

//
//  This defines what we want to filter with FltMgr
//

CONST FLT_REGISTRATION FilterRegistration = {

    sizeof(FLT_REGISTRATION),               //  Size
    FLT_REGISTRATION_VERSION,               //  Version
    0,                                      //  Flags

    Contexts,                               //  Context
    Callbacks,                              //  Operation callbacks

    SpyFilterUnload,                        //  FilterUnload

    NULL,                                   //  InstanceSetup
    SpyQueryTeardown,                       //  InstanceQueryTeardown
    NULL,                                   //  InstanceTeardownStart
    NULL,                                   //  InstanceTeardownComplete

    NULL,                                   //  GenerateFileName
    NULL,                                   //  GenerateDestinationFileName
    NULL                                    //  NormalizeNameComponent

#if ACDRV_LONGHORN
    ,
    SpyKtmNotificationCallback              //  KTM notification callback

#endif // ACDRV_LONGHORN

};

SpyPreOperationCallback() Function

SpyPreOperationCallback() function should be written like this:

C++
FLT_PREOP_CALLBACK_STATUS
SpyPreOperationCallback (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
{
	FLT_PREOP_CALLBACK_STATUS returnStatus = 
		FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call 
					//our completion routine
	
	...
	
	if (Data->Iopb->MajorFunction == IRP_MJ_SYSTEM_CONTROL)
		DbgPrint("System Control Called\n");
	//------------------------------------insert my process
	if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
		
		...
		
		//If drive(path's first letter) is USB Memory Drive or CD Drive, 
		//Block!!!
		Data->IoStatus.Status = STATUS_ACCESS_DENIED;
		returnStatus = FLT_PREOP_COMPLETE;
		return returnStatus;
		
		...
	}
	returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;

	...

	return returnStatus;
}

Conclusion

Ok, it's all over. I know this is not the best method, but I think this technique can be used anytime. I hope this will be a good help for you to develop a similar program. If you have any questions, please contact me. I hope you are satisfied with my article. Please rate it for me. Thank you.

History

  • 27th April, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
GeneralMy vote of 5 Pin
Michael_Romanov9-Dec-12 3:17
Michael_Romanov9-Dec-12 3:17 
Questioncnli Pin
Gun Gun Febrianza9-Oct-12 15:15
Gun Gun Febrianza9-Oct-12 15:15 
GeneralMy vote of 5 Pin
Gun Gun Febrianza9-Oct-12 15:14
Gun Gun Febrianza9-Oct-12 15:14 
GeneralMy vote of 1 Pin
Digvijay Chauhan20-Sep-11 11:36
Digvijay Chauhan20-Sep-11 11:36 
GeneralMy vote of 1 Pin
prashu10012-May-11 22:47
prashu10012-May-11 22:47 
No explaination
GeneralMy Vote of 0.00 Pin
persian music6-May-11 10:52
persian music6-May-11 10:52 
GeneralMy vote of 1 Pin
Dave Kreskowiak4-May-11 11:54
mveDave Kreskowiak4-May-11 11:54 
GeneralRe: My vote of 1 Pin
Sharjith5-May-11 7:21
professionalSharjith5-May-11 7:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.